平行

@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