معرفی gilguard؛ پیدا کردن import‌هایی که GIL را دوباره فعال می‌کنند

چرا مهم است

اگر به free-threaded Python مهاجرت می‌کنی، یک C extension می‌تواند GIL را برای باقی اجرای برنامه دوباره فعال کند. gilguard کمک می‌کند package و import آغازکننده را پیدا کنی، نه اینکه فقط نام یک module را در log ببینی.

قبل از خواندن، این‌ها را بدان۳ مفهوم
Global Interpreter Lock (GIL)

محدودیتی در اجرای Python که باعث می‌شود thread‌ها برای اجرای کد Python نوبت بگیرند؛ در حالت free-threaded می‌توان بدون آن اجرا کرد.

بیشتر درباره این مفهوم

CPython

پیاده‌سازی Python که با C نوشته شده و اجرای برنامه‌های Python و بارگذاری C extension‌ها را انجام می‌دهد.

در حال نگارش

thread-safe

ویژگی کد یا ابزاری که هنگام استفاده هم‌زمان از چند thread،؜ رفتار درست خود را حفظ می‌کند؛ خاموش ماندن GIL به‌تنهایی این ویژگی را ثابت نمی‌کند.

در حال نگارش

Michael Kennedy ابزار gilguard را معرفی کرده است؛ یک package کوچک و بدون dependency برای free-threaded Python در نسخه‌های 3.13t،؜ 3.14t و 3.15t که روی PyPI در دسترس است. کار آن شناسایی import‌هایی است که Global Interpreter Lock (GIL) را دوباره فعال می‌کنند؛ اتفاقی که ممکن است تنها با یک RuntimeWarning در log آغاز اجرای برنامه دیده شود.

در CPython،؜ هر C extension باید از طریق Py_mod_gil اعلام کند که می‌تواند بدون GIL اجرا شود. اگر چنین اعلامی وجود نداشته باشد، هنگام بارگذاری آن، GIL برای باقی عمر process فعال می‌شود و دیگر نمی‌توان آن را در همان process خاموش کرد. thread‌ها همچنان اجرا می‌شوند، اما دوباره نوبتی پیش می‌روند. هشدار معمول فقط نام module را نشان می‌دهد، نه لزوماً package ارائه‌دهنده یا import آغازکننده در برنامه.

gilguard باید اولین import برنامه باشد و به فراخوانی یا تنظیم دیگری نیاز ندارد. اگر import بعدی GIL را فعال کند، ابزار در همان نقطه GILEnabledError ایجاد می‌کند. گزارش شامل نام و مسیر extension module،؜ نام و نسخه distribution،؜ زنجیره import و خط مربوط در برنامه است. این exception از BaseException ارث می‌برد تا handler‌های معمول Exception آن را نبلعند. برای ادامه اجرا همراه با هشدار، می‌توان GILGUARD_ACTION=warn را تنظیم کرد.

دستور python -m gilguard محیط را بررسی می‌کند و برای هر package یک interpreter تازه اجرا می‌کند؛ چون در یک process فقط نخستین فعال‌کننده GIL قابل تشخیص است. این بررسی‌ها به‌صورت موازی انجام می‌شوند و در صورت یافتن مورد، exit code برابر ۱ است تا بتوان آن را در CI به کار گرفت. گزینه --json برای خروجی قابل استفاده در ابزارها و --strict برای شکست بررسی در صورت خطای import هم وجود دارد.

این scanner،؜ extension‌هایی را که به‌صورت lazy و هنگام استفاده از یک قابلیت بارگذاری می‌شوند، در زمان import نمی‌بیند. برای پوشش این حالت، نویسنده اجرای pytest -p gilguard را در کنار scanner توصیه می‌کند؛ البته فقط مسیرهایی بررسی می‌شوند که test‌ها واقعاً اجرا کنند.

نتیجه بدون خطا صرفاً یعنی import‌های اجراشده GIL را دوباره فعال نکرده‌اند. این نتیجه نه thread-safe بودن برنامه و dependency‌های آن را ثابت می‌کند، نه افزایش سرعت با thread‌های بیشتر را؛ این‌ها به بررسی‌های جداگانه نیاز دارند.

نکته کلیدی

بررسی محیط با python -m gilguard را کنار اجرای pytest -p gilguard به کار بگیر؛ اولی زمان import را می‌بیند و دومی مسیرهای اجراشده در test را. نتیجه بدون خطا را مدرک thread-safe بودن یا افزایش سرعت ندان.

خواندن مقاله اصلی (در زبانه‌ی جدید باز می‌شود)
برچسب‌ها (۳)