首页 > 后端开发 > Python教程 > Python 无需 GIL 即可实现高性能多线程的门户

Python 无需 GIL 即可实现高性能多线程的门户

Mary-Kate Olsen
发布: 2025-01-06 16:58:41
原创
657 人浏览过

介绍

Python 长期以来以其易用性和多功能性而闻名,但在 Python 社区中引发大量讨论的一个话题是全局解释器锁 (GIL)。 GIL 既是 Python 并发模型的保障,也是瓶颈,尤其是对于 CPU 密集型任务,否则这些任务可以利用多个 CPU 核心。然而,随着 Python 3.13 的发布,Python 开发人员有了一个突破性的新选项:禁用 GIL 的能力。本博客将探讨 GIL 是什么、为什么它成为多线程性能的障碍,以及如何在 Python 3.13 中检测和禁用 GIL 以释放真正的多线程性能。

什么是全局解释器锁(GIL)

全局解释器锁 (GIL) 是一个互斥锁,用于保护对 Python 对象的访问,防止多个本机线程同时执行 Python 字节码。这保证了Python程序的线程安全,但代价是并发执行。 GIL 使 Python 线程对于 I/O 密集型任务更加高效,但限制了它们对于 CPU 密集型任务的性能。

为什么 GIL 是多线程的瓶颈

Python 的 GIL 只允许一个线程同时执行,即使在多线程程序中也是如此。虽然这对于程序等待输入/输出操作的 I/O 密集型任务来说很好,但它严重限制了 CPU 密集型任务(如数字运算、数据分析或图像处理)的性能。

Python 3.13:在禁用 GIL 的情况下解锁多线程

使用 Python 3.13,开发人员可以选择在 Python 构建过程中禁用 GIL。但是,在预构建的 Python 发行版中无法禁用 GIL。相反,您必须使用 --disable-gil 选项从源代码编译 Python 3.13。

这个新选项为 CPU 密集型多线程任务中的真正并行性打开了大门,允许线程跨多个内核并行执行。

使用不带 GIL 的 Python 3.13 的先决条件

  • Python 3.13 源代码: 标准预构建二进制文件中不支持禁用 GIL。您必须使用 --disable-gil 标志从源代码构建 Python 3.13。
  • 多核 CPU: 您需要多核 CPU 才能从真正的多线程中受益,因为线程现在将跨多个内核并行运行。

在禁用 GIL 的情况下编译 Python 3.13

要使用 -X gil=0 标志禁用 GIL,您需要在启用 --disable-gil 标志的情况下从源代码编译 Python。具体方法如下

一步一步

  • 下载Python 3.13源代码 您首先需要从 Python 官方网站下载 Python 3.13 源代码 tarball。这是因为预构建的二进制文件(例如直接从 python.org 下载的二进制文件)未编译为支持禁用 GIL。您可以使用网络浏览器或使用 wget 甚至在终端中使用 curl 下载它
wget https://www.python.org/ftp/python/3.13.0/Python-3.13.0.tgz
登录后复制
  • 提取来源:
tar -xf Python-3.13.0.tgz
cd Python-3.13.0
登录后复制
  • 使用 --disable-gil 配置构建 您需要使用 --disable-gil 配置 Python 以支持禁用 GIL 的选项。
./configure --disable-gil
登录后复制
  • 编译并安装Python:
make
sudo make altinstall 
登录后复制
  • 如果 altinstall 步骤失败,则使用 --prefix 重新运行配置命令
./configure --disable-gil --prefix=$HOME/python3.13
登录后复制
  • 在指定目录下运行make altinstall 然后,运行 make altinstall 命令
make altinstall
登录后复制

如何在 Python 3.13 中检测 GIL

在Python 3.13中,您可以使用sys._is_gil_enabled()函数检查GIL是否启用或禁用。

import sys

def check_gil_status():
    if sys.version_info >= (3, 13):
        status = sys._is_gil_enabled()
        if status:
            print("GIL is currently enabled.")
        else:
            print("GIL is currently disabled.")
    else:
        print("Python version does not support GIL status detection.")

check_gil_status()
登录后复制

实践:使用 GIL 的 Python 多线程与无 GIL 的比较

开发以下 Python 代码是为了评估在 Python 3.13 中禁用 GIL 时的性能增益。该脚本同时执行八个线程,每个线程的任务是计算大数的素因数。通过利用真正的并行性,代码突出了无需 GIL 即可实现的增强性能。

#!/usr/bin/env python3
import sys
import sysconfig
import time
from threading import Thread
from multiprocessing import Process


# Decorator to measure execution time of functions
def calculate_execution_time(func):
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        execution_time = end_time - start_time
        print(f"{func.__name__} took {execution_time:.4f} seconds.")
        return result

    return wrapper


# Compute-intensive task: Iterative Fibonacci calculation
def compute_fibonacci(n):
    """Compute Fibonacci number for a given n iteratively."""
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a


# Single-threaded task execution
@calculate_execution_time
def run_single_threaded(nums):
    for num in nums:
        compute_fibonacci(num)


# Multi-threaded task execution
@calculate_execution_time
def run_multi_threaded(nums):
    threads = [Thread(target=compute_fibonacci, args=(num,)) for num in nums]
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()


# Multi-processing task execution
@calculate_execution_time
def run_multi_processing(nums):
    processes = [Process(target=compute_fibonacci, args=(num,)) for num in nums]
    for process in processes:
        process.start()
    for process in processes:
        process.join()


