我正在朱莉娅编写一个测试随机拓扑的神经网络。我将所有索引保留在未被节点占用的节点数组中(但可能在未来的拓扑结构中)未定义,因为它节省了内存。当旧拓扑中的节点不再连接到新拓扑中的其他节点时,是否有办法取消初始化该节点所属的索引?另外,有没有理由不这样做?
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演出。 我的活动监控器也证实了这一点。
答案 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.jl或LightGraphs.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