Hello,
I need to use large arrays of floats in a large number of iterations. In each iteration, the size of the arrays will be different. If memory permits, I can pre-allocate the memory needed for all iterations at once and all is well.
But what are my options if it doesn’t all fit in memory? I can allocate at each iteration, but that’s painfully slow. So suppose that I pre-allocate the maximum amount of memory used in a single iteration.
I now want to address that memory efficiently. I can reshape a view as indicated below, but that still allocates the memory. Viewing a reshape would presumably not have that problem, but the dimensions may not allow reshaping.
I found an old post that does exactly what I want (How do I reinterpret and reshape a multi-dimensional array `J` into a two-dimensional array `j` such that `typeof(j) <: Matrix` is true in Julia 0.7 - #2 by zygmuntszpak), but the unsafe_wrap
business scares me and the follow up suggestions in the same post depend on the dimensions allowing reshaping.
Is there a more Julian (sanctioned) way of achieving the same goal equally fast?
Here are the times if I run with two threads:
(hungry) 209.590382 seconds (349.13 k allocations: 743.015 GiB, 10.23% gc time, 0.05% compilation time)
(stuffed) 87.106966 seconds (102.11 k allocations: 1.495 GiB, 0.04% gc time, 0.01% compilation time)
(I’m running @time only once, because I only get to run the whole exercise once in practice anyway, so compilation time is part of the story)
const T = Float64
function hungry( x, y, z )
zeros( x, y, z )
end
function stuffed( pool, x, y, z )
unsafe_wrap(Array{T,3}, pointer(pool), (x,y,z)) # *** this does what I want
end
# *** the rest below is for testing
function experiment( which, howoften )
nth = Base.Threads.nthreads()
if which == :stuffed
pool = [ zeros( T, 100_000_000 ) for th ∈ 1:nth ]
end
redundant = zeros( T, nth )
for r ∈ 1:howoften
@info "iteration $r"
x = rand( 1:2000 )
y = rand( 1:div(2_000_000,x) )
z = rand( 1:div(100_000_000,x*y ) )
Base.Threads.@threads for th ∈ 1:nth
if which == :hungry
A = hungry( x, y, z )
elseif which == :stuffed
A = stuffed( pool[th], x, y, z )
end
for k ∈ axes( A, 3 )
for j ∈ axes( A, 2 )
for i ∈ axes( A, 1 )
A[i,j,k] = rand(T) # do something with the memory
end
end
end
redundant[th] = sum( A )
end
end
println( sum(redundant) )
end
function timeme( )
@time experiment( :hungry, 1000 )
@time experiment( :stuffed, 1000 )
# @time experiment( :hungry, 5 )
# @time experiment( :stuffed, 5 )
end
timeme()