为了更好地了解并行,我正在比较一组不同的代码。
这是基本的代码(code_piece_1)。
import time
# setup
problem_size = 1e7
items = range(9)
# serial
def counter(num=0):
junk = 0
for i in range(int(problem_size)):
junk += 1
junk -= 1
return num
def sum_list(args):
print("sum_list fn:", args)
return sum(args)
start = time.time()
summed = sum_list([counter(i) for i in items])
print(summed)
print('for loop {}s'.format(time.time() - start))
这段代码以串行方式(for循环)运行了一个耗时者,并得到了结果
sum_list fn: [0, 1, 2, 3, 4, 5, 6, 7, 8]
36
for loop 8.7735116481781s
可以将多处理方式视为实现并行计算的一种方式吗?
我假设是,因为doc这样说。
这是code_piece_2
import multiprocessing
start = time.time()
pool = multiprocessing.Pool(len(items))
num_to_sum = pool.map(counter, items)
print(sum_list(num_to_sum))
print('pool.map {}s'.format(time.time() - start))
此代码以多处理方式同时运行消费者,并获得了结果
sum_list fn: [0, 1, 2, 3, 4, 5, 6, 7, 8]
36
pool.map 1.6011056900024414s
很明显,在这种情况下,多处理程序比串行处理要快。
Dask是用于Python中并行计算的灵活库。
此代码(code_piece_3)与Dask的消费者同时运行(我不确定我是否以正确的方式使用Dask。)
@delayed
def counter(num=0):
junk = 0
for i in range(int(problem_size)):
junk += 1
junk -= 1
return num
@delayed
def sum_list(args):
print("sum_list fn:", args)
return sum(args)
start = time.time()
summed = sum_list([counter(i) for i in items])
print(summed.compute())
print('dask delayed {}s'.format(time.time() - start))
我知道了
sum_list fn: [0, 1, 2, 3, 4, 5, 6, 7, 8]
36
dask delayed 10.288054704666138s
我的CPU有6个物理核心
为什么Dask的执行速度如此之慢,而多处理的执行速度却如此之快?
我使用Dask的方式错误吗?如果是,正确的方法是什么?
注意:请讨论此特定案例或其他特定具体案例。请不要一般地说。
答案 0 :(得分:1)
Q :为什么并行计算要比串行计算花费更长的时间?
因为有更多的方法可以加载到CPU上执行(即使在指令/预期计算块的第一步首先进入CPU之前,“很多”仍然“令人敬畏”),然后纯[SERIAL]
的情况,在执行流程中未添加任何附加成本。
对于这些(隐藏在源代码中)的附加操作(您在[TIME]
域(此类“准备工作”的持续时间)和[SPACE]
域(分配更多RAM包含操作[PARALLEL]
的代码所需的所有涉及结构(好吧,如果我们的术语学得很准确的话,大多数情况下仍然是[CONCURRENT]
操作的代码),这又使您花费{ {1}},因为每个RAM-I / O花费的费用大约是[TIME]
的1/3)
除非,您的工作负载包具有“足够”的工作量,可以并行执行(非阻塞,无锁,互斥,共享,无依赖项,无我/ O,...实际上是独立的,具有最少的RAM-I / O重新获取),很容易“付款方式比您获得的回报多” 。
有关附加费用的详细信息以及对最终加速产生巨大影响的事情,请开始阅读the criticism of blind using the original, overhead naive formulation of the Amdahl's law here。
答案 1 :(得分:1)
您拥有的代码需要GIL,因此一次仅运行一个任务,而您得到的只是额外的开销。例如,如果您将分布式调度程序与进程一起使用,那么您将获得更好的性能。
答案 2 :(得分:1)
在您的示例中,dask比python多处理要慢,因为您没有指定调度程序,所以dask使用多线程后端,这是默认设置。正如mdurant所指出的那样,您的代码不会释放GIL,因此多线程无法并行执行任务图。
在这里查看有关该主题的完整概述:https://docs.dask.org/en/stable/scheduler-overview.html
对于您的代码,可以通过调用以下命令切换到多处理后端:
.compute(scheduler='processes')
。
如果使用多处理后端,则进程之间的所有通信仍需要通过主进程。因此,您可能还需要检出分布式调度程序,在该调度程序中工作进程可以直接相互通信,这对于复杂的任务图尤其有用。此外,分布式调度程序支持工作窃取以平衡流程之间的工作,并具有Web界面,可提供有关正在运行的任务的某些诊断信息。即使只想在本地计算机上进行计算,使用分布式调度程序而不是多处理调度程序通常也很有意义。