3

模块提供了一个joblib简单的帮助类来使用多处理编写并行 for 循环。

此代码使用列表推导来完成这项工作:

import time
from math import sqrt
from joblib import Parallel, delayed

start_t = time.time()
list_comprehension = [sqrt(i ** 2) for i in range(1000000)]
print('list comprehension: {}s'.format(time.time() - start_t))

大约需要 0.51 秒

list comprehension: 0.5140271186828613s

此代码使用joblib.Parallel()构造函数:

start_t = time.time()
list_from_parallel = Parallel(n_jobs=2)(delayed(sqrt)(i ** 2) for i in range(1000000))
print('Parallel: {}s'.format(time.time() - start_t))

大约需要 31 秒

Parallel: 31.3990638256073s

这是为什么?不应该Parallel()比非并行计算更快吗?

以下是部分内容cpuinfo

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 79
model name      : Intel(R) Xeon(R) CPU @ 2.20GHz
stepping        : 0
microcode       : 0x1
cpu MHz         : 2200.000
cache size      : 56320 KB
physical id     : 0
siblings        : 8
core id         : 0
cpu cores       : 4
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
4

1 回答 1

3

不应该Parallel()比非并行计算更快吗?

好吧,这取决于,很大程度上取决于情况(无论是一种joblib.Parallel()方式还是其他方式)。

没有免费的好处 (所有这些承诺都未能兑现,自 1917 年以来......)

另外,
很容易发生
支付更多的费用 (在用于启动多处理的生成过程上)
比您收到的回报(预期比原始工作流程更快) ......所以必须小心谨慎


最好的第一步:

重新审视Amdahl 的法律修订和对流程调度效果的批评(加速实现了流程流程的重组,并至少在某些部分使用了并行流程调度)。

最初的 Amdahl 的表述没有明确说明进入并行工作流程所必须支付的所谓附加 “成本”[SERIAL] ,这些成本不在最初的纯工作流程预算之内。

1)进程实例化在python中总是很昂贵,因为它首先必须复制尽可能多的副本(O / S驱动的RAM分配大小为n_jobs(2)副本+ O / S驱动的复制主的RAM映像python session) (基于线程的多处理确实会加速,因为在所有生成的线程中仍然存在[SERIAL]工作步骤的 GIL 锁重新化,所以你什么也得不到,而你已经为生成 + 支付了巨大的附加成本每个附加的 GIL-ackquire/GIL-release 舞步——对于计算密集型任务来说是一个糟糕的反模式,它可能有助于掩盖一些与 I/O 相关的延迟情况,但绝对不是计算密集型工作负载的情况)

2)参数传输的附加成本- 您必须将一些数据从主流程移动到新流程。它需要额外的时间,并且您必须支付这个额外的成本,这在原始的纯[SERIAL]工作流程中是不存在的。

3)结果返回传输的附加成本- 您必须将一些数据从新数据移回原始(主)流程。它需要额外的时间,并且您必须支付这个额外的成本,这在原始的纯[SERIAL]工作流程中是不存在的。

4)任何数据交换的附加成本(最好避免在并行工作流程中使用它的任何诱惑 - 为什么?a)它阻塞+ b)它很昂贵,你必须支付更多的附加成本才能获得更多,您无需在纯[SERIAL]原创工作流程中付费)。


为什么joblib.Parallel()比非并行计算需要更多的时间?

简单地说,因为您必须支付更多的费用来启动整个精心策划的马戏团,而不是您从这种并行工作流程组织中获得的回报(工作量太少,math.sqrt( <int> )无法证明产生 2-full 的相对巨大成本是合理的-原始python-(main)-session的副本+所有舞蹈的编排,仅发送每个( <int>)-from-(main)-there并检索返回的每个结果( <float>)-from-(joblib.Parallel( )-过程)-返回-(主)。

您的原始基准测试时间提供了对累积成本的充分比较,以得出相同的结果:

[SERIAL]-<iterator> feeding a [SERIAL]-processing storing into list[]:  0.51 [s]
[SERIAL]-<iterator> feeding [PARALLEL]-processing storing into list[]: 31.39 [s]

原始估计表明,大约 30.9 秒被“浪费来完成相同(少量)的工作,只是因为忘记了一个人总是要支付的附加成本。


那么,在一个人必须支付之前,如何衡量你必须 支付多少......

基准测试、基准测试、对实际代码进行基准测试...(原型)

如果有兴趣对这些成本进行基准测试 -执行 1)、2) 或 3)需要多长时间[us](即您必须支付多少费用,甚至在任何有用的工作开始之前),发布了基准测试模板来测试和验证这些自己平台上的主要成本,在能够决定之前,什么是最小工作包,可以证明这些不可避免的费用是合理的,并产生更大的“积极”加速,(最好更大)>> 1.0000与纯粹的相比-[SERIAL]原创。

于 2019-08-29T12:55:15.403 回答