尽管有 GIL,多线程仍可加速 CPU 绑定任务

Multithreading accelerates CPU bound tasks despite of GIL

我最近在 python 学习了 GIL。 我正在做一些基准测试,发现多线程实际上确实提高了性能。我比较了不使用任何内部多线程的逐元素 NumPy 操作。在第一个测试中,我从一个 for 循环中连续调用一个函数 32 次。在第二种情况下,我使用多线程。但是如果 GIL 正在工作,在第二种情况下,一次应该只有 1 个线程处于活动状态,因此执行时间应该大致相等(由于多线程开销,在第二种情况下更糟)。这不是我观察到的。

import os
import threading
import numpy as np, time

def elemntwiseoperations(a,b):
    np.exp(a)+np.sin(b)
        
N=1024
a=np.random.rand(N,N)
b=np.random.rand(N,N)


NoTasks=32

start_time = time.time()
for i in range(NoTasks):
    elemntwiseoperations(a,b)
print("Execution time for {} tasks: {} seconds, {} seconds per task".format(NoTasks,time.time() - start_time,(time.time() - start_time)/NoTasks))

threads=[]
start_time = time.time()
for i in range(NoTasks):
    x = threading.Thread(target=elemntwiseoperations,name=''.format(i),args=(a,b))
    x.start()
    threads.append(x)
    
for process in threads:
    process.join()

print("Execution time for {} tasks: {} seconds, {} seconds per task".format(NoTasks,time.time() - start_time,(time.time() - start_time)/NoTasks))

输出:

Execution time for 32 tasks: 0.5654711723327637 seconds, 0.01767103374004364 seconds per task
Execution time for 32 tasks: 0.17153215408325195 seconds, 0.005360409617424011 seconds per task

P.S。 MAC os, python 3.7.6, Cpython 实现.

因此,我目前的最佳猜测如下: 在第一种情况下,一个线程顺序启动 C 例程。在开始新的之前,它会等待每个完成。由于我只使用了numpy中没有并行化的elementwise操作,所以整个过程只涉及一个线程。

在第二种情况下,我调用了 32 个虚拟线程,每个都受 GIL 影响。第一个线程启动 C 例程并将 GIL 控制权交给第二个线程,然后第二个线程启动 C 例程并将控制权交给第三个线程,依此类推。即使不是同时调用 C 例程,它们也会同时执行,因为 C 不受 GIL 影响。

我不知道如何实际检查它,但在阅读了几篇关于 GIL 的 python 博客后,我是这样理解的。