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 问题吗?

谢谢你。

4

3 回答 3

3

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

请记住,在 CPython 中,一次只能有一个线程执行 Python 字节码(因为 GIL)。因此,对于 CPython 线程中的计算密集型问题不会有太大帮助。

分散可以并行完成的工作的一种方法是使用multiprocessing.Pool. 默认情况下,这不会使用您的 CPU 具有内核的更多进程。使用更多的进程主要会让它们争夺资源(CPU、内存),而不是完成有用的工作。

但是利用多个处理器需要您有工作让他们去做!换句话说,如果不能将问题分割成可以单独并行计算的小块,那么许多 CPU 内核将没有多大用处。

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

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

如果您正在处理的数据比机器内存所能容纳的多得多,那么您的程序将从磁盘进行大量读取和写入。与 RAM 相比,磁盘慢,与 CPU 相比非常慢,因此您的程序成为I/O-bound

于 2014-07-30T13:01:54.137 回答
2
# very heavyweight function with lots of CPU/IO/network usage

许多CPU会因为GIL而受到影响,因此您只能从多个进程中受益。

IO网络(其实网络也是一种 IO)不会受到GIL太大的影响,因为锁是显式释放的,在IO操作完成后会再次获取。CPython中对此有宏定义:

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

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

最后——这是一条通用规则——不仅适用于 Python:线程/进程的最佳数量取决于程序实际在做什么。通常,如果它密集使用 CPU,如果进程数大于 CPU 内核数,则几乎没有性能提升。例如,Gentoo 文档说编译器的最佳线程数是 CPU cores + 1。

于 2014-07-30T12:55:29.497 回答
0

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

于 2019-07-16T06:27:27.770 回答