感受没有GIL锁的Python3
GIL全局解释器锁
GIL(Global Interpreter Lock)保证在CPython进程中,只有一个线程执行字节码,甚至在多核CPU的情况下,也是只能允许一个CPU上的一个线程在运行。
由于GIL锁的存在,Python3的多线程不适合CPU密集型的工作。GIL使得部署Python AI模型变得困难。GIL导致无法充分利用GPU资源。
目前PEP 703(python增强建议书)中,针对GIL对科学计算造成的问题做了详细的描述,有兴趣的可以去看看:https://peps.python.org/pep-0703/。

目前社区中对于移除GIL锁的呼声也越来越高了,也不乏有大神将GIL锁给移除了,下面就根据大神的没有GIL锁的Pyhton来感受一下它的性能。为了有对比,先来有GIL锁的通用python版本运行多线程看看性能。
设备配置:
操作系统:CentOS 7
CPU:Intel(R) Xeon(R) E-2224 CPU @ 3.40GHz 4核
内存:32G
通用代码:CPU密集型多线程代码
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@文件 :threading-test.py
@说明 : 多线程CPU密集型测试。
@时间 :2022/11/22 11:12:04
@作者 :zhide.zhang
'''
import threading
import logging
import datetime
from threading import Event, Semaphore, Barrier
FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
logging.basicConfig(format=FORMAT,level=logging.INFO)
start = datetime.datetime.now()
# 计算 多线程执行
def calc():
sum = 0
for _ in range(1000000000):
sum += 1
ts = []
for _ in range(5):
t = threading.Thread(target=calc)
ts.append(t)
t.start()
for i in ts:
i.join()
delta = (datetime.datetime.now()-start).total_seconds()
print(delta)
1、有GIL锁的python性能展示
Python版本:3.8.9

执行结果:最终运行178s。

根据上述运行图可知,由于GIL锁的存在,5个线程在4个核心之间来回切换,使得CPU负载只能到100。
2、没有GIL锁的python性能展示
项目地址:https://github.com/colesbury/nogil
通过启用docker来展示。代码还用一样的代码。设备还是一样的设备。
2.1、下载docker镜像
docker pull nogil/python
2.2、运行docker镜像
docker run -it --rm nogil/python
2.3、进入镜像,并运行代码

docker exec -it 51d256330bed /bin/bash # 进入容器
由于镜像中没有vi和vim,用scp将代码从物理机copy到容器中。
scp root@192.168.90.111:/root/threading-test.py ./
root@51d256330bed:/# python3 threading-test.py

执行结果:最终运行81s。

根据上述运行图可知,由于没有GIL锁的存在,5个线程同时执行,将4个核心直接拉满,使得CPU负载一度达到了400。同样的代码,在有GIL锁和没有GIL锁的环境下,性能直接提升了1倍。
结语:
GIL锁的加入,是为了Python多线程的线程安全,就目前来看,在没有官方出品前,还是要慎用没有GIL锁的Python。本人在这之前也测试过Python 3.12.0a4的版本,(被国内某些标题党误导的,比如:"Python-3.12 告别 GIL 锁 & 性能原地飞升!"),该版本目前属于开发版,根据我用上面的代码实测,Python 3.12对多线程的优化还是有的,同一台设备,同样的代码,不同的python环境,Python 3.12的多线程也能比Python3.8.16的多线程性能快1倍。但是Python 3.12仍然有GIL锁,多线程代码运行过程中多核心仍然达不到100的使用率。总共也只有100。