Python 的 gil 到底解决了什么具体的问题?

2021-05-17 23:52:52 +08:00
 LeeReamond
如题,用了这么多年 py,Gil 按理说应该很熟悉了,但是今天被问到 Gil 具体锁了哪些东西的时候却被问住了。毕竟虽然引入了 gil 机制,但 py 中的线程争用资源由于原子性问题仍然需要用户自行上锁,细究的话很多文章讲的加入 Gil 后避免细粒度锁其实是不对的,因为用户层面并没有实现无锁感知。

具体来说,例如放两个线程同时在各自负责的内存空间操作完全不相干的对象时(比如双线程同时计算质数,各自维护各自的资源),那么按照大多数语言的思路,由于没有资源争用,实际上并不需要加锁(反之,如果有争用则必须加锁,目前 py 中也是这么干的),如果这么考量的话,Gil 所谓的有锁线程才允许解释,又解决了什么问题呢,完全没必要不是么
5984 次点击
所在节点    Python
50 条回复
neoblackcap
2021-05-19 20:55:48 +08:00
@ipwx 讨论 VM 是没有意义的,因为 tracing gc 跟 VM 的没有什么关系。tracing GC 只要有运行时提供功能就可以。而且 CPython 是有 tracing gc 兜底的,只不过不是通用的方式罢了。
leven87
2021-05-19 21:16:28 +08:00
来学习下各位大佬的思路
ipwx
2021-05-19 23:33:44 +08:00
@sagaxu 呃,我和你理解的 CPU 密集型是不是有点不一样。。。CPU 密集型,我理解就该多少核开多少线程,工作全部入队列,一个结果计算好就发出去。。。这种专门后台大量计算的场景吗? GIL 在 CPU 密集型,直接开多进程不就可以了。。。。

好吧我理解你的意思了,这种情况下你还想用多线程那 python 就没辙了。但是话说回来,这种场景多进程超好写啊。反而是 IO 密集型,当你的 IO 再上一个台阶,要用到多核多线程 + 每个线程 event loop,那搁多进程反而就有开销了。。。


@LeeReamond 对,我个人觉得,这是 GIL 非常重要的一点,对 C/C++ 扩展至关重要。其他什么引用计数、Python 内部解释器的各种乱七八糟问题,我觉着只要钱到位肯定都能给你搞出来。只有这一点,真的是牵扯到扔掉半壁江山的事情了。。。为了兼容性可能 python 会很有顾虑。你看 pypy 不是很多 C 扩展就不能用吗。


@neoblackcap ummm 我说的 vm 是相对于 c/c++ 原生代码的。你看 python byte code 不也是一种 vm 吗,有了这个 vm,python 才能做 tracing gc 啊。对 c/c++ native code,python 是真的 tracing 不起来啊。
sagaxu
2021-05-20 00:39:06 +08:00
@ipwx 多进程 ipc 要序列化反序列化,不能简单的共享数据结构,进程之间同步也比线程之间更麻烦。多线程多个 event loop 跑 IO,如果 CPU 占用率超过 50%,按照分类标准,其实已经算 CPU 密集型了。IO 密集型通常 CPU 占用率远低于 50%,线程的大部分时间在等待 IO 完成,简单堆线程就能提高性能瓶颈。

一个典型的例子就是 tomcat 跑 java web,http connector 的 acceptorThreadCount 默认是 1,而 request processing threads 的 maxThreads 默认 200,后者是 IO 密集型,线程数往往设置为 CPU 线程数的几十上百倍。假如 Java 也有 GIL,他会造成 connector 提高线程数反而降低性能,但对 request processing threads 影响却不大了,开 100 个性能比只开 1 个要好的多。
ysc3839
2021-05-20 10:52:53 +08:00
@ipwx
atomic 要硬件支持吧?我不知道当年选择 GIL 的背景是什么,但我觉得在当时可能考虑到许多硬件不支持 atomic 操作,而大部分操作系统都提供了锁。
neoblackcap
2021-05-20 11:56:59 +08:00
@ipwx 谈外部代码就过了,什么 VM 都对堆外内存无能为力。只能靠插件自我维护。实际上搞成 Java 那样是挺好的,JIT 性能提上去,C 扩展完全可以不用。不过都是当年遗留下来的产物,要 Python 抛弃 C 扩展,基本上是要它去死了
ipwx
2021-05-20 15:30:35 +08:00
@sagaxu All right,你说的大概是一堆用户要请求服务器的场景吧。Okay Okay,可以算 CPU 密集型。

