平行
@parallel 可用于并行化循环,将循环的步骤分配给不同的工作者。作为一个非常简单的例子:
addprocs(3)
a = collect(1:10)
for idx = 1:10
println(a[idx])
end
有关更复杂的示例,请考虑:
@time begin
@sync begin
@parallel for idx in 1:length(a)
sleep(a[idx])
end
end
end
27.023411 seconds (13.48 k allocations: 762.532 KB)
julia> sum(a)
55
因此,我们看到如果我们在没有 @parallel
的情况下执行了这个循环,那么执行它将花费 55 秒而不是 27 秒。
我们还可以为 @parallel
宏提供减少运算符。假设我们有一个数组,我们想要对数组的每一列求和,然后将这些和相乘:
A = rand(100,100);
@parallel (*) for idx = 1:size(A,1)
sum(A[:,idx])
end
使用 @parallel
避免意外行为时,需要记住几件重要的事情。
第一: 如果你想在你的循环中使用不在基础 Julia 中的任何函数(例如你在脚本中定义的函数或从包中导入的函数),那么你必须使这些函数可以被 worker 使用。因此,例如,以下将不工作:
myprint(x) = println(x)
for idx = 1:10
myprint(a[idx])
end
相反,我们需要使用:
@everywhere begin
function myprint(x)
println(x)
end
end
@parallel for idx in 1:length(a)
myprint(a[idx])
end
第二,虽然每个工作人员都能够访问控制器范围内的对象,但他们无法修改它们。从而
a = collect(1:10)
@parallel for idx = 1:length(a)
a[idx] += 1
end
julia> a'
1x10 Array{Int64,2}:
1 2 3 4 5 6 7 8 9 10
然而,如果我们已经执行了 @parallel 的循环,它将成功修改数组 a
。
为了解决这个问题,我们可以将 a
设为 SharedArray
类型的对象,以便每个工作人员都可以访问和修改它:
a = convert(SharedArray{Float64,1}, collect(1:10))
@parallel for idx = 1:length(a)
a[idx] += 1
end
julia> a'
1x10 Array{Float64,2}:
2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0