用于矩阵构造的Julia线程安全循环并行性



我的Julia for循环属于"令人尴尬的平行";形成

M=Array{Float64}(undef,200,100,100)
for i in 1:200
M[i,:,:,:]=construct_row(i)
end

其中CCD_ 1是返回100x100矩阵的某个函数。

我想使用我可用的64个内核(实际上是272个线程,因为超线程(,通过让每个k值在自己的线程上运行来并行化这个循环。因此,我用Threads作为for循环的序言@螺纹。

据我所知,这是一个明显的线程安全情况,不需要同步。然而,在阅读之后https://discourse.julialang.org/t/poor-performance-while-multithreading-julia-1-0/20325/9,我关心foobar_lv2 的评论

最致命的模式是例如4xN矩阵,其中线程k读写M[k,:]:显然对人类来说是安全的,非常对于将在循环中运行的糟糕CPU来说,这显然是线程安全的。

那么:多线程是否会像我为for循环所描述的那样工作,或者我在这里遗漏了一些主要问题

Julia矩阵是列主矩阵。事实上,当每个线程改变相邻的内存单元时,您将获得最佳性能。因此,最佳性能将通过以下途径获得:

@inbounds for i in 1:100
(@view M[:,:,i]) .= construct_row(i)
end

为了说明,让我们在运行4个线程的Julia上进行测试:

julia> const m=Array{Float64}(undef,100,100,100);
julia> @btime Threads.@threads for i in 1:100
(@view m[:,:,i]) .= reshape(1.0:10_000.0,100,100)
end
572.500 μs (19 allocations: 2.39 KiB)
julia> @btime Threads.@threads for i in 1:100
(@view m[i,:,:]) .= reshape(1.0:10_000.0,100,100)
end
1.051 ms (21 allocations: 2.45 KiB)

你可以看到,突变相邻的细胞意味着2倍的性能。

拥有如此大量的线程,您还应该考虑使用多处理和SharedArrays。我的经验是,超过16或32个线程-多处理可以产生比多线程更好的性能。然而,这是针对具体情况的,需要适当的基准。

最新更新