1 poin oleh GN⁺ 2024-03-12 | 1 komentar | Bagikan ke WhatsApp
  • CPython PR #116338 menggabungkan perubahan yang memungkinkan build free-threaded menonaktifkan GIL dengan PYTHON_GIL=0 atau -X gil=0 ke python:main
  • Untuk mempertahankan kemungkinan menyalakan kembali GIL saat runtime, struktur data terkait GIL tetap diinisialisasi seperti biasa, dan penonaktifan dilakukan dengan menyetel flag saat startup agar take_gil() dan drop_gil() langsung mengembalikan hasil lebih awal
  • Dalam pemeriksaan awal, dengan setelan PYTHON_GIL=0, beberapa pengujian dan program kecil yang tidak memakai thread berjalan normal, dan program thread yang sangat dasar kadang-kadang berjalan, tetapi seluruh test suite cepat crash di test_asyncio
  • Selama proses review, pengujian PYTHON_GIL, dokumentasi, opsi -X gil, dan refleksi ke sys.flags ditambahkan, serta penanganan setelan juga diperbaiki agar PYTHON_GIL=1 memaksa pengaktifan GIL
  • Pekerjaan lanjutan dipisahkan menjadi masalah menyalakan kembali GIL saat memuat ekstensi yang tidak kompatibel dan masalah menonaktifkan GIL secara default, dan perubahan ini menambahkan permukaan kontrol GIL pada build free-threaded Python 3.13

Perubahan yang digabungkan

  • CPython PR #116338 membahas perubahan gh-116167: Allow disabling the GIL with PYTHON_GIL=0 or -X gil=0
  • colesbury menggabungkannya ke python:main pada 11 Maret 2024
  • Skala perubahan ditandai sebagai 12 file, 163 baris ditambahkan, 1 baris dihapus
  • Fitur yang dituju adalah opsi eksekusi untuk menonaktifkan GIL pada build free-threaded, bukan build biasa

Cara menonaktifkan GIL

  • Pada build free-threaded, GIL dapat dinonaktifkan dengan setelan berikut
    • PYTHON_GIL=0
    • -X gil=0
  • Agar GIL bisa dinyalakan kembali saat runtime, semua struktur data terkait GIL diinisialisasi seperti biasa
  • Penonaktifan aktual dilakukan dengan cara menyetel flag saat startup
    • Karena flag ini, take_gil() dan drop_gil() langsung mengembalikan hasil lebih awal
  • Saat review, commit untuk menyetel enable_gil dengan benar ketika PYTHON_GIL=1 juga ditambahkan

Pengujian dan keterbatasan saat ini

  • Dengan setelan PYTHON_GIL=0, beberapa pengujian dan program kecil diperiksa
    • Pengujian dan program kecil yang tidak menggunakan thread dikonfirmasi berjalan normal
    • Program thread yang sangat dasar terkadang berjalan
  • Seluruh test suite cepat crash, dan lokasinya dicatat di test_asyncio
  • Dengan perintah !buildbot nogil, pengujian builder terkait NoGIL dijadwalkan beberapa kali
    • x86-64 MacOS Intel ASAN NoGIL PR
    • x86-64 MacOS Intel NoGIL PR
    • ARM64 MacOS M1 Refleaks NoGIL PR
    • ARM64 MacOS M1 NoGIL PR
    • AMD64 Ubuntu NoGIL Refleaks PR
    • AMD64 Ubuntu NoGIL PR
    • AMD64 Windows Server 2022 NoGIL PR

Cakupan yang ditambahkan selama review

  • corona10 menyarankan bahwa ada nilai dalam menambahkan pengujian variabel lingkungan ke Lib/test/test_cmd_line.py
  • Setelah itu commit berikut ditambahkan
    • Add test for PYTHON_GIL in test_cmd_line
    • Set enable_gil properly when PYTHON_GIL=1
    • Don't add 'enable_gil' to test_embed in normal builds
  • colesbury menilai bahwa dokumentasi sebaiknya ditambahkan saat variabel lingkungan diperkenalkan
    • Ia mendasarkan hal itu pada fakta bahwa flag configure --disable-gil sudah didokumentasikan
    • Dokumen perlu memuat bahwa ini hanya tersedia pada build free-threaded, 0 memaksa penonaktifan GIL, 1 memaksa pengaktifan GIL, dan bahwa ini adalah hal baru di Python 3.13
  • Setelah itu commit Document PYTHON_GIL environment variable ditambahkan

Penambahan opsi -X gil dan penggabungan akhir

  • Setelah diskusi di Discord, diputuskan untuk menambahkan opsi -X yang bisa dipakai bersama variabel lingkungan
  • Judul PR diubah dari bentuk yang hanya membahas PYTHON_GIL=0 menjadi mencakup PYTHON_GIL=0 or -X gil=0
  • Commit tambahan mencakup hal-hal berikut
    • Add -X gil option, add to sys.flags, modify test to cover env var… and option
    • Fix link to -X gil
    • Fix PYTHON_GIL versionchanged line
    • Clarify test_flags in normal builds
  • ericsnowcurrently, erlend-aasland, corona10, dan colesbury menyetujui perubahan ini
  • Commit merge adalah 2731913, dan setelah merge vstinner menanggapi perubahan ini sebagai “menarik dan sangat menakutkan”

Pekerjaan lanjutan

  • Sebagai isu lanjutan, dua pekerjaan dipisahkan
    • #116322: pekerjaan untuk mengaktifkan kembali GIL saat memuat ekstensi yang tidak kompatibel
    • #116329: pekerjaan untuk menonaktifkan GIL secara default
  • PR saat ini bukan perubahan default GIL, melainkan perubahan yang memungkinkan pengguna mengontrol status GIL pada build free-threaded melalui variabel lingkungan atau opsi -X