我脑子里面 CPU 密集型的第一反应是跑一个 PageRank,跑一个 CNN 之类的……那点 IO 开销和模型本身比简直可以忽略不计,多进程是最好的选项。因而,Python GIL 在这种任务面前根本不是事情。

所以到底为啥人闲的蛋疼要用 Python 做 CPU 密集型的 Web 服务器啊,这不是找虐么。多学一门语言和生态不就好了。。。

@neoblackcap 不过份。参考上面这段,我认为 Python 在 CPU 密集型的 Web 服务器领域就毫无价值,而在科学计算 / 机器学习 / 数据处理方面才有价值。因此外部代码才是 Python 的重点,用 FastAPI 直接快速上线 PyTorch 模型或者包装一下手写的 C++ 模型,不香吗?在 C++ 里面写个 RestAPI 那是找虐啊!

所以同样的,优化掉 GIL 去适应 Web Application,为啥要找这方面虐呢?老老实实学习 Java / Go 不好吗?
ipwx
2021-05-20 15:32:14 +08:00
@neoblackcap 所以 “搞成 Java 那样是挺好的,JIT 性能提上去,C 扩展完全可以不用” 那就不是 Python 了。Python 最大的价值就在于搞成现在这样,可以无缝衔接各种 C/C++。高兴了我就把算法用 C++ 写一下,pybind11 直接嵌入 FastAPI 多好。。。
hxysnail
2021-05-21 09:56:56 +08:00
GIL 目的在于保护 Python 虚拟机内部状态。举个例子,Python 很多变量空间,比如全局变量,内部是用 dict 来实现的。
变量的赋值,在 Python 内部最终是执行 STORE_NAME 字节码,这个字节码将变量的值,保存到对应的 dict 对象中。
假设这个动作底层是由字典的 dict.set(name, value)函数负责,它会非常复杂,还涉及 dict 对象扩容缩容,肯定不是线程安全的。

那怎么办呢?①dict.set(name, value)加锁;②用 GIL 保证同一时间只有一个线程在执行字节码。
Python 选择②,因为①引入的线程开销也不小。
有测试表明①虽然提升了 Python 的并行能力,但获得的性能提升非常有限,单线程下则全是消耗。

那为什么有 GIL 之后,多线程应用还需要加锁呢?
举个例子,有个全局变量 a,多个进程并发执行 a += 1 。
这个语句编译后大概会生成这样几个字节码:
1 LOAD_NAME 将变量从名字空间 dict 中取出,并保存在临时栈;
2 ADD 在临时栈中做加法操作;
3 STORE_NAME 将计算结果保存到名字空间 dict 中;
GIL 保证了线程在执行一个字节码时,其他线程不能执行,以便保护名字空间 dict 的安全性。

但这 3 个字节码之间可以任意切换,这样应用就会产生中间态。
举个例子,线程①执行 LOAD_NAME 后,切到其他线程执行,变量 a 发生了修改。
线程①恢复执行后,它临时栈中的值仍是旧的,这样就会覆盖了其他线程的写入。
因此,需要用户自行加锁,保存 a += 1 对应的这几个字节码的原子性,一次性执行,中间不能被打断。

总而言之,GIL 保证一个线程在执行字节码时,其他线程不能同时执行,目的是保护虚拟机内部状态的线程安全性;
用户自己加的锁,是为了让多条字节码成为一个原子操作,中间不会发生线程切换,目的在于保护程序逻辑的正确性,消除竞争态。

想要完全理解这个问题,需要了解 Python [内建对象] [虚拟机] [字节码] 等知识,有兴趣的话推荐看一个叫 [Python 源码剖析] 的专栏:
https://fasionchan.com/python-source/virtual-machine/gil/
BrightLiao
2022-07-14 09:25:05 +08:00
Python 的 GIL 带来的主要影响是多线程执行效率低。
如何在实际应用中提升 python 程序的性能呢?之前做过一些尝试,总结起来,当我们想要并行加速多个计算密集型任务时,主要思路有两个:
- 用 c 语言实现,显示的释放 GIL ,之后就可以利用线程加速了
- 改用多进程来加速,避免了 GIL 问题

详细的例子,可以参考我之前的一篇总结文章: https://brightliao.com/2019/07/25/ways-to-improve-python-perf/

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/777560

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX