是python能够在多个核心上运行?

Nar*_*ser 57 python multithreading

问:因为python使用"GIL"是python能够同时运行其单独的线程吗?


信息:

看完这个我离开的时候相当不确定的蟒蛇是否是能够把多核处理器的优势.就像python一样,认为它缺乏如此强大的能力感觉真的很奇怪.所以感觉不确定,我决定在这里问.如果我编写一个多线程的程序,它是否能够在多个内核上同时执行?

Sin*_*ion 52

答案是"是的,但......"

但是当你使用常规线程进行并发时,cPython不能.

您既可以使用类似的东西multiprocessing,celerympi4py可以将并行工作拆分为另一个流程;

或者你可以使用像JythonIronPython这样的东西来使用没有GIL的替代解释器.

一个更软的解决方案是使用不与GIL相遇的库来执行繁重的CPU任务,例如numpy可以在不保留GIL的情况下进行繁重的工作,因此其他python线程可以继续.您也可以ctypes这种方式使用库.

如果你没有进行CPU绑定工作,你可以完全忽略GIL问题(种类),因为python在等待IO时不会获取GIL.

  • 考虑几乎每个人都回答我的问题,好像我不知道GIL是什么,我觉得你是唯一一个阅读整个问题的人...无论如何,谢谢,所有链接到各种库是相当的很有帮助. (2认同)

nin*_*cko 33

Python 线程无法利用许多内核.这是由于在python(cPython)的C实现中称为GIL(全局解释器锁)的内部实现细节,几乎可以肯定你使用它.

解决方法是为此目的开发的multiprocessing模块http://www.python.org/dev/peps/pep-0371/.

文档:http://docs.python.org/library/multiprocessing.html

(或者使用并行语言.)

  • +1 表示线程在某种程度上受到限制,这是我迄今为止所经历的……使用多处理它可以工作! (4认同)

小智 11

正如之前的答案所述 - 这取决于“cpu 或 i/o 限制?”的答案,
还取决于“线程或多处理?”的答案:

示例在 Raspberry Pi 3B 1.2GHz 4 核和 Python3.7.3 上运行
- (运行其他进程,包括 htop )

  • 对于此测试 - 多处理和线程对于 i/o 限制具有相似的结果,
    但多处理比 cpu 限制的线程更有效。

使用线程:

典型结果:
. 启动 4000 个 io 绑定线程周期
。顺序运行时间:39.15秒
。4个线程并行运行时间:18.19秒
。2 个线程并行 - 两次运行时间:20.61 秒

典型结果:
. 启动 1000000 个仅 cpu 线程周期
。顺序运行时间:9.39秒
。4个线程并行运行时间:10.19秒
。2 个线程并行两次 - 运行时间:9.58 秒

使用多处理:

典型结果:
. 开始 4000 个 io 绑定处理周期
。顺序运行时间:39.74 秒
。4 个进程并行运行时间:17.68 秒
。2 个进程并行两次 - 运行时间:20.68 秒

典型结果:
. 启动 1000000 个仅 cpu 处理周期
。顺序运行时间:9.24秒
。4 个进程并行运行时间:2.59 秒
。2 个进程并行两次 - 运行时间:4.76 秒

compare_io_multiproc.py:

#!/usr/bin/env python3

# Compare single proc vs multiple procs execution for io bound operation

"""
Typical Result:
  Starting 4000 cycles of io-bound processing
  Sequential - run time: 39.74 seconds
  4 procs Parallel - run time: 17.68 seconds
  2 procs Parallel twice - run time: 20.68 seconds
"""
import time
import multiprocessing as mp

# one thousand
cycles = 1 * 1000

def t():
        with open('/dev/urandom', 'rb') as f:
                for x in range(cycles):
                        f.read(4 * 65535)

if __name__ == '__main__':
    print("  Starting {} cycles of io-bound processing".format(cycles*4))
    start_time = time.time()
    t()
    t()
    t()
    t()
    print("  Sequential - run time: %.2f seconds" % (time.time() - start_time))

    # four procs
    start_time = time.time()
    p1 = mp.Process(target=t)
    p2 = mp.Process(target=t)
    p3 = mp.Process(target=t)
    p4 = mp.Process(target=t)
    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p1.join()
    p2.join()
    p3.join()
    p4.join()
    print("  4 procs Parallel - run time: %.2f seconds" % (time.time() - start_time))

    # two procs
    start_time = time.time()
    p1 = mp.Process(target=t)
    p2 = mp.Process(target=t)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    p3 = mp.Process(target=t)
    p4 = mp.Process(target=t)
    p3.start()
    p4.start()
    p3.join()
    p4.join()
    print("  2 procs Parallel twice - run time: %.2f seconds" % (time.time() - start_time))
Run Code Online (Sandbox Code Playgroud)

compare_cpu_multiproc.py

#!/usr/bin/env python3

# Compare single proc vs multiple procs execution for cpu bound operation

"""
Typical Result:
  Starting 1000000 cycles of cpu-only processing
  Sequential run time: 9.24 seconds
  4 procs Parallel - run time: 2.59 seconds
  2 procs Parallel twice - run time: 4.76 seconds
"""
import time
import multiprocessing as mp

# one million
cycles = 1000 * 1000

def t():
    for x in range(cycles):
        fdivision = cycles / 2.0
        fcomparison = (x > fdivision)
        faddition = fdivision + 1.0
        fsubtract = fdivision - 2.0
        fmultiply = fdivision * 2.0

if __name__ == '__main__':
    print("  Starting {} cycles of cpu-only processing".format(cycles))
    start_time = time.time()
    t()
    t()
    t()
    t()
    print("  Sequential run time: %.2f seconds" % (time.time() - start_time))

    # four procs
    start_time = time.time()
    p1 = mp.Process(target=t)
    p2 = mp.Process(target=t)
    p3 = mp.Process(target=t)
    p4 = mp.Process(target=t)
    p1.start()
    p2.start()
    p3.start()
    p4.start()
    p1.join()
    p2.join()
    p3.join()
    p4.join()
    print("  4 procs Parallel - run time: %.2f seconds" % (time.time() - start_time))

    # two procs
    start_time = time.time()
    p1 = mp.Process(target=t)
    p2 = mp.Process(target=t)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    p3 = mp.Process(target=t)
    p4 = mp.Process(target=t)
    p3.start()
    p4.start()
    p3.join()
    p4.join()
    print("  2 procs Parallel twice - run time: %.2f seconds" % (time.time() - start_time))


Run Code Online (Sandbox Code Playgroud)


Ned*_*der 10

CPython(Python的经典和流行的实现)不能同时具有多个执行Python字节码的线程.这意味着计算绑定程序将只使用一个核心.C扩展(例如numpy)内发生的I/O操作和计算可以同时运行.

Python的其他实现(例如Jython或PyPy)可能表现不同,我对它们的细节不太清楚.

通常的建议是使用许多进程而不是许多线程.


est*_*est 5

示例代码在我的 ubuntu 14.04、python 2.7 64 位上使用所有 4 个核心。

import time
import threading


def t():
    with open('/dev/urandom') as f:
        for x in xrange(100):
            f.read(4 * 65535)

if __name__ == '__main__':
    start_time = time.time()
    t()
    t()
    t()
    t()
    print "Sequential run time: %.2f seconds" % (time.time() - start_time)

    start_time = time.time()
    t1 = threading.Thread(target=t)
    t2 = threading.Thread(target=t)
    t3 = threading.Thread(target=t)
    t4 = threading.Thread(target=t)
    t1.start()
    t2.start()
    t3.start()
    t4.start()
    t1.join()
    t2.join()
    t3.join()
    t4.join()
    print "Parallel run time: %.2f seconds" % (time.time() - start_time)
Run Code Online (Sandbox Code Playgroud)

结果:

$ python 1.py
Sequential run time: 3.69 seconds
Parallel run time: 4.82 seconds
Run Code Online (Sandbox Code Playgroud)

  • 所以并行运行时间比顺序运行时间更糟糕:o (5认同)
  • 在这里你会失去上下文切换的机会。对您来说真正的好处是,如果您与网络服务交互,其中大部分时间都花在等待响应上。 (2认同)