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

ins*_*get 6 python multithreading python-3.x threadpoolexecutor concurrent.futures

我正在使用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')
Run Code Online (Sandbox Code Playgroud)

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

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

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

有没有人知道为什么这可能需要这么长时间?似乎一个简单的平方操作,实际上是高度可并行化的,应该真的不需要这么长时间.它可能是由于字典的人口(如果是这样,是什么导致那里的减速?)?

技术细节:

  • Python 3.3.3
  • 四核(8个具有高速的逻辑核心)CPU
  • MAC OSX 10.9.1(小牛队)

dst*_*erg 2

我还没有尝试过 futures,但我相信它是基于线程的,所以这可能适用: http://www.youtube.com/watch ?v=ph374fJqFPE

简而言之,I/O 密集型工作负载在 CPython 中可以很好地线程化,但 CPU 密集型工作负载则不然。如果在同一个进程中混合 I/O 绑定线程和 CPU 绑定线程,线程也不会很好。

如果这就是问题所在,我建议增加工作块的大小(仅对数字进行平方就非常小),并使用multiprocessing。多处理类似于线程,但它使用具有共享内存的多个进程,并且无论如何都倾向于在程序组件之间提供比线程更松散的耦合。

或者切换到 Jython 或 IronPython;据说这些螺纹很好。