Python telah lama dikenali kerana kemudahan penggunaan dan serba boleh, tetapi satu topik yang telah mencetuskan banyak perbincangan dalam komuniti Python ialah Global Interpreter Lock (GIL). GIL telah menjadi pelindung dan halangan untuk model konkurensi Python, terutamanya untuk tugas terikat CPU yang sebaliknya boleh mengambil kesempatan daripada berbilang teras CPU. Walau bagaimanapun, dengan keluaran Python 3.13, pembangun Python mempunyai pilihan baharu yang hebat: keupayaan untuk melumpuhkan GIL. Blog ini akan meneroka apa itu GIL, mengapa ia menjadi penghalang untuk prestasi dalam multithreading, dan cara untuk mengesan dan melumpuhkan GIL dalam Python 3.13 untuk membuka kunci prestasi multithreading yang sebenar.
Global Interpreter Lock (GIL) ialah mutex yang melindungi akses kepada objek Python, menghalang berbilang benang asli daripada melaksanakan kod bait Python sekaligus. Ini memastikan keselamatan benang untuk program Python tetapi dengan kos pelaksanaan serentak. GIL menjadikan benang Python lebih cekap untuk tugas terikat I/O tetapi mengehadkan prestasinya untuk tugas terikat CPU.
GIL Python membenarkan hanya satu utas untuk dilaksanakan secara serentak, walaupun dalam program berbilang benang. Walaupun ini baik untuk tugas terikat I/O di mana program sedang menunggu operasi input/output, ia sangat mengehadkan prestasi untuk tugas terikat CPU seperti pecah nombor, analisis data atau pemprosesan imej.
Dengan Python 3.13, pembangun mempunyai pilihan untuk melumpuhkan GIL semasa proses binaan Python. Walau bagaimanapun, melumpuhkan GIL tidak tersedia dalam pengedaran Python pra-bina. Sebaliknya, anda mesti menyusun Python 3.13 daripada sumber dengan pilihan --disable-gil.
Pilihan baharu ini membuka pintu untuk keselarian sebenar dalam tugas berbilang benang terikat CPU, membenarkan urutan dilaksanakan secara selari merentas berbilang teras.
Untuk melumpuhkan GIL menggunakan bendera -X gil=0, anda perlu menyusun Python daripada sumber dengan bendera --disable-gil didayakan. Begini caranya
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
./configure --disable-gil
make sudo make altinstall
./configure --disable-gil --prefix=$HOME/python3.13
make altinstall
Dalam Python 3.13, anda boleh menyemak sama ada GIL didayakan atau dilumpuhkan menggunakan fungsi sys._is_gil_enabled().
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()
Kod Python berikut telah dibangunkan untuk menilai keuntungan prestasi apabila melumpuhkan GIL dalam Python 3.13. Skrip melaksanakan lapan utas serentak, setiap satu ditugaskan untuk mengira faktor perdana nombor besar. Dengan memanfaatkan keselarian sebenar, kod tersebut menyerlahkan prestasi dipertingkat yang dicapai tanpa 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.
Prestasi berbilang benang: Faedah sebenar melumpuhkan GIL terbukti dalam senario berbilang benang:
Dengan GIL dilumpuhkan (3.13), masa pelaksanaan ialah 1.5703 saat.
Dengan GIL didayakan (3.13), masa pelaksanaan ialah 8.5901 saat.
Keputusan: Tindakan melumpuhkan GIL menghasilkan peningkatan prestasi kira-kira 81.7% untuk tugasan berbilang benang.
Carta jelas menunjukkan bahawa melumpuhkan GIL dalam Python 3.13 membawa kepada peningkatan prestasi yang ketara untuk tugasan terikat CPU berbilang benang, membolehkan Python menggunakan berbilang teras CPU secara selari dengan cekap. Walaupun prestasi satu-benang dan berbilang pemprosesan sebahagian besarnya kekal tidak terjejas, prestasi berbilang-benang menunjukkan peningkatan yang ketara, menjadikan Python 3.13 sebagai penukar permainan untuk aplikasi intensif CPU yang bergantung pada berbilang-benang.
Walau bagaimanapun, versi Python sebelum 3.13 tidak menyokong pelumpuhan GIL, yang menjelaskan mengapa prestasi berbilang benang mereka kekal serupa dengan Python 3.13 dengan GIL didayakan. Had ini dalam versi terdahulu terus menyekat keupayaan Python untuk mengeksploitasi sepenuhnya berbilang benang untuk tugas terikat CPU.
Melumpuhkan Global Interpreter Lock (GIL) dalam Python 3.13 boleh membuka kunci peningkatan prestasi yang ketara dalam tugasan terikat CPU berbilang benang. Walau bagaimanapun, terdapat beberapa faktor penting yang perlu dipertimbangkan sebelum berbuat demikian:
Keselamatan Benang: Tanpa GIL, anda mesti mengendalikan keselamatan benang secara manual menggunakan kunci atau mekanisme penyegerakan lain untuk mengelakkan keadaan perlumbaan dalam kod anda.
Potensi Kemerosotan Prestasi: Penguncian berbutir halus boleh menimbulkan perbalahan, yang mungkin merendahkan prestasi dalam tugasan berbenang tunggal atau terikat I/O yang sebelum ini mendapat manfaat daripada GIL.
Keserasian dengan Perpustakaan Pihak Ketiga: Banyak sambungan dan perpustakaan C menganggap kehadiran GIL untuk keselamatan benang. Melumpuhkan GIL mungkin memerlukan kemas kini pada perpustakaan ini untuk memastikan ia berfungsi dengan betul dalam persekitaran berbilang benang.
Pengurusan Memori Kompleks: Melumpuhkan GIL memperkenalkan lebih banyak kerumitan dalam pengurusan memori, memerlukan pengendalian memori selamat benang yang boleh meningkatkan risiko pepijat dan ralat.
Tugas terikat I/O: Melumpuhkan GIL memberikan faedah terhad untuk tugas terikat I/O, di mana mekanisme I/O tidak menyekat seperti asyncio mungkin lebih berkesan.
Kesukaran dalam Menyahpepijat: Tanpa GIL, penyahpepijatan aplikasi berbilang benang boleh menjadi lebih mencabar disebabkan peningkatan kemungkinan keadaan perlumbaan dan kebuntuan.
Penggunaan Memori Lebih Tinggi: Menggunakan kunci dan mengurus keadaan benang tanpa GIL boleh meningkatkan penggunaan memori, terutamanya dalam aplikasi berbilang benang.
Sistem Terbenam: Melumpuhkan GIL mungkin merumitkan penyepaduan Python dengan persekitaran berbilang benang dalam sistem terbenam, memerlukan lebih banyak usaha untuk penyepaduan yang berkesan.
Lock Contention: Dalam sesetengah kes, melumpuhkan GIL boleh membawa kepada mengunci pertikaian antara thread, yang mungkin mengurangkan peningkatan prestasi yang dijangkakan.
Anda boleh mendapatkan kod sumber lengkap untuk contoh dalam blog ini di GitHub saya:
Analisis Prestasi Python GIL
Ini adalah blog peribadi. Pandangan dan pendapat yang dinyatakan di sini hanyalah pendapat pengarang dan tidak mewakili mana-mana organisasi atau mana-mana individu yang mungkin dikaitkan dengan pengarang, secara profesional atau peribadi.
Atas ialah kandungan terperinci Python Gerbang kepada Multithreading Berprestasi Tinggi Tanpa GIL. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!