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

时间:2011-09-25 01:00:25

标签: python multithreading

问题:因为python使用“GIL”是python能够同时运行它的单独线程吗?


的信息:

在阅读this后,我不确定python是否能够利用多核处理器。就像python一样,认为它缺乏如此强大的能力感觉真的很奇怪。所以感觉不确定,我决定在这里问。如果我编写一个多线程的程序,它是否能够在多个内核上同时执行?

9 个答案:

答案 0 :(得分:44)

答案是“是的,但是......”

但是当您使用常规线程进行并发时,cPython无法实现。

您可以使用multiprocessingcelerympi4py之类的内容将并行工作拆分为另一个流程;

或者您可以使用JythonIronPython之类的内容来使用没有GIL的替代解释器。

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

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

答案 1 :(得分:29)

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

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

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

(或使用并行语言。)

答案 2 :(得分:10)

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

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

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

答案 3 :(得分:2)

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

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

  • 对于这个测试 - 多处理和线程在 i/o 绑定方面有相似的结果,
    但多处理比 cpu 绑定的线程更有效。

使用线程:

典型结果:
.启动 4000 个循环的 io-bound 线程
.顺序运行时间:39.15 秒
. 4线程并行运行时间:18.19秒
. 2 个线程并行 - 两次运行时间:20.61 秒

典型结果:
.启动 1000000 个循环的 cpu-only 线程
.顺序运行时间:9.39 秒
. 4线程并行运行时间:10.19秒
. 2 个线程并行两次 - 运行时间:9.58 秒

使用多处理:

典型结果:
.启动 4000 个循环的 io-bound 处理
.顺序 - 运行时间:39.74 秒
. 4 进程并行 - 运行时间:17.68 秒
. 2 procs 并行两次 - 运行时间:20.68 秒

典型结果:
.启动 1000000 个循环的 cpu-only 处理
.顺序运行时间:9.24 秒
. 4 进程并行 - 运行时间:2.59 秒
. 2 procs 并行两次 - 运行时间: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))

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))


答案 4 :(得分:1)

线程共享进程并且进程在核心上运行,但是您可以使用python的多处理模块在单独的进程中调用您的函数并使用其他核心,或者您可以使用子进程模块,它可以运行您的代码和非也是python代码。

答案 5 :(得分:1)

示例代码在我的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)

结果:

$ python 1.py
Sequential run time: 3.69 seconds
Parallel run time: 4.82 seconds

答案 6 :(得分:0)

我将脚本转换为Python3并在我的Raspberry Pi 3B +上运行它:

import time
import threading

def t():
        with open('/dev/urandom', 'rb') as f:
                for x in range(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))

python3 t.py

Sequential run time: 2.10 seconds
Parallel run time: 1.41 seconds

对我来说,并行运行更快。

答案 7 :(得分:0)

在Raspberry Pi 3 Model B修订版1.2上

pi@raspberrypi:~/smart/cpu $ python2 core.py
Sequential run time: 2.34 seconds
Parallel run time: 1.74 seconds
pi@raspberrypi:~/smart/cpu $ python2.7 core.py
Sequential run time: 2.34 seconds
Parallel run time: 1.74 seconds
pi@raspberrypi:~/smart/cpu $ python3 core.py
Sequential run time: 2.32 seconds
Parallel run time: 1.74 seconds
pi@raspberrypi:~/smart/cpu $ python3.4 core.py
Sequential run time: 2.32 seconds
Parallel run time: 1.74 seconds

答案 8 :(得分:0)

对我也一样并行速度更快。

range(10000)
Sequential run time: 228.56 seconds
Parallel run time: 147.03 seconds

Raspberry Pi 3 B+
Raspbian v 4.19.57-v7+ #1244
Python v. 3.5.3
GCC v. 6.3

在顺序过程中,CPU利用率达到25%, 在并行过程中,CPU会达到100%