首頁 > 後端開發 > 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
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板