用于预分配数据并在Julia中传递数据的设计模式
我希望改进如何为一个在大型数组和矩阵上运行的计算量很大的程序编写Julia代码的某些方面 <> P>有很多私有函数进行中间处理,并且系统以前是用C++设计的,这样内存就被自定义对象显式处理,以避免不断重新初始化和重新分配。这是内联的 我对朱莉娅的理解是:用于预分配数据并在Julia中传递数据的设计模式,julia,Julia,我希望改进如何为一个在大型数组和矩阵上运行的计算量很大的程序编写Julia代码的某些方面 P>有很多私有函数进行中间处理,并且系统以前是用C++设计的,这样内存就被自定义对象显式处理,以避免不断重新初始化和重新分配。这是内联的 我对朱莉娅的理解是: mutable struct Bar # A data structure holding data for a domain object end mutable struct FooData # A data structure
mutable struct Bar
# A data structure holding data for a domain object
end
mutable struct FooData
# A data structure holding intermediate computation variables
data::Array{Float32, 1}
function FooData()
new(Array{Float32, 1}(undef, 10000)
end
end
function foo!(data::FooData, b::Bar, param1, param2)
# modify data and return something
end
data = FooData()
for i=1:100
foo!(data, ...)
end
经常是福!做不止一件事。这主要是为了性能原因而合并操作。这使得命名foo和FooData特别困难
我的问题是:
很难知道你所说的更好的方式是什么意思——为什么不好呢?在您的MWE中根本不需要
FooData
类型,因为您可以只传递一个向量,但实际用例可能更复杂。在C++中,不需要像在C++中那样定义对象。p>
为了回答第二个问题,本地函数的名称通常类似于
\u foobar
。但是封装是由模块负责的——您不需要从模块中导出这些函数。它们不是private
,如果您愿意,您仍然可以使用它们(使用MyModule.\u foobar()
),但这是您自己的责任。这种约定过于强制在Julia中很常见。这不是答案,只是指出Julia中的数组是可变的,即使在不可变结构中也是如此。例如,您可以定义struct FooData;数据::向量{Float64};结束
(一个不可变的)并且仍然做foodata.data[3:4]=rand(2)。如果要执行诸如FooData.data=rand(5)
之类的操作,只需将FooData
设置为可变,即重新分配整个字段data
。我之所以提到这一点,是因为根据您的问题,听起来您可能只想修改data
的元素,而不是FooData
本身……有时,我会使FooData可变,以允许释放其内存的某些部分。例如,说foodata.data=nothing。我的理解是,这会触发垃圾收集器。如果数据的类型是::Union{Nothing,Array{Float32,1}},我希望数组的内存确实被释放了。这是有意义的,尽管我不确定垃圾收集是否是自动的。您可能需要使用gc()
强制它。如果你在一两天内没有得到答案,我会考虑问这个问题。我不认为这会触发垃圾收集器。通常你会让Julia通过作用域来处理内存,而不是显式地释放内存。我发现显式函数所有权的丧失造成了一类我不知道放在哪里的对象。在OO中,对象拥有自己的方法。在我看来,这赋予了对象对域数据和函数的共同责任。在需要缓存中间操作的结果以提高性能的情况下,执行缓存的结构可以存储在此OO对象中。然而,在Julia中,由于函数和对象是分开的,我不太确定该结构现在属于何处,因为该结构与函数的关系比与域数据的关系更密切。我倾向于在对象中缓存中间结果。您可以有一个包含缓存的单独类型,也可以在cache::Ref{Union{T,Nothing}类型的对象中有一个字段,其中T是中间类型。但这是一个通用的解决方案,它可能会为您的用例提供另一个特定的答案——具体的用途是什么?