【问题标题】:Can I apply multithreading for computationally intensive task in python?我可以在 python 中为计算密集型任务应用多线程吗?
【发布时间】:2020-09-29 14:28:07
【问题描述】:

更新:为了节省您的时间,我直接在这里给出答案。如果您使用纯 Python 编写代码,Python 不能同时使用多个 cpu 内核。但是 Python 可以在调用一些用 C 语言编写的函数或包时同时使用多核,例如 Numpy 等。


我听说“python 中的多线程不是真正的多线程,因为 GIL”。而且我还听说“python多线程可以处理IO密集型任务而不是计算密集型任务,因为只有一个线程同时运行”。

但我的经历让我重新思考了这个问题。我的经验表明,即使对于计算密集型任务,python 多线程 可以 几乎可以加速计算。 (在多线程之前,我运行下面的程序花了我 300 秒,在我使用多线程之后,我花了 100 秒。)

下图显示pythonCPython为编译器,包threading创建了5个线程,所有cpu cores的百分比接近100%。

我认为截图可以证明5个cpu核心同时运行。

那么谁能给我解释一下?我可以将多线程应用于 python 中的计算密集型任务吗?或者python中可以多线程/多核同时运行吗?

我的代码:


import threading
import time
import numpy as np
from scipy import interpolate


number_list = list(range(10))

def image_interpolation():
    while True:
        number = None
        with threading.Lock():
            if len(number_list):
                number = number_list.pop()
        if number is not None:
            # Make a fake image - you can use yours.
            image = np.ones((20000, 20000))
            # Make your orig array (skipping the extra dimensions).
            orig = np.random.rand(12800, 16000)
            # Make its coordinates; x is horizontal.
            x = np.linspace(0, image.shape[1], orig.shape[1])
            y = np.linspace(0, image.shape[0], orig.shape[0])
            # Make the interpolator function.
            f = interpolate.interp2d(x, y, orig, kind='linear')

        else:
            return 1

workers=5
thd_list = []
t1 = time.time()

for i in range(workers):
    thd = threading.Thread(target=image_interpolation)
    thd.start()
    thd_list.append(thd)

for thd in thd_list:
    thd.join()

t2 = time.time()
print("total time cost with multithreading: " + str(t2-t1))
number_list = list(range(10))

for i in range(10):
    image_interpolation()

t3 = time.time()

print("total time cost without multithreading: " + str(t3-t2))

输出是:

total time cost with multithreading: 112.71922039985657
total time cost without multithreading: 328.45561170578003

top 的屏幕截图 多线程处理期间

top -H 的屏幕截图 多线程处理期间

top 的屏幕截图,然后按 1多线程中

top -H 的屏幕截图 没有多线程

【问题讨论】:

  • 你用什么代码测试过它?
  • 如果它们都有不同的 PID,那么您很可能是不小心使用了多处理。
  • @FiddleStix 是的,我想知道这一点。此外,509 total, 508 sleeping 表明没有实际工作正在完成。
  • @FiddleStix 我在python中使用线程包,所以我认为我没有使用多处理。
  • @Carcigenicate 我认为509 total, 508 sleeping 没有提供有关运行线程号的任何信息。表示任务号或进程号。

标签: python multithreading cpython gil


【解决方案1】:

正如您所提到的,Python 有一个“全局解释器锁”(GIL),它可以防止 Python 代码 的两个线程同时运行。多线程可以加速 IO 绑定任务的原因是 Python 在例如侦听网络套接字或等待磁盘读取时释放 GIL。因此,GIL 不会阻止您的计算机同时完成两项工作,它会阻止同一 Python 进程中的两个 Python 线程同时运行。

在您的示例中,您使用 numpy 和 scipy。这些主要是用 C 编写的,并利用用 C/Fortran/Assembly 编写的库(BLAS、LAPACK 等)。当您对 numpy 数组执行操作时,它类似于在 the GIL is released 中侦听套接字。当 GIL 被释放并调用 numpy 数组操作时,numpy 开始决定如何执行工作。如果需要,它可以生成其他线程或进程,并且它调用的 BLAS 子例程可能会生成其他线程。如果你想从源代码编译 numpy,可以在构建时配置是否/如何完成。

因此,总而言之,您发现了该规则的一个例外。如果你只使用纯 Python 函数重复实验,你会得到完全不同的结果(例如,参见上面链接页面的“比较”部分)。

【讨论】:

  • 你的意思是,如果我使用可能用 C 编写的包中的函数,那么多个线程可以同时运行。但是如果我只使用纯python构建的函数,那么多线程就不能同时运行了。对吗?
  • 是的,没错。我不确定 Python 解释器是否会自动释放 GIL,或者 C 代码(即本例中的 numpy)是否必须明确这样做。换句话说,我不确定它是否适用于从 Python 调用的所有 C 代码,它可能取决于具体情况。
【解决方案2】:

Python 线程是真正的线程,只是解释器中不能同时存在两个线程(这就是 GIL 的意义所在)。代码的本机部分可以很好地并行运行,而不会在多个线程上争用,只有在深入解释器时,它们才必须相互序列化。

仅将所有 CPU 内核加载到 100% 的事实并不能证明您正在“有效”地使用机器。您需要确保 CPU 使用率不是由于上下文切换造成的。

如果您切换到多处理而不是线程(它们非常相似),您就不必再猜测了,但是在线程之间传递时您必须编组有效负载。

所以无论如何都需要衡量一切。

【讨论】:

    猜你喜欢
    • 2015-11-15
    • 2020-10-11
    • 1970-01-01
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多