为了激发我的问题,请考虑在Julia中处理元素类型Int
的{{3}}(为简单起见)时的情况。有两种方法可以存储它们:
Vector{Vector{Int}}
Vector{Union{Vector{Int}, Int}}
(特别是,如果想要存储足够多的1元素向量)我的问题是哪一个更有效/更快/更好?
要回答这个问题,我需要知道每个内容是如何存储在内存中的。即:
我假设Vector{Vector{Int}}
类型的变量被认为是同类型数组,因此我希望它在内存中连续存储,因此更加cpu-cache友好。我对吗?或者连续性仅适用于其元素为'数据类型是原始的吗?
类型Vector{Union{Vector{Int}, Int}}
的变量是否会考虑异构数组,并且在内存中存储不 连续?
如何将内存中连续表示的好处与不具有1个元素数组成员的数组容器的好处进行比较,即将它们存储为原始数据类型(在这种情况下为Int
)?哪一个产生更高的效率?
答案 0 :(得分:7)
如果T
为真,则Julia的数组只会存储isbits(T)
类型的元素。也就是说,元素必须是不可变的和无指针的。查看元素是否立即存储的简单方法是分配未初始化的数组。未装箱(立即)值的连续数组将有胡言乱语:
julia> Array(Int, 3)
3-element Array{Int64,1}:
4430901168
4470602000
4430901232
而非isbits类型的数组将有#undef
个指针:
julia> Array(Vector{Int}, 3)
3-element Array{Array{Int64,1},1}:
#undef
#undef
#undef
想象一下,如果后者返回一个连续的Int
块,会发生什么。怎么知道它有多大?或者一个矢量停止而下一个矢量开始?这将取决于矢量的大小,这还不为人所知。
Vector{Union{Vector{Int}, Int}}
同样会将其元素存储为指针;这次是因为Julia不知道如何解释内联的每个元素(它应该像整数一样读取内存还是像数组一样?)。它还有一个缺点,就是朱莉娅不再知道它会从索引中返回什么类型。这是一种类型不稳定性,并且对于性能而言肯定会比使用单元素向量更差
可以创建自己的乱七八糟的数组类型来存储其元素内联,但是它使标准库像普通数组一样工作非常棘手,因为它打破了很多关于索引如何工作的假设。您可以查看我最近的尝试:RaggedArrays.jl。您可以看到我如何将其与Issue#2中的先前工作进行比较。