我可以在 python 中为计算密集型任务应用多线程吗?
Can I apply multithreading for computationally intensive task in python?
更新:为了节省大家的时间,我这里直接给出答案。 Python 如果您使用纯 Python 编写代码,则不能 同时使用多个 cpu 内核。但是Python可以同时调用一些用C写的函数或者包,比如Numpy等
我听说“python 中的多线程不是真正的多线程,因为 GIL”。而且我还听说“python 多线程可以处理 IO 密集型任务而不是计算密集型任务,因为同时只有一个线程 运行ning".
但我的经历让我重新思考这个问题。我的经验表明,即使对于计算密集型任务,python 多线程 也可以 几乎加速计算。 (多线程之前,运行下面的程序耗时300秒,使用多线程后,耗时100秒。)
下图显示python
使用CPython
作为编译器包threading
创建了5个线程,所有cpu cores
都接近100%。
我觉得截图可以证明5个cpu核同时运行ning
所以谁能给我解释一下?我可以在 python 中为计算密集型任务应用多线程吗?或者可以在python中同时使用多个threads/cores运行?
我的代码:
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
无多线程的截图
Python线程是真正的线程,只是解释器中不能同时有两个线程(这就是 GIL 的意义所在)。代码的本机部分可以很好地 运行 并行而不会在多个线程上争用,只有当回到解释器时,它们才必须相互序列化。
您将所有 CPU 个核心加载到 100% 的事实并不能证明您正在“高效”地使用机器。您需要确保 CPU 用法不是由于上下文切换引起的。
如果您切换到多处理而不是线程(它们非常相似),您将不必反复猜测,但在线程之间传递时您将不得不编组有效负载。
所以无论如何都需要测量。
正如您提到的,Python 有一个“全局解释器锁”(GIL),可以防止 Python 代码 运行 的两个线程同时宁。 multi-threading 可以加速 IO 绑定任务的原因是 Python 在例如监听网络套接字或等待磁盘读取时释放 GIL。因此 GIL 不会阻止您的计算机同时完成两项工作,它会阻止同一 Python 进程中的两个 Python 线程同时 运行。
在您的示例中,您使用了 numpy 和 scipy。这些主要是用 C 编写的,并利用了用 C/Fortran/Assembly 编写的库(BLAS、LAPACK 等)。当您对 numpy 数组执行操作时,它类似于在 the GIL is released 中监听套接字。当 GIL 被释放并调用 numpy 数组操作时,numpy 开始决定如何执行工作。如果需要,它可以生成其他线程或进程,并且它调用的 BLAS 子例程可能会生成其他线程。准确地说 if/how 如果您想从源代码编译 numpy,可以在构建时配置完成。
总而言之,您发现了规则的例外情况。如果您仅使用纯 Python 函数重复实验,您会得到截然不同的结果(例如,请参阅上面链接的页面的“比较”部分)。
更新:为了节省大家的时间,我这里直接给出答案。 Python 如果您使用纯 Python 编写代码,则不能 同时使用多个 cpu 内核。但是Python可以同时调用一些用C写的函数或者包,比如Numpy等
我听说“python 中的多线程不是真正的多线程,因为 GIL”。而且我还听说“python 多线程可以处理 IO 密集型任务而不是计算密集型任务,因为同时只有一个线程 运行ning".
但我的经历让我重新思考这个问题。我的经验表明,即使对于计算密集型任务,python 多线程 也可以 几乎加速计算。 (多线程之前,运行下面的程序耗时300秒,使用多线程后,耗时100秒。)
下图显示python
使用CPython
作为编译器包threading
创建了5个线程,所有cpu cores
都接近100%。
我觉得截图可以证明5个cpu核同时运行ning
所以谁能给我解释一下?我可以在 python 中为计算密集型任务应用多线程吗?或者可以在python中同时使用多个threads/cores运行?
我的代码:
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
无多线程的截图
Python线程是真正的线程,只是解释器中不能同时有两个线程(这就是 GIL 的意义所在)。代码的本机部分可以很好地 运行 并行而不会在多个线程上争用,只有当回到解释器时,它们才必须相互序列化。
您将所有 CPU 个核心加载到 100% 的事实并不能证明您正在“高效”地使用机器。您需要确保 CPU 用法不是由于上下文切换引起的。
如果您切换到多处理而不是线程(它们非常相似),您将不必反复猜测,但在线程之间传递时您将不得不编组有效负载。
所以无论如何都需要测量。
正如您提到的,Python 有一个“全局解释器锁”(GIL),可以防止 Python 代码 运行 的两个线程同时宁。 multi-threading 可以加速 IO 绑定任务的原因是 Python 在例如监听网络套接字或等待磁盘读取时释放 GIL。因此 GIL 不会阻止您的计算机同时完成两项工作,它会阻止同一 Python 进程中的两个 Python 线程同时 运行。
在您的示例中,您使用了 numpy 和 scipy。这些主要是用 C 编写的,并利用了用 C/Fortran/Assembly 编写的库(BLAS、LAPACK 等)。当您对 numpy 数组执行操作时,它类似于在 the GIL is released 中监听套接字。当 GIL 被释放并调用 numpy 数组操作时,numpy 开始决定如何执行工作。如果需要,它可以生成其他线程或进程,并且它调用的 BLAS 子例程可能会生成其他线程。准确地说 if/how 如果您想从源代码编译 numpy,可以在构建时配置完成。
总而言之,您发现了规则的例外情况。如果您仅使用纯 Python 函数重复实验,您会得到截然不同的结果(例如,请参阅上面链接的页面的“比较”部分)。