Redian新闻
>
Gunicorn 与 Python GIL

Gunicorn 与 Python GIL

科技


新钛云服已累计为您分享684篇技术干货



什么是 Python GIL,它是如何工作的,以及它如何影响 gunicorn。
生产环境我应该选择哪种 Gunicorn worker类型?
Python 有一个全局锁 (GIL),它只允许一个线程运行(即解释字节码)。在我看来,如果你想优化你的 Python 服务,理解 Python 如何处理并发是必不可少的。
Python 和 gunicorn 为您提供了处理并发的不同方法,并且由于没有涵盖所有用例的灵丹妙药,因此最好了解每个选项的选项、权衡和优势。
Gunicorn worker类型
Gunicorn 以“workers types”的概念公开了这些不同的选项。每种类型都适用于一组特定的用例。

· sync——将进程分叉成 N 个并行运行的进程来处理请求

· gthread——产生 N 个线程来并发服务请求

· eventlet/gevent——产生绿色线程来并发服务请求

Gunicorn sync worker

这是最简单的工作类型,其中唯一的并发选项是分叉N个进程,它们将并行地服务请求。
它们可以很好地工作,但会招致大量开销(例如内存和CPU上下文切换),而且如果您的大部分请求时间都在等待I/O,那么伸缩性就不好。

Gunicorn gthread worker

gthread worker 通过允许您为每个进程创建 N 个线程来改进这一点。这提高了 I/O 性能,因为您可以同时运行更多代码实例。这是受 GIL 影响的四个中唯一一个。

Gunicorn eventlet and gevent workers

eventlet/gevent workers试图通过运行轻量级用户线程(又名绿色线程、greenlets 等)来进一步改进 gthread 模型。
与系统线程相比,这允许您以很少的成本拥有数千个所述的greenlet。 另一个区别是它遵循协作工作模型而不是抢占式,允许不间断工作,直到它们阻塞为止。我们将首先分析 gthread 工作线程在处理请求时的行为以及它如何受 GIL 影响。
与每个请求直接由一个进程提供服务的sync不同,使用 gthread,每个进程都有 N 个线程,以便更好地扩展,而无需产生多个进程的开销。由于您在同一个进程中运行多个线程,GIL 将阻止它们并行运行。
GIL 不是进程或特殊线程。它只是一个布尔变量,其访问受互斥锁保护,用于确保每个进程内只有一个线程在运行。
它的工作方式可以在上图中看到。在这个例子中,我们可以看到我们有 2 个系统线程并发运行,每个线程处理 1 个请求。流程是这样的:
- 线程 A 持有 GIL 开始服务请求
- 过了一会儿,线程 B 尝试提供请求,但无法持有 GIL。
- B 设置超时以强制释放 GIL,如果在达到超时之前不会发生这种情况
- A 在达到超时之前不会释放 GIL。
- B 设置 gil_drop_request 标志以强制 A 立即释放 GIL。
- A 释放 GIL 并将等待直到另一个线程抓取 GIL,以避免出现 A 会不断释放并抓取 GIL 而其他线程无法抓取它的情况。
- B 开始运行
- B 在阻塞 I/O 的同时释放 GIL
- A 开始运行
- B 尝试再次运行但被暂停
- A 在达到超时之前完成
- B 运行完毕

相同的场景,但使用 gevent

在不使用进程的情况下增加并发性的另一个选择是使用 greenlets。该worker产生“用户线程”而不是“系统线程”以增加并发性。
尽管这意味着它们不受 GIL 的影响,但这也意味着您仍然无法增加并行度,因为它们无法由 CPU 并行调度。

- Greenlet A将开始运行,直到发生I/O事件或执行完毕

- Greenlet B将等待直到Greenlet A释放事件循环

- A结束

- B开始

- B释放事件循环以等待I/O

- B完成

对于这种情况,很明显,拥有一个 greenlet 类型的worker并不理想。我们最终让第二个请求等到第一个请求完成,然后再次空闲等待 I/O。
在这些场景中,greenlet 协作模型真的很出色,因为您不会在上下文切换上浪费时间并避免运行多个系统线程的开销。我们将在本文末尾的基准测试中见证这一点。
现在,这引出了以下问题:

· 更改线程上下文切换超时是否会影响服务延迟和吞吐量?

· 当您混合使用 I/O 和 CPU 工作时,如何在 gevent/eventlet 和 gthread 之间进行选择

· 如何使用 gthread worker 选择线程数

· 我应该只使用sync worker并增加分叉进程的数量来避免 GIL 吗?


