Holding/Looping 通过具有相同抽象父类型的对象集合的最佳实践 (Julia)
Best-Practice for Holding/Looping Through a Collection of Objects with Same Abstract Parent Type (Julia)
这是一个初学者问题,我仍在“面向对象编程”中思考,所以如果我错过了手册中的答案或者答案很明显,我深表歉意。
假设我们有一个抽象类型,
abstract type My_Abstract_type end
以及作为该类型子级的几个具体结构类型:
mutable struct Concrete_struct1 <: My_Abstract_type end
mutable struct Concrete_struct2 <: My_Abstract_type end
...
假设我们有大量具体类型的对象,我们需要存储和循环这些对象。在 Python 中,我们可以只创建一个对象列表,然后循环遍历该列表。类似地,在 C++ 中,我们可以创建一个指针数组(My_Abstract_type 类型),然后遍历它,以多态方式调用所需的一切。
但是,我不知道如何在 Julia 中干净利落地做到这一点。我们可以创建一个数组 my_array::Array{My_Abstract_type,1}
然后遍历它:
for my_object in my_array
do_something!(my_object)
end
但是,正如此处 https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-abstract-container-1 所讨论的,这会带来巨大的性能损失(在我的用例中大约慢 25 倍)。
一种替代方法是执行以下操作:
my_array1::Array{Concrete_struct1,1}
my_array2::Array{Concrete_struct2,1}
my_array3::Array{Concrete_struct3,1}
...
然后
for my_object in my_array1
do_something!(my_object)
end
for my_object in my_array2
do_something!(my_object)
end
for my_object in my_array3
do_something!(my_object)
end
这给了我们想要的性能,但显然是糟糕的软件工程实践,尤其是在有大量具体类型的情况下。我们如何在 Julia 中 干净地 和 存储和循环这些对象而不牺牲性能 ?谢谢!
如果您没有超过四种具体类型,则只需使用其中的 Union
个,如 Julia 手册中 here 所述。对于这种情况,编译器将为您生成有效的代码。
如果你有很多类型,那么你可以使用数组的数组:
a = [my_array1, my_array2, my_array3]
现在做
foreach(a) do x
for my_object in x
do_something!(my_object)
end
end
现在 a
本身没有具体类型,但对匿名函数的调用应该启用代码专业化。这将有一些开销(a
的每个元素一个动态调度),但假设每个数组的元素比类型多得多,它应该相当快。
最后,如果你想以接受大量编译成本为代价完全避免动态调度,你可以这样写:
processing_fun() = nothing
function processing_fun(x, xs...)
for my_object in x
do_something!(my_object)
end
processing_fun(xs...)
end
然后调用:
processing_fun(a...)
(但如果它有益,则必须在您的特定情况下进行基准测试,因为它涉及递归,递归也有其成本)
这是一个初学者问题,我仍在“面向对象编程”中思考,所以如果我错过了手册中的答案或者答案很明显,我深表歉意。
假设我们有一个抽象类型,
abstract type My_Abstract_type end
以及作为该类型子级的几个具体结构类型:
mutable struct Concrete_struct1 <: My_Abstract_type end
mutable struct Concrete_struct2 <: My_Abstract_type end
...
假设我们有大量具体类型的对象,我们需要存储和循环这些对象。在 Python 中,我们可以只创建一个对象列表,然后循环遍历该列表。类似地,在 C++ 中,我们可以创建一个指针数组(My_Abstract_type 类型),然后遍历它,以多态方式调用所需的一切。
但是,我不知道如何在 Julia 中干净利落地做到这一点。我们可以创建一个数组 my_array::Array{My_Abstract_type,1}
然后遍历它:
for my_object in my_array
do_something!(my_object)
end
但是,正如此处 https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-abstract-container-1 所讨论的,这会带来巨大的性能损失(在我的用例中大约慢 25 倍)。
一种替代方法是执行以下操作:
my_array1::Array{Concrete_struct1,1}
my_array2::Array{Concrete_struct2,1}
my_array3::Array{Concrete_struct3,1}
...
然后
for my_object in my_array1
do_something!(my_object)
end
for my_object in my_array2
do_something!(my_object)
end
for my_object in my_array3
do_something!(my_object)
end
这给了我们想要的性能,但显然是糟糕的软件工程实践,尤其是在有大量具体类型的情况下。我们如何在 Julia 中 干净地 和 存储和循环这些对象而不牺牲性能 ?谢谢!
如果您没有超过四种具体类型,则只需使用其中的 Union
个,如 Julia 手册中 here 所述。对于这种情况,编译器将为您生成有效的代码。
如果你有很多类型,那么你可以使用数组的数组:
a = [my_array1, my_array2, my_array3]
现在做
foreach(a) do x
for my_object in x
do_something!(my_object)
end
end
现在 a
本身没有具体类型,但对匿名函数的调用应该启用代码专业化。这将有一些开销(a
的每个元素一个动态调度),但假设每个数组的元素比类型多得多,它应该相当快。
最后,如果你想以接受大量编译成本为代价完全避免动态调度,你可以这样写:
processing_fun() = nothing
function processing_fun(x, xs...)
for my_object in x
do_something!(my_object)
end
processing_fun(xs...)
end
然后调用:
processing_fun(a...)
(但如果它有益,则必须在您的特定情况下进行基准测试,因为它涉及递归,递归也有其成本)