Why do take! and put! for channels allocate memory?

I ran a bunch of floats through some channels across threads to test it out and discovered that memory was being allocated during the process which seems odd. Increasing the count in the for loop increases the allocations so there appear to be allocations throughout use. This reported 22.88MB bytes allocated. I ran --track-allocation=user to analyze it and although it showed allocations all over the place, by far most were on the put! and take! calls. I ran @code_warntype against this code and there were no warnings. Is this expected? Is it something that can be optimized somehow?

function proc(ch1, ch2)
    i = 0
    try
        while true
            x = take!(ch1)
            put!(ch2, x)
            i += 1
        end
    catch _
        println("processed $(i)")
    end
end
function consume(ch)
    i = 0
    try
        while true
            take!(ch)
            i += 1
        end
    catch _
        println("consumed $(i)")
    end
end
function test()
    len = 100000
    sink1 = Channel{Float64}(len)
    sink2 = Channel{Float64}(len)
    sink3 = Channel{Float64}(len)

    Threads.@spawn consume(sink3)
    Threads.@spawn proc(sink2, sink3)
    for _ in 1:Threads.nthreads()
        Threads.@spawn proc(sink1, sink2)
    end

    for _ in 1:10000
        put!(sink1, 1.1)
    end

    sleep(1)

    d = @timed begin
        for _ in 1:100000
            put!(sink1, 1.1)
        end
        sleep(4.0)
    end
    sleep(1.0)
    println(d)
    close(sink1)
    close(sink2)
    close(sink3)
    sleep(1.0)
end
1 Like

I’m starting to get the feeling that the recommendation to look at the code is a good one. It appears that Channel uses a Vector for the buffer https://github.com/JuliaLang/julia/blob/master/base/channels.jl#L39 . It’s not going to handle multithreading efficiently.

I stumbled across this announcement: ConcurrentCollections.jl: "lock-free" dictionary, queue, etc. for Julia 1.7 so I’ll take a look at those and see if they are more appropriate.