Threading: Array comprehension vs. fill

I have noticed a very strange issue, which occurs in julia 1.4.2 on Windows 10. I have neither been able to reproduce it on Fedora 32 with julia 1.4.2 nor on Ubuntu 20.04 (WSL2) with julia 1.4.1. Define two functions:

function mwe_comprehension(N)

    out_split = [zeros(N,N) for k in Base.OneTo(Threads.nthreads())]

    Threads.@threads for k in 1:N
        t = Threads.threadid()
        out_split[t] .+= k
    end

    out = broadcast(+, out_split...)
end

and

function mwe_fill(N)

    out_split = fill(zeros(N,N), Threads.nthreads())

    Threads.@threads for k in 1:N
        t = Threads.threadid()
        out_split[t] .+= k
    end

    out = broadcast(+, out_split...)
end

If we know run both functions for N=4 we get

julia> mwe_fill(4)
4×4 Array{Float64,2}:
 40.0  40.0  40.0  40.0
 40.0  40.0  40.0  40.0
 40.0  40.0  40.0  40.0
 40.0  40.0  40.0  40.0

julia> mwe_comprehension(4)
4×4 Array{Float64,2}:
 10.0  10.0  10.0  10.0
 10.0  10.0  10.0  10.0
 10.0  10.0  10.0  10.0
 10.0  10.0  10.0  10.0

where only the comprehension version yields the correct output. On both linux OS’ the results are identical. How comes there is a difference? Running fill with a matrix as an argument and the array comprehension should give the same results.

Thank you very much in advance!

I think the issue is that fill gets one array & re-uses it, see e.g. here: Structure Usage . And I guess that in mwe_fill(N) the different threads are over-writing each other’s results.

Edit – actually, no overwriting is necessary, you get the same result without threads. mwe_comprehension(4) simply contains sum(1:4), while mwe_fill(4) contains that times the number of threads.

But the result of mwe_fill is not safe, e.g. unique(mwe_fill(100)) has a variety of numbers, different on every run.

3 Likes

Thank you very much! I did not expect fill to reuse the array - now my observations make sense.

See also https://docs.julialang.org/en/v1.6-dev/base/arrays/#Base.fill

3 Likes