Python中的多进程和多线程

4
我听说过这样一种说法:“如果你想从并行应用程序中获得最大性能,你应该创建与计算机CPU数量相同的进程,并在每个进程中创建一些线程”。

这是真的吗?

我写了一段代码来实现这个习惯:

import multiprocessing, threading

number_of_processes = multiprocessing.cpu_count()
number_of_threads_in_process = 25   # some constant


def one_thread():
    # very heavyweight function with lots of CPU/IO/network usage
    do_main_work()


def one_process():
    for _ in range(number_of_threads_in_process):
        t = threading.Thread(target=one_thread, args=())
        t.start()


for _ in range(number_of_processes):
    p = multiprocessing.Process(target=one_process, args=())
    p.start()

这是否正确?我的do_main_work函数真的会并行运行,而不会遇到任何GIL问题吗?

谢谢。


每个进程将拥有25个线程,在它们之间,GIL仍然存在。 - bereal
3个回答

3

这真的很大程度上取决于你正在做什么。

请记住,在CPython中,由于GIL的存在,只有一个线程可以执行Python字节码(因此在计算密集型问题中使用线程并不能帮助你太多)。

一种将可以并行处理的工作分散开来的方法是使用multiprocessing.Pool。默认情况下,它不会使用比CPU核心更多的进程。使用更多进程主要会导致它们争夺资源(CPU、内存),而无法完成有用的工作。

但是利用多个处理器需要你为它们提供工作!换句话说,如果问题无法被分成可以分别且并行地计算的较小部分,则许多CPU核心将没有太多用处。

此外,并非所有问题都受到必须计算的数量的限制。

计算机的RAM比CPU慢得多。如果你正在处理的数据集比CPU缓存要大得多,从RAM读取数据并将结果返回可能会成为速度瓶颈。这称为内存绑定

如果你处理的数据量超过了机器内存的容量,那么你的程序将会频繁地进行读写操作。相对于内存而言,磁盘读写速度较慢,而相对于 CPU 而言则非常缓慢,因此你的程序会变得 I/O-bound

2
# very heavyweight function with lots of CPU/IO/network usage

许多CPU会因为GIL而受到影响,所以你只能从多个进程中获得好处。

IOnetwork(实际上网络也属于IO)不会受到GIL太大的影响,因为在IO操作完成后,lock会被显式释放并重新获取。 在CPython中有宏定义来实现这一点:

Py_BEGIN_ALLOW_THREADS
... Do some blocking I/O operation ...
Py_END_ALLOW_THREADS

由于使用了GIL包装代码,性能仍然会受到影响,但是使用多个线程仍然可以获得更好的性能。

最后 - 这是一个普遍的规则,不仅适用于Python:最优线程/进程数量取决于程序实际执行的任务。一般来说,如果程序需要大量使用CPU,当进程数大于CPU内核数时,几乎没有性能提升。例如Gentoo文档表示,编译器的最佳线程数为CPU内核数+1。


0

我认为你每个进程使用的线程数太高了。通常对于任何英特尔处理器,每个进程的线程数为2。核心数量从2(英特尔酷睿i3)到6(英特尔酷睿i7)不等。因此,在所有进程同时运行时,最大线程数将为6*2=12。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接