【发布时间】:2019-08-26 20:47:53
【问题描述】:
我在脚本foo.jl 中有以下代码来执行一个简单的可并行化任务(它可能看起来很普通,但实际上类似于我研究中的一项关键任务):
using LinearAlgebra
using Statistics
N = 60
length = 200
function matOps(mat1, mat2)
mat = mat1'mat2 - mat2'mat1
eye = Matrix{Float64}(I,N,N)
for i in 0:10000
mat = eye*transpose(mat)*eye
end
return mat
end
y = zeros(length)
println("Number of threads: $(Threads.nthreads())")
@time Threads.@threads for i in 1:length
mat1 = rand(N,N)
mat2 = rand(N,N)
y[i] = mean(matOps(mat1, mat2))
end
所以这一切只是在 60x60 矩阵上执行一些(无意义的)操作 10,000 次,然后取矩阵元素的平均值并将其存储在长度为 200 的数组 y 中。所以我们循环这个 200 长度数组和每个元素我们做这个矩阵运算链,并存储单个平均值。
这是非常可并行化的,因为每次迭代都是彼此完全独立的,但是使用更多线程运行循环实际上会更慢。下面是一些输出:
$:> julia foo.jl
Number of threads: 1
184.268869 seconds (9.85 M allocations: 107.718 GiB, 5.16% gc time)
$:> $env:JULIA_NUM_THREADS=2
$:> julia foo.jl
Number of threads: 2
377.960229 seconds (9.85 M allocations: 107.583 GiB, 71.04% gc time)
$:> $env:JULIA_NUM_THREADS=4
$:> julia foo.jl
Number of threads: 4
1121.259542 seconds (9.84 M allocations: 107.190 GiB, 94.31% gc time)
这实际上给了我使用Distributed 和SharedArrays 而不是Threads 的加速。我也理解Threads 是实验性的,所以我理解这是否只是正在处理的事情。只是想我会检查我是否做错了什么,因为只看时间是如何缩放的,似乎每个线程都在按顺序执行整个循环。
我在 Windows 10 上使用 Powershell,Julia 版本 1.0.3
编辑:我将矩阵运算从eye*mat'eye 更改为eye*transpose(mat)*eye,因为它弄乱了这里的语法。因为垫子是真实的,所以它是一样的。
【问题讨论】:
-
您应该从阅读性能提示开始:docs.julialang.org/en/v1/manual/performance-tips/index.html 特别是,您正在使用全局变量,并且您还测量了编译时间而不仅仅是运行时。将您的代码放入函数中,从 REPL 内部调用它们,并使用 BenchmarkTools 而不是
@time宏。此外,至少在 1.2 版之前,rand不是线程安全的,应该在线程循环之外。 -
顺便说一句,
length是一个非常重要的内置函数,所以不建议覆盖它。 -
@Bebotron - 解决
rand问题的最简单方法是隔离mat1和mat2的生成,这是您过程中的快速步骤,带有锁。在 Julia 1.3 中,rand默认是线程安全的。 -
嗯,在我的实际研究中,这是在没有全局变量的函数中完成的,我只是想找到最简单的方法来复制问题。但可以肯定的是,即使将所有内容都放入
function main(),并将rand声明置于循环之外,我仍然会遇到同样的减速。
标签: multithreading julia