Julia在特定索引处取消初始化数组

时间:2015-05-06 00:11:32

标签: arrays julia

我正在朱莉娅编写一个测试随机拓扑的神经网络。我将所有索引保留在未被节点占用的节点数组中(但可能在未来的拓扑结构中)未定义,因为它节省了内存。当旧拓扑中的节点不再连接到新拓扑中的其他节点时,是否有办法取消初始化该节点所属的索引?另外,有没有理由不这样做?

local preSyns = Array(Vector{Int64}, (2,2)) 
println(preSyns)

preSyns[1] = [3]
println(preSyns)

输出

[#undef #undef

#undef #undef]

[[1] #undef

#undef #undef]

如何在第一次打印输出时使第一个索引未定义?

如果您不相信我,请关注内存问题,请看下面

function memtest()
    y = Array(Vector{Int64}, 100000000)
end

function memtestF()
    y = fill!(Array(Vector{Int64}, 100000000),[])
end

@time memtest()
@time memtestF()

输出

elapsed time: 0.468254929 seconds (800029916 bytes allocated)
elapsed time: 30.801266299 seconds (5600291712 bytes allocated, 69.42% gc time)

未初始化的数组需要0.8演出并初始化需要5演出。 我的活动监控器也证实了这一点。

1 个答案:

答案 0 :(得分:3)

未定义的值本质上是空指针,并且没有第一类方法将数组元素“取消设置”回空指针。对于非常大的数组,这变得更加困难,因为您不希望对表示未连接节点的标记值有太多(或任何)开销。在64位系统上,1亿个元素的数组仅占用指针仅约800MB,而空数组占用其头部元数据需要48个字节。因此,如果为每个元素分配一个独立的空数组,最终会得到大约5GB的数组头。

Julia 0.3中fill!的行为有点不稳定(0.4 has been corrected)。如果您使用显式类型[] fill!而不是使用Int64[]填充数组,则每个元素都会指向相同的空数组。这意味着您的数组及其元素将比未初始化的数组多占用48个字节。但这也意味着修改一个节点的子阵列(例如,使用push!)将意味着所有节点将获得该连接。这可能不是你想要的。你仍然可以使用空数组作为标记值,​​但你必须非常小心不要修改它。

如果你的数组将被密集地填充子数组,那么对于数组头文件来说,这种开销并没有直接的方法。使用独立空数组初始化数组的更强大且前向兼容的方法是理解:Vector{Int64}[Int64[] for i=1:2, j=1:2]。这也将在0.3中更高效,因为它不需要为每个元素转换[]Int64[]。如果每个元素可能包含非空数组,则无论如何都需要支付数组开销的成本。要从拓扑中删除节点,只需调用empty!

但是,如果您的节点数组将被稀疏打包,您可以尝试使用更直接支持未设置元素的不同数据结构。根据您的用例,您可以使用将索引元素映射到向量的默认字典(来自DataStructures.jl;使用函数确保“default”每次都是新分配的独立空数组),或尝试专用于Graphs.jlLightGraphs.jl等拓扑的包。

最后,为了回答你问的实际问题,是的,有一种hacky方法可以将数组元素取消设置回#undef。这是不受支持的,可能随时中断:

function unset!{T}(A::Array{T}, I::Int...)
    isbits(T) && error("cannot unset! an element of a bits array")
    P = pointer_to_array(convert(Ptr{Ptr{T}}, pointer(A)), size(A))        
    P[I...] = C_NULL
    return A
end