1 komentar

 
GN⁺ 2024-03-12
Komentar Hacker News
  • Untuk yang penasaran dengan pekerjaan no-GIL, saya tinggalkan tautan tambahan: [0], [1]
    [0] Multithreaded Python without the GIL
    https://docs.google.com/document/d/18CXhDb1ygxg-YXNBJNzfzZsD...
    [1] Repo Github
    https://github.com/colesbury/nogil

  • Saya menantikan seberapa jauh Python standar bisa dibuat lebih cepat. Terlalu banyak alat yang mencoba mengurangi masalah itu, sampai-sampai proposisi nilai Python sendiri ikut tertantang
    Untuk alat peningkat kecepatan, yang terlintas adalah Mojo, pytorch, triton, numba, taichi. Ada begitu banyak upaya untuk memecahkan masalah ini sehingga saat terakhir kali saya mencoba salah satunya, saya kewalahan karena pilihannya terlalu banyak. Akhirnya saya memilih taichi, dan itu cukup menyenangkan serta mudah dipakai, tetapi cakupan penerapannya agak terbatas

  • Saya penasaran mengapa pendekatan biased reference counting yang dijelaskan di https://peps.python.org/pep-0703/ hanya mengasumsikan afinitas satu thread, lalu memerlukan increment/decrement atomik bila diakses dari thread lain
    Pada implementasi lain, misalnya beberapa crate Rust yang juga menerapkan biased reference counting, saya pernah melihat pendekatan yang hanya melakukan increment atomik saat dipindahkan ke thread baru, lalu thread tersebut melakukan increment/decrement non-atomik sampai nilainya kembali ke 0, dan di akhir melakukan decrement atomik. Saya penasaran apakah ini karena implementasinya ditambahkan ke sistem yang sudah ada sehingga hanya ada satu PyObject dan tidak bisa diganti agar menunjuk ke objek lokal-thread yang baru

    • Ke depannya CPython mungkin bisa mengimplementasikan transfer kepemilikan, tetapi itu sedikit lebih rumit
      Di Rust, "move" untuk transfer kepemilikan adalah bagian dari bahasa, tetapi di C atau Python tidak ada konsep yang setara, jadi sulit menentukan kapan kepemilikan harus dipindahkan dan thread mana yang harus menjadi pemilik baru. Heuristik bisa dipakai. Misalnya, saat objek dimasukkan ke queue.SimpleQueue, kepemilikan bisa dilepas atau dipindahkan, tetapi bahkan dalam kasus itu pun sulit mengetahui sebelumnya thread mana yang akan melakukan "get" pada objek di dalam queue
      Keuntungan performanya juga tampaknya kecil. Banyak objek hanya diakses oleh satu thread, dan sebagian objek memang diakses oleh banyak thread, tetapi objek yang awalnya diakses eksklusif oleh satu thread lalu setelah itu diakses eksklusif hanya oleh thread lain itu jarang
  • Tadinya saya membaca berita tentang tranched bread, dan sekarang ini juga? Zaman yang luar biasa
    Saya agak kecewa saat proyek Unladen Swallow [1] meredup begitu saja. Senang melihat Python kembali ke jalur optimasi inti
    [1] https://en.wikipedia.org/wiki/CPython#Unladen_Swallow

  • Tolong jelaskan seperti kepada anak lima tahun
    Saya paham secara konsep apa itu GIL. Tapi dampak perubahan ini apa? Apakah sekarang paket-paket akan rusak sambil kita berharap ada peningkatan performa secara umum?

    • Dulu, karena GIL, orang pada praktiknya hampir tidak menulis Python multithread. Thread biasanya dipakai untuk menangani beberapa pekerjaan yang mungkin terblokir pada I/O yang terpisah, dan tentu itu umum serta berguna, tetapi tidak membantu performa kode Python yang berfokus pada CPU
      Bahkan kalau bukan pekerjaan CPU berat, perubahan ini tetap bisa berguna. Belakangan ini banyak kode ditulis dengan fitur bahasa asyncio bawaan Python. Ini bekerja di satu thread dengan menyerahkan eksekusi melalui async/await seperti NodeJS, dan dengan satu thread saja sudah bisa menghasilkan throughput yang cukup baik hingga ribuan request per detik
      Namun masalah besarnya adalah begitu ada pekerjaan CPU apa pun dijalankan, semua coroutine lain akan ikut terblokir, sehingga muncul berbagai masalah yang samar dan throughput request per detik pun hancur. Misalnya, Anda melihat timeout I/O acak pada satu coroutine, padahal penyebab sebenarnya bisa jadi coroutine lain yang sama sekali berbeda sempat memakai CPU sejenak. Sangat sulit juga mengamati mengapa hal seperti ini terjadi. asyncio memang menyediakan fungsi asyncio.to_thread() [1] yang membantu memindahkan pekerjaan blocking ke luar thread utama, tetapi karena adanya GIL, itu tidak benar-benar mengisolasi pekerjaan yang berfokus pada CPU agar tidak mengganggu coroutine lain
      [1] https://docs.python.org/3/library/asyncio-task.html#asyncio....
    • Jika ada paket yang bergantung pada GIL, maka GIL akan diaktifkan. Paketnya tidak akan rusak
  • Untuk yang penasaran, GIL adalah singkatan dari Global Interpreter Lock

  • Apakah ada bahan yang merangkum gambaran besarnya dengan baik?

  • Akhirnya, saya menantikan benchmark berbagai alat