当我阅读多核并行计算的Julia文档时,我注意到有并行映射pmap
和for循环@distributed for
。
从文档中看,"Julia 的pmap
是为每个函数调用执行大量工作的情况而设计的。相比之下,@distributed for
可以处理每次迭代都很小的情况"。
pmap
和@distributed for
有什么区别?为什么@distributed for
对于大量工作来说很慢?
谢谢
问题是pmap
进行负载平衡,而@distributed for
将作业拆分为相等的块。可以通过运行以下两个代码示例来确认这一点:
julia> @time res = pmap(x -> (sleep(x/10); println(x)), [10;ones(Int, 19)]);
From worker 2: 1
From worker 3: 1
From worker 4: 1
From worker 2: 1
From worker 3: 1
From worker 4: 1
From worker 3: 1
From worker 2: 1
From worker 4: 1
From worker 4: 1
From worker 2: 1
From worker 3: 1
From worker 2: 1
From worker 3: 1
From worker 4: 1
From worker 4: 1
From worker 3: 1
From worker 2: 1
From worker 4: 1
From worker 5: 10
1.106504 seconds (173.34 k allocations: 8.711 MiB, 0.66% gc time)
julia> @time @sync @distributed for x in [10;ones(Int, 19)]
sleep(x/10); println(x)
end
From worker 4: 1
From worker 3: 1
From worker 5: 1
From worker 4: 1
From worker 5: 1
From worker 3: 1
From worker 5: 1
From worker 3: 1
From worker 4: 1
From worker 3: 1
From worker 4: 1
From worker 5: 1
From worker 4: 1
From worker 5: 1
From worker 3: 1
From worker 2: 10
From worker 2: 1
From worker 2: 1
From worker 2: 1
From worker 2: 1
1.543574 seconds (184.19 k allocations: 9.013 MiB)
Task (done) @0x0000000005c5c8b0
你可以看到,大作业(值10
)使得pmap
对获得大作业的工人执行所有小作业不同(在我的示例中,工人5
只做10
工作,而工人2
到4
做所有其他工作)。另一方面,@distributed for
为每个工人分配相同数量的作业。因此,获得工作10
的工人(第二个示例中的工人2
)仍然必须做四份短期工作(因为每个工人平均必须做5
份工作 - 我的例子总共有20
份工作和4
名工人)。
现在@distributed for
的优点是,如果工作便宜,那么在工人之间平均分配工作就避免了必须进行动态调度,这也不是免费的。
总之,正如文档所述,如果作业成本高昂(特别是如果其运行时间可能差异很大),则最好使用pmap
,因为它会进行负载平衡。
pmap
有一个batch_size
参数,默认情况下为1。这意味着集合的每个元素都将逐个发送到可用的工作线程或任务,以便由您提供的函数进行转换。如果每个函数调用都执行大量工作,并且每个调用所需的时间可能不同,则使用pmap
的优点是不会让工作线程闲置,而其他工作线程则执行工作,因为当工作线程完成一个转换时,它将要求下一个元素进行转换。因此,pmap
有效地平衡了工作人员/任务之间的负载。
但是,@distributed
for 循环在开始时在工作线程之间对给定范围进行一次分区,不知道该范围的每个分区将花费多少时间。例如,考虑一个矩阵集合,其中集合的前一百个元素是 2×2 矩阵,接下来的一百个元素是 1000 x 1000 个矩阵,我们希望使用@distributed
for 循环和 2 个工作进程获取每个矩阵的逆矩阵。
@sync @distributed for i = 1:200
B[i] = inv(A[i])
end
第一个工作线程将获得所有 2×2 矩阵,第二个工作线程将获得 1000 x 1000 矩阵。第一个工作人员将很快完成所有转换并闲置,而另一个工作人员将继续长时间工作。虽然您使用 2 个工作线程,但整个工作的主要部分将在第二个工作线程上有效地串行执行,并且使用多个工作线程几乎没有任何好处。此问题在并行计算的上下文中称为负载平衡。例如,当一个处理器很慢而另一个处理器很快时,即使要完成的工作是同质的,也可能会出现问题。
但是,对于非常小的工作转换,使用具有小批量大小的pmap
会产生通信开销,这可能很大,因为在每个批处理之后,处理器需要从调用进程中获取下一个批处理,而对于@distributed
for-loop,每个工作进程在开始时都会知道它负责范围的哪一部分。
pmap
和@distributed
for 循环之间的选择取决于您要实现的目标。如果您要像map
一样转换集合,并且每次转换都需要大量工作并且工作量各不相同,那么选择pmap
可能会更好。如果每个转换都非常小,那么选择@distributed
for 循环可能会更好。
请注意,如果在转换后需要缩减操作,@distributed
for 循环已经提供了一个,则大部分缩减将在本地应用,而最终的缩减将在调用过程中进行。但是,使用pmap
,您将需要自己处理减少。
如果您确实需要,您还可以使用非常复杂的负载平衡和减少方案来实现自己的pmap
功能。
https://docs.julialang.org/en/v1/manual/parallel-computing/