当我阅读多核并行计算的 Julia 文档时,我注意到并行映射pmap
和 for-loop都有@distributed for
。
从文档中可以看出,“Juliapmap
专为每个函数调用执行大量工作的情况而设计。相比之下,@distributed for
可以处理每次迭代很小的情况”。
pmap
和 有什么区别@distributed for
?为什么@distributed for
大量工作很慢?
谢谢
当我阅读多核并行计算的 Julia 文档时,我注意到并行映射pmap
和 for-loop都有@distributed for
。
从文档中可以看出,“Juliapmap
专为每个函数调用执行大量工作的情况而设计。相比之下,@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。这意味着集合的每个元素将被一个一个发送到可用的工作人员或任务,以由您提供的函数进行转换。如果每个函数调用做了大量的工作,并且可能每个调用所花费的时间不同,那么 usingpmap
的优点是不让 worker 空闲,而其他 worker 做工作,因为当一个 worker 完成一个转换时,它会要求下一个要转换的元素。因此,pmap
有效地平衡了工作人员/任务之间的负载。
@distributed
然而,for-loop 在开始时在工作人员之间划分给定范围一次,不知道范围的每个分区将花费多少时间。例如,考虑一个矩阵集合,其中集合的前 100 个元素是 2×2 矩阵,接下来的 100 个元素是 1000×1000 矩阵,我们希望使用@distributed
for对每个矩阵求逆-loops 和 2 个工作进程。
@sync @distributed for i = 1:200
B[i] = inv(A[i])
end
第一个工作人员将获得所有 2×2 矩阵,第二个工作人员将获得 1000×1000 矩阵。第一个工人将很快完成所有的转换并闲置,而另一个工人将继续工作很长时间。尽管您使用了 2 个工作人员,但整个工作的主要部分将有效地在第二个工作人员上连续执行,并且使用多个工作人员几乎没有任何好处。这个问题在并行计算的上下文中被称为负载平衡。例如,即使要完成的工作是同质的,当一个处理器速度慢而另一个处理器速度快时,也可能出现问题。
然而,对于非常小的工作转换,使用pmap
小批量会产生可能很大的通信开销,因为在每个批次之后,处理器需要从调用进程中获取下一个批次,而使用@distributed
for-loops,每个工作进程都会知道,在开始时,它负责范围的哪一部分。
pmap
和for 循环之间的选择@distributed
取决于您想要实现的目标。如果您要像 in 那样转换集合,map
并且每次转换都需要大量工作并且这个工作量是变化的,那么您可能会更好地选择pmap
. 如果每个转换都非常小,那么您可能会更好地选择@distributed
for-loop。
请注意,如果您需要在转换后进行归约操作,@distributed
for-loop 已经提供了一项,大部分归约将在本地应用,而最终归约将在调用进程上进行。但是,pmap
您需要自己处理减少。
pmap
如果您真的需要,您还可以使用非常复杂的负载平衡和减少方案来实现自己的功能。