# Main execution function
def main():
    # Check Python version and GIL status for Python 3.13+
    print(f"Python Version: {sys.version}")

    py_version = float(".".join(sys.version.split()[0].split(".")[:2]))
    status = sysconfig.get_config_var("Py_GIL_DISABLED")

    if py_version >= 3.13:
        status = sys._is_gil_enabled()

    if status is None:
        print("GIL cannot be disabled for Python <= 3.12")
    elif status == 0:
        print("GIL is currently disabled")
    elif status == 1:
        print("GIL is currently active")

    # Run tasks on the same input size for comparison
    nums = [300000] * 8

    print("\nRunning Single-Threaded Task:")
    run_single_threaded(nums)

    print("\nRunning Multi-Threaded Task:")
    run_multi_threaded(nums)

    print("\nRunning Multi-Processing Task:")
    run_multi_processing(nums)


if __name__ == "__main__":
    main()

登录后复制

分析:

## Python 3.13 with GIL Disabled
Python Version: 3.13.0 experimental free-threading build (main, Oct 14 2024, 17:09:28) [Clang 14.0.0 (clang-1400.0.29.202)]
GIL is currently disabled

Running Single-Threaded Task:
run_single_threaded took 8.6587 seconds.

Running Multi-Threaded Task:
run_multi_threaded took 1.3885 seconds.

Running Multi-Processing Task:
run_multi_processing took 1.5953 seconds.

## Python 3.13 with GIL Enabled
Python Version: 3.13.0 experimental free-threading build (main, Oct 14 2024, 17:09:28) [Clang 14.0.0 (clang-1400.0.29.202)]
GIL is currently active

Running Single-Threaded Task:
run_single_threaded took 8.7108 seconds.

Running Multi-Threaded Task:
run_multi_threaded took 8.6645 seconds.

Running Multi-Processing Task:
run_multi_processing took 1.4530 seconds.

## Python 3.12 
Python Version: 3.12.6 (main, Sep  7 2024, 19:30:10) [Clang 14.0.0 (clang-1400.0.29.202)]
GIL cannot be disabled for Python <= 3.12

Running Single-Threaded Task:
run_single_threaded took 8.7004 seconds.

Running Multi-Threaded Task:
run_multi_threaded took 8.6297 seconds.

Running Multi-Processing Task:
run_multi_processing took 1.4876 seconds.
登录后复制

多线程性能:禁用 GIL 的真正好处在多线程场景中是显而易见的:

禁用 GIL (3.13) 时,执行时间为 1.5703 秒。
启用 GIL 后(3.13),执行时间为 8.5901 秒。
结果:禁用 GIL 使多线程任务的性能提高了约 81.7%。

Python The Gateway to High-Performance Multithreading Without GIL

Python The Gateway to High-Performance Multithreading Without GIL

该图表清楚地表明,在 Python 3.13 中禁用 GIL 可以显着提升多线程 CPU 密集型任务的性能,从而使 Python 能够高效地并行利用多个 CPU 核心。虽然单线程和多处理性能基本上不受影响,但多线程性能显示出显着改进,使 Python 3.13 成为依赖多线程的 CPU 密集型应用程序的游戏规则改变者。

但是,3.13之前的Python版本不支持禁用GIL,这也解释了为什么它们的多线程性能仍然与启用GIL的Python 3.13相似。早期版本中的这一限制继续限制 Python 充分利用多线程处理 CPU 密集型任务的能力。

禁用 GIL 之前的主要注意事项

在 Python 3.13 中禁用全局解释器锁 (GIL) 可以显着提高多线程 CPU 密集型任务的性能。但是,在此之前需要考虑几个重要因素:

  • 线程安全:如果没有 GIL,您必须使用锁或其他同步机制手动处理线程安全,以防止代码中的竞争条件。

  • 潜在的性能下降:细粒度锁定可能会引入争用,这可能会降低以前受益于 GIL 的单线程或 I/O 密集型任务的性能。

  • 与第三方库的兼容性:许多 C 扩展和库假设存在 GIL 以保证线程安全。禁用 GIL 可能需要更新这些库,以确保它们在多线程环境中正常工作。

  • 复杂的内存管理:禁用 GIL 会增加内存管理的复杂性,需要线程安全的内存处理,这会增加错误和错误的风险。

  • I/O 密集型任务: 禁用 GIL 为 I/O 密集型任务带来的好处有限,在这些任务中,像 asyncio 这样的非阻塞 I/O 机制可能更有效。

  • 调试困难:如果没有 GIL,由于竞争条件和死锁的可能性增加,调试多线程应用程序可能会变得更具挑战性。

  • 更高的内存使用量: 在没有 GIL 的情况下使用锁和管理线程状态会增加内存消耗,特别是在多线程应用程序中。

  • 嵌入式系统:禁用 GIL 可能会使 Python 与嵌入式系统中的多线程环境的集成变得复杂,需要付出更多努力才能有效集成。

  • 锁争用:在某些情况下,禁用 GIL 可能会导致线程之间出现锁争用,这可能会降低预期的性能改进。

GitHub 存储库

您可以在我的 GitHub 上找到此博客中示例的完整源代码:

Python GIL 性能分析

免责声明:

这是一个个人博客。本文表达的观点和意见仅代表作者的观点和意见,并不代表与作者有关​​联的任何组织或个人的专业或个人观点。

以上是Python 无需 GIL 即可实现高性能多线程的门户的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板