gpt4 book ai didi

python - concurrent.futures.ThreadPoolExecutor.map 比 for 循环慢

转载 作者:太空狗 更新时间:2023-10-29 23:59:15 25 4
gpt4 key购买 nike

我正在玩 concurrent.futures.ThreadPoolExecutor 看看我是否可以从我的四核处理器(具有 8 个逻辑内核)中挤出更多的工作。所以我写了下面的代码:

from concurrent import futures

def square(n):
return n**2

def threadWorker(t):
n, d = t
if n not in d:
d[n] = square(n)

def master(n, numthreads):
d = {}
with futures.ThreadPoolExecutor(max_workers=numthreads) as e:
for i in e.map(threadWorker, ((i, d) for i in range(n))):
pass # done so that it actually fetches each result. threadWorker has its own side-effects on d
return len(d)

if __name__ == "__main__":
print('starting')
print(master(10**6, 6))
print('done')

有趣的是,同样的功能,当写在 for 循环中时大约需要一秒钟:

>>> d = {}
>>> for i in range(10**6):
... if i not in d: d[i] = i**2

... 而线程池代码需要超过 10 秒。现在我知道它至少使用了 4 个线程,因为我看到每个内核上的处理器负载。但即使使用共享内存(我能理解为什么进程可能需要一段时间,因为内存复制),我觉得运行时的这种差异太大了。

有人知道为什么这会花这么长时间吗?看起来一个简单的平方运算,确实是高度可并行化的,真的不应该花这么长时间。可能是由于字典的数量(如果是这样,是什么导致那里速度变慢的原因?)?

技术细节:

  • python 3.3.3
  • 四核(8 个带超线程的逻辑核心)CPU
  • MAC OSX 10.9.1(小牛)

最佳答案

线程有开销

与其他答案相反,我会声称这里的罪魁祸首不是 GIL(尽管这是一个问题),而是使用线程的开销。

在系统级线程之间产生和切换的开销很小(小于 1 毫秒),但仍然可能超过对单个整数求平方的成本。理想情况下,您希望在使用任何类型的并行性时将计算分成更大的部分(可能是一百万个整数的平方)。

绕过GIL

如果您使用数字 Python 堆栈 (NumPy/Pandas/C/Fortran/Cython/Numba),则可以绕过 GIL。例如,以下函数将对数字数组求平方并释放 GIL。

import numpy as np
x = np.array(my_list)

import numba

@numba.jit(nogil=True)
def square(x):
for i in range(len(x)):
x[i] = x[i]**2
return x

或者大多数 numpy 操作释放 GIL

x = x**2

内存瓶颈

没有系统能够在仅对整数求平方的同时使用多个内核。您的 CPU 计算整数平方的速度远快于您的内存层次结构能够提供它们的速度。

关于python - concurrent.futures.ThreadPoolExecutor.map 比 for 循环慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21210254/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com