Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Julia uninitialize array at particular index

Tags:

arrays

julia

I'm writing a neural network in Julia that tests random topologies. I've left all indices in an array of nodes that are not occupied by a node (but which may be under a future topology) undefined as it saves memory. When a node in an old topology is no longer connected to other nodes in a new topology, is there a way to uninitialize the index to which the node belongs? Also, are there any reasons not to do it this way?

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

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

The output

[#undef #undef

#undef #undef]

[[1] #undef

#undef #undef]

How do I make the first index undefined as it was during the first printout?

In case you dont believe me please about the memory issue take a look below

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

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

@time memtest()
@time memtestF()

Output

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

an un initialized array takes 0.8 gigs and initialized takes 5 gigs. My activity monitor also confirms this.

like image 589
James Beezho Avatar asked Jul 15 '26 11:07

James Beezho


1 Answers

Undefined values are essentially null pointers, and there's no first-class way to "unset" an array element back to a null pointer. This becomes more difficult with very large arrays since you don't want to have much (or any) overhead for your sentinel values that represent unconnected nodes. On a 64 bit system, an array of 100 million elements takes up ~800MB for just the pointers alone, and an empty array takes up 48 bytes for its header metadata. So if you assign an independent empty array to each element, you end up with ~5GB worth of array headers.

The behavior of fill! in Julia 0.3 is a little flakey (and has been corrected in 0.4). If, instead of filling your array with [], you fill! it with an explicitly typed Int64[], every element will point to the same empty array. This means that your array and its elements will only take up 48 more bytes than the uninitialized array. But it also means that modifying that subarray for one of your nodes (e.g., with push!) will mean that all nodes will get that connection. This probably isn't what you want. You could still use the empty array as a sentinel value, but you'd have to be very careful to not modify it.

If your array is going to be densely packed with subarrays, then there's no straightforward way around this overhead for the array headers. A more robust and forward-compatible way of initializing an array with independent empty arrays is with a comprehension: Vector{Int64}[Int64[] for i=1:2, j=1:2]. This will also be more performant in 0.3 as it doesn't need to convert [] to Int64[] for each element. If each element is likely to contain a non-empty array, you'll need to pay the cost of the array overhead in any case. To remove the node from the topology, you'd simply call empty!.

If, however, your array of nodes is going to be sparsely packed, you could try a different data structure that supports unset elements more directly. Depending upon your use-case, you could use a default dictionary that maps an index-tuple to your vectors (from DataStructures.jl; use a function to ensure that the "default" is a newly allocated and independent empty array every time), or try a package dedicated to topologies like Graphs.jl or LightGraphs.jl.

Finally, to answer the actual question you asked, yes, there is a hacky way to unset an element of an array back to #undef. This is unsupported and may break at any time:

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
like image 61
mbauman Avatar answered Jul 18 '26 18:07

mbauman