要回答这些问题,您需要进行监控以收集必要的指标,然后针对这些相同的指标运行量身定制的基准测试。运行与您的实际使用模式零相关性的综合基准测试是没有用的 下图显示了不同场景的延迟和吞吐量指标,让您了解这一切是如何协同工作的。
对 GIL 切换间隔进行基准测试
在这里我们可以看到更改 GIL 线程切换间隔/超时如何影响请求延迟。正如预期的那样,IO 延迟随着切换间隔的降低而变得更好。发生这种情况是因为受 CPU 限制的线程被迫更频繁地释放 GIL 并允许其他线程完成它们的工作。
但这不是灵丹妙药。减少切换间隔将使 CPU 绑定线程需要更长的时间才能完成。我们还可以看到总延迟增加,由于恒定线程切换的开销增加,超时时间减少。如果您想自己尝试,可以使用以下代码更改切换间隔:

使用 CPU 绑定请求对 gthread 与 gevent 延迟进行基准测试
总的来说,我们可以看到基准测试反映了我们之前对 GIL 绑定线程和 greenlet 如何工作的分析所产生的直觉。
由于切换间隔迫使长时间运行的线程释放,gthread 对于 IO 绑定请求具有更好的平均延迟
gevent CPU 绑定请求比 gthread 具有更好的延迟,因为它们不会被中断以服务其他请求
使用 CPU 绑定请求对 gthread 与 gevent 吞吐量进行基准测试

这里的结果也反映了我们之前对 gevent 比 gthread 具有更好吞吐量的直觉。这些基准高度依赖于完成的工作类型,不一定直接转化为您的用例。
这些基准测试的主要目标是为您提供一些有关测试和测量内容的指南,以便最大限度地提高将服务于请求的每个 CPU 内核。
由于所有 gunicorn worker 都允许您指定将运行的进程数,因此更改的是每个进程如何处理并发连接。因此,请确保使用相同数量的worker以使测试公平。现在让我们尝试使用从我们的基准测试中收集的数据来回答前面的问题。
更改线程上下文切换超时是否会影响服务延迟和吞吐量? 
确实如此。然而,对于绝大多数工作负载来说,它并没有改变游戏规则。
当您混合使用 I/O 和 CPU 工作时,如何在 gevent/eventlet 和 gthread 之间进行选择? 正如我们所看到的,当您有更多 CPU 密集型工作时,ghtread 往往允许更好的并发性。
如何选择gthread worker的线程数?
只要您的基准测试能够模拟类似生产的行为,您就会清楚地看到峰值性能,然后它会因线程过多而开始下降。
我应该只使用同步工作者并增加分叉进程的数量来避免 GIL 吗?
除非您的 I/O 几乎为零,否则仅使用进程进行扩展并不是最佳选择。
结论
Coroutines/Greenlets 可以提高 CPU 效率,因为它们避免了线程之间的中断和上下文切换。协程用延迟换取吞吐量。
如果您混合使用 IO 和 CPU 绑定端点,协程可能会导致更难以预测的延迟——CPU 绑定端点不会被中断以服务其他传入请求。如果您花时间正确配置 gunicorn,GIL 不是问题。
原文链接:https://louispetrik.medium.com/pypy-vs-python-49153daca65c

    推荐阅读   





    推荐视频    


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
10 个 Python 脚本来自动化你的日常任务Python之谜:四舍五入round(4.5)等于4?33 个 "不得不看" 的 Python 关键字总结!官方发布!最适合留学生快速上手的python教程来了涨知识!Python 的异常信息还能这样展现硬核观察 #739 Python 虽然是最受欢迎的编程语言,但是找工作还是要会点 SQL看漫画就能学会?最适合留学生快速上手的Python教程来了!Python环境搭建手把手图文教程资深吃货必须了解的14种糖这几年学 Python 的感悟Python证书的含金量高吗?胡渊鸣:import 一个“太极”库,让 Python 代码提速100倍!关于 Python 的文件操作详解MNE/Python-fNIRS近红外数据处理中文手册《天才基本法》完结!张子枫学Python的样子,像极了出国后的我自己...粉蝶将你的 Python 脚本转换为命令行程序 | Linux 中国如何为 Python 应用选择最好的 Docker 镜像?Julia 和 Python,哪一个更快? | Linux 中国字节大佬编写的这本《Python背记手册》,带我横扫互联网大厂秋招!扑通扑通 两次扑街10个 Python 脚本来自动化你的日常任务用 Python 写了一个电子考勤系统!Python中常见魔法方法介绍资本下有脱离政治的文学艺术吗《天才基本法》揭秘Python真实用法,留学生直呼“上当了”修复 Ubuntu Linux 中 “Command ‘python’ not found” 的错误 | Linux 中国火爆北美的少儿Python编程课免费领!藤校师资,竞赛AP两手抓!10个Python脚本来自动化你的日常任务Python 中可观测性的 7 个关键部分 | Linux 中国4 步打包一个新的 Python 模块 | Linux 中国硬核观察 #800 Python 3.11 发布:性能大幅提升最新网络禁忌词:未来五年用 Python 测试 API 的 3 种方式 | Linux 中国Python程序化套利实战班
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。