一文弄懂 Gunicorn 与 Python GIL
什么是 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 不是问题。
以上是一文弄懂 Gunicorn 与 Python GIL的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

PHP和Python各有优劣,选择取决于项目需求和个人偏好。1.PHP适合快速开发和维护大型Web应用。2.Python在数据科学和机器学习领域占据主导地位。

Docker利用Linux内核特性,提供高效、隔离的应用运行环境。其工作原理如下:1. 镜像作为只读模板,包含运行应用所需的一切;2. 联合文件系统(UnionFS)层叠多个文件系统,只存储差异部分,节省空间并加快速度;3. 守护进程管理镜像和容器,客户端用于交互;4. Namespaces和cgroups实现容器隔离和资源限制;5. 多种网络模式支持容器互联。理解这些核心概念,才能更好地利用Docker。

在CentOS系统上高效训练PyTorch模型,需要分步骤进行,本文将提供详细指南。一、环境准备:Python及依赖项安装:CentOS系统通常预装Python,但版本可能较旧。建议使用yum或dnf安装Python3并升级pip:sudoyumupdatepython3(或sudodnfupdatepython3),pip3install--upgradepip。CUDA与cuDNN(GPU加速):如果使用NVIDIAGPU,需安装CUDATool

在CentOS系统上启用PyTorchGPU加速,需要安装CUDA、cuDNN以及PyTorch的GPU版本。以下步骤将引导您完成这一过程:CUDA和cuDNN安装确定CUDA版本兼容性:使用nvidia-smi命令查看您的NVIDIA显卡支持的CUDA版本。例如,您的MX450显卡可能支持CUDA11.1或更高版本。下载并安装CUDAToolkit:访问NVIDIACUDAToolkit官网,根据您显卡支持的最高CUDA版本下载并安装相应的版本。安装cuDNN库:前

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

在CentOS下选择PyTorch版本时,需要考虑以下几个关键因素:1.CUDA版本兼容性GPU支持:如果你有NVIDIAGPU并且希望利用GPU加速,需要选择支持相应CUDA版本的PyTorch。可以通过运行nvidia-smi命令查看你的显卡支持的CUDA版本。CPU版本:如果没有GPU或不想使用GPU,可以选择CPU版本的PyTorch。2.Python版本PyTorch

CentOS 安装 Nginx 需要遵循以下步骤:安装依赖包,如开发工具、pcre-devel 和 openssl-devel。下载 Nginx 源码包,解压后编译安装,并指定安装路径为 /usr/local/nginx。创建 Nginx 用户和用户组,并设置权限。修改配置文件 nginx.conf,配置监听端口和域名/IP 地址。启动 Nginx 服务。需要注意常见的错误,如依赖问题、端口冲突和配置文件错误。性能优化需要根据具体情况调整,如开启缓存和调整 worker 进程数量。

在CentOS系统上高效处理PyTorch数据,需要以下步骤:依赖安装:首先更新系统并安装Python3和pip:sudoyumupdate-ysudoyuminstallpython3-ysudoyuminstallpython3-pip-y然后,根据您的CentOS版本和GPU型号,从NVIDIA官网下载并安装CUDAToolkit和cuDNN。虚拟环境配置(推荐):使用conda创建并激活一个新的虚拟环境,例如:condacreate-n
