۳۰ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

آیا بالاخره زمان حذف Python GIL فرا رسیده است؟

قفل مفسر سراسری هم جزء کلیدی زمان اجرای پایتون است و هم یک مانع بزرگ برای چند رشته ای. اینها برنامه هایی برای دور زدن یا خلاص شدن از شر آن هستند.

قفل مفسر سراسری هم جزء کلیدی زمان اجرای پایتون است و هم یک مانع بزرگ برای چند رشته ای. اینها برنامه هایی برای دور زدن یا خلاص شدن از شر آن هستند.

قوی، انعطاف‌پذیر و برنامه‌نویس سازگار، Python به طور گسترده برای همه چیز از توسعه وب گرفته تا یادگیری ماشین استفاده می‌شود. توسط دو بیشترین استناد معیار، پایتون حتی از جاوا و C پیشی گرفته و به محبوب ترین زبان برنامه نویسی تبدیل شده است. پس از سال‌ها محبوبیت فزاینده، پایتون ممکن است غیرقابل توقف به نظر برسد.

اما پایتون حداقل با یک مانع بزرگ برای رشد آینده خود به عنوان یک زبان برنامه نویسی مواجه است. GIL نامیده می‌شود، قفل مفسر جهانی، و توسعه‌دهندگان پایتون چندین دهه است که سعی کرده‌اند آن را از اجرای پیش‌فرض پایتون حذف کنند.

اگرچه GIL یک هدف حیاتی، یعنی تضمین ایمنی نخ را انجام می دهد، اما همچنین یک گلوگاه جدی برای برنامه های چند رشته ای ایجاد می کند. به طور خلاصه، GIL از استفاده کامل پایتون از سیستم های چند پردازنده ای جلوگیری می کند. برای اینکه پایتون یک زبان درجه یک برای برنامه نویسی همزمان باشد، بسیاری معتقدند که GIL باید حذف شود.

تاکنون، تلاش‌ها برای حذف GIL شکست خورده است. اما موج جدیدی از تلاش‌ها در حال افزایش است تا GIL را به گذشته تبدیل کند و Python را حتی برای برآوردن نیازهای برنامه‌نویسی آینده مجهزتر کند.

چرا پایتون GIL دارد

به طور دقیق، قفل مفسر سراسری به طور انتزاعی بخشی از پایتون نیست. این جزء پرکاربردترین پیاده سازی پایتون، CPython است که توسط بنیاد نرم افزار پایتون نگهداری می شود.

GIL ایمنی رشته را در CPython با اجازه دادن به تنها یک رشته در حال اجرا در هر زمان برای اجرای بایت کد پایتون تضمین می کند. سیستم‌های مدیریت حافظه CPython ایمن نیستند، بنابراین از GIL برای سریال‌سازی دسترسی به اشیا و حافظه برای جلوگیری از شرایط مسابقه استفاده می‌شود. اگر CPython GIL نداشت، باید شرایط همزمانی و مسابقه را به روش دیگری مدیریت کند.

چه چیزی GIL را چنین مشکلی ایجاد می کند؟ برای مثال، از چند رشته ای واقعی در مفسر CPython جلوگیری می کند. این امر باعث می‌شود تا یک کلاس کامل از شتاب‌دهی‌های کد – بهینه‌سازی‌هایی که به راحتی در سایر زبان‌های برنامه‌نویسی در دسترس هستند – پیاده‌سازی در پایتون بسیار سخت‌تر شود.

بیشتر توسعه دهندگان به یک شکل در اطراف GIL کار می کنند. برای مثال، ماژول multiprocessing امکان اجرای نمونه‌های همزمان مفسر پایتون (هر کدام در رشته فیزیکی خاص خود) و به اشتراک گذاشتن کار بین آنها را ممکن می‌سازد. با این حال، از آنجایی که به اشتراک گذاری داده ها بین نمونه های پایتون هزینه های زیادی را به همراه دارد، multiprocessing فقط برای کلاس های خاصی از مشکلات به خوبی کار می کند.

راه‌حل دیگر استفاده از پسوندهای پایتون است که معمولاً به زبان C نوشته می‌شوند. این برنامه‌ها خارج از مفسر پایتون اجرا می‌شوند، بنابراین پردازشی که انجام می‌دهند وابسته به GIL نیست. مشکل این است که این فقط تا زمانی صادق است که کار شامل اشیاء پایتون نباشد، فقط کد C و ساختارهای داده C را شامل شود. بنابراین، مانند multiprocessing، پسوندهای C فقط دسته کوچکی از مشکلات را حل می‌کنند.

بازگشت به دفتر یک اشتباه است

با افزایش محبوبیت پایتون، خجالت ناشی از کسری مانند GIL در زبان نیز افزایش می یابد. و بنابراین تلاش‌های مختلفی در گذشته و حال برای از بین بردن GIL انجام شده است.

خلاص شدن از شر GIL

مشکل، همانطور که ممکن است حدس بزنید، این است که خلاص شدن از شر GIL بسیار ساده تر از انجام آن است. GIL هدف مهمی را دنبال می کند. جایگزینی آن نه تنها باید ایمنی رزوه را تضمین کند، بلکه باید تعدادی از الزامات دیگر را نیز برآورده کند.

در میان بسیاری از اهدافی که یک جایگزین GIL باید به آنها برسد، اینها مهمترین آنها هستند: p>

  • فعال کردن همزمانی. سود بزرگ برای داشتن پایتون بدون GIL، همزمانی واقعی در زبان است. جایگزینی GIL با مکانیسم دیگری که همزمانی را فعال نمی‌کند، پیشرفتی نیست.

  • نداشتن سرعت برنامه‌های تک رشته‌ای. هر جایگزینی GIL که باعث کندتر اجرای برنامه‌های تک رشته‌ای شود، ضرر خالص است، زیرا اکثریت قریب به اتفاق نرم‌افزار پایتون تک رشته‌ای هستند.

  • سازگاری رو به عقب را خراب نمی کند. نرم افزار موجود پایتون نه تنها باید به سرعت قبل اجرا شود، بلکه باید مطابق انتظار عمل کند.

  • هزینه تعمیر و نگهداری عمده ای متحمل نمی شود. تیم توسعه Python منابع یا نیروی انسانی بی نهایت ندارد. یک Python بدون GIL باید حداقل به اندازه مفسر موجود آسان باشد.

فعال کردن همزمانی. سود بزرگ برای داشتن پایتون بدون GIL، همزمانی واقعی در زبان است. جایگزینی GIL با مکانیسم دیگری که همزمانی را فعال نمی‌کند، پیشرفتی نیست.

نداشتن سرعت برنامه‌های تک رشته‌ای. هر جایگزینی GIL که باعث کندتر اجرای برنامه‌های تک رشته‌ای شود، ضرر خالص است، زیرا اکثریت قریب به اتفاق نرم‌افزار پایتون تک رشته‌ای هستند.

سازگاری رو به عقب را خراب نمی کند. نرم افزار موجود پایتون نه تنها باید به سرعت قبل اجرا شود، بلکه باید مطابق انتظار عمل کند.

هزینه تعمیر و نگهداری عمده ای متحمل نمی شود. تیم توسعه Python منابع یا نیروی انسانی بی نهایت ندارد. یک Python بدون GIL باید حداقل به اندازه مفسر موجود آسان باشد.

با توجه به نوار بالای یک جایگزین GIL، جای تعجب نیست که تمام تلاش‌های قبلی برای حذف GIL متوقف شده یا بی نتیجه مانده است.

پابلو گالیندو، یکی از پنج عضو شورای راهبری پایتون که مسیر توسعه پایتون را تعیین می‌کند، معتقد است که حذف GIL یک هدف واقع‌بینانه برای پایتون است، “اما همچنین یک هدف بسیار دشوار”.

گالیندو در یک مصاحبه ایمیلی گفت: «سوال واقعاً این نیست که آیا امکان پذیر است (ما می دانیم که قطعاً ممکن است). سوال اینجاست که قیمت واقعی چقدر است؟ و اگر ما به عنوان یک جامعه بخواهیم این هزینه را بپردازیم. این نیز یک موضوع پیچیده است، زیرا هزینه پرداختی نیز به طور مساوی توزیع نمی شود.»

هزینه حذف GIL را نه تنها توسعه‌دهندگان اصلی پایتون، بلکه همه توسعه‌دهندگانی که از پایتون استفاده می‌کنند و همچنین کسانی که بسته‌های زبان پایتون را نگهداری می‌کنند، پرداخت می‌کنند.

تلاش های قبلی برای حذف GIL

خلاص شدن از شر GIL ایده جدیدی نیست. تلاش‌های قبلی برای حذف GIL Python نمونه‌هایی از مشکلاتی را که گالیندو درباره آن صحبت می‌کند ارائه می‌دهد.

اولین تلاش‌های رسمی برای حذف GIL به سال ۱۹۹۶ برمی‌گردد، زمانی که پایتون در نسخه ۱.۴ بود. گرگ استاین وصله ای برای حذف GIL ایجاد کرد ، عمدتاً به عنوان یک آزمایش. کار کرد، اما برنامه‌های تک رشته عملکرد قابل توجهی داشتند. نه تنها این پچ پذیرفته نشد، بلکه تجربه به وضوح نشان داد که حذف GIL دشوار است. هزینه توسعه هنگفتی دارد.

در چند سال گذشته، با افزایش محبوبیت پایتون، پروژه‌های حذف GIL بیشتری مطرح شده‌اند. یکی از تلاش‌هایی که به طور گسترده مورد بحث قرار گرفت، پروژه ژیلکتومی لری هستینگز بود، یک انشعاب از پایتون که چندین تغییر مهم را به کار می‌برد. شمارش مرجع و سایر مکانیسم های داخلی Gilectomy نویدهایی را نشان داد، اما اکثر APIهای موجود CPython و حتی بیشتر آنها را شکست. کار شجاعانه از طرف هاستینگز نتوانست Gilectomy را به اندازه CPython کند.

چند پروژه دیگر شامل فورک کردن پایتون و بازنویسی آن برای پشتیبانی بهتر از موازی سازی بود. PyParallel، یکی از این پروژه‌ها، GIL را به عنوان یک محدودیت برای موازی‌سازی بهتر حذف کرد. در واقع حذف GIL. PyParallel یک ماژول جدید به نام parallel اضافه کرد که به اشیا اجازه می داد از طریق پشته TCP با یکدیگر ارتباط برقرار کنند. در حالی که PyParallel با موفقیت GIL را دور زد، این رویکرد محدودیت هایی داشت. برای یکی، کد موازی باید از طریق پشته TCP (آهسته) به جای مکانیسم حافظه مشترک (سریع) ارتباط برقرار می کرد. PyParallel از سال ۲۰۱۶ به روز نشده است.

PyPy، جایگزین پایتون کامپایل JIT، نه تنها یک GIL مخصوص به خود دارد، بلکه یک پروژه حذف GIL نیز دارد. هدف STM (حافظه معاملاتی نرم‌افزار) سرعت بخشیدن به چندین رشته به صورت موازی بود. در PyPy، اما در اینجا نیز هزینه به عملکرد تک رشته ای ضربه قابل توجهی وارد کرد، از ۲۰٪ تا ۲ برابر کندتر. شاخه STM PyPy نیز دیگر در حال توسعه فعال نیست.

تلاش های فعلی برای حذف GIL

سوابق بد برای تلاش‌های قبلی برای حذف GIL، تفکر جدیدی را در مورد راه آینده برانگیخته است. شاید بهترین رویکرد حذف GIL نباشد، بلکه – همانطور که PyParallel تلاش کرد – با کنار زدن آن از مانعی برای موازی سازی کمتر کند، سپس آن عملکرد را به متوسط ​​توسعه دهنده پایتون.

در تئوری، ماژول‌های Python مانند multiprocessing و پروژه‌های شخص ثالث مانند Dask قبلا این کار را انجام دهید یکی چندین کپی مجزا از مفسر را می چرخاند، یک وظیفه را بین آنها تقسیم می کند و در صورت نیاز، داده های شی را بین آنها سریال می کند. اما multiprocessing با هزینه‌های سربار زیادی همراه است و پروژه‌های شخص ثالث دقیقاً همین هستند – پیشنهادات شخص ثالث، نه مؤلفه‌های بومی ساخته شده در پایتون.

چند پیشنهاد پایتون برای بهبود این وضعیت در حال انجام است. هیچ یک از آنها به خودی خود راه حلی را تشکیل نمی دهند. همه هنوز فقط پیشنهاد هستند. اما در مجموع، آنها به جهتی که پایتون در حال حرکت است اشاره می کنند.

حذف GIL با مترجم فرعی

یک پروژه، PEP 684، پروژه “GIL هر مترجم” است. ایده این است که مفسرهای متعدد پایتون داشته باشیم که هر کدام GIL خاص خود را دارند و در یک فرآیند اجرا می‌شوند. . در واقع پایتون از نسخه ۱.۵ از انجام این کار پشتیبانی کرده است، اما مفسران در همان فرآیند همیشه حالت جهانی زیادی را برای دستیابی به موازی سازی واقعی به اشتراک گذاشته اند. PEP 684 تا آنجا که ممکن است حالت مشترک را به هر مفسر منتقل می کند، بنابراین آنها می توانند با حداقل وابستگی متقابل کنار هم اجرا شوند.

اما یک مشکل بزرگ در این رویکرد این است که چگونه اشیاء پایتون را بین مفسرها به اشتراک بگذاریم. به اشتراک گذاری داده های خام، مانند جریان های بایت، دشوار نیست، اما همچنین بسیار مفید نیست. به اشتراک گذاری اشیاء غنی پایتون بسیار مفیدتر است، اما همچنین بسیار دشوارتر است. با این وجود، هر طرحی برای اجازه همزمانی واقعی باید شامل راهی برای اشتراک گذاری اشیاء پایتون باشد.

گالیندو می‌گوید رویکرد زیرمفسر (همانطور که به آن نیز گفته می‌شود) یک کاندید اصلی برای کار در اطراف GIL و برای ارائه استراتژی برای مدیریت اشیاء پایتون در میان مفسرها است. همانطور که گالیندو آن را در ایمیلی به من گفت:

یکی از چشم اندازهای جذاب مفسرهای چندگانه این است که ممکن است بتوان اشیاء را بین این مفسرها در فضای حافظه یکسان کانالیزه کرد، بدون اینکه نیازی به تطبیق آنها در سراسر فرآیندها باشد. این ممکن است همچنین به برخی از جنبه‌های مشکل کپی در نوشتن که CPython با مفسرهای متعدد دارد کمک کند، اما این هنوز هم قابل مشاهده است، زیرا ما فاقد یک پیاده‌سازی کامل با سطح کاملاً تعریف شده هستیم. API.

به عبارت دیگر، قبل از اینکه یک GIL برای هر مترجم اتفاق بیفتد، کارهای بیشتری باید روی قسمت‌های داخلی CPython انجام شود.

یک پیشنهاد دیگر که در ابتدا در سال ۲۰۱۷ مطرح شد، با PEP 684 همراه است. PEP 554 عملکرد چند مترجم را برای کاربر معمولی پایتون به عنوان بخشی از کتابخانه استاندارد نشان می دهد، به جای اینکه آنها را ملزم به نوشتن پسوند C کند. به این ترتیب، همانطور که چندین مفسر به طور واقعی مفیدتر می شوند، توسعه دهندگان پایتون یک روش استاندارد برای کار با آنها خواهند داشت.

ایده های دیگر برای حذف GIL

پیشنهاد دیگری که در ژانویه ۲۰۲۳ مطرح شد و در حال حاضر در دست بحث‌های فعال است، راهی را برای توسعه‌دهندگان فراهم می‌کند تا در کنار پایتون موجود روی پایتون بدون GIL کار کنند.

PEP 703 یک گزینه ساخت به CPython اضافه می کند تا امکان کامپایل مفسر بدون GIL را فراهم کند. پیش‌فرض همچنان شامل GIL است، اما توسعه‌دهندگان پایتون می‌توانند به‌جای اینکه در پروژه‌ای جداگانه، GIL را به‌عنوان بخشی از توسعه CPython مستقیماً حذف کنند. با گذشت زمان و با کار کافی، نسخه بدون GIL Python می‌تواند به حالت پیش‌فرض ساخت تبدیل شود.

اما این رویکرد دارای چندین جنبه منفی است. یکی از مهمترین آنها هزینه نگهداری بزرگتر است، نه تنها برای CPython، بلکه همچنین برای برنامه های افزودنی که ممکن است به دلیل فرضیات مربوط به داخلی CPython خراب شود. علاوه بر این، مانند تمام تلاش‌های قبلی برای حذف GIL، تغییرات PEP 703 منجر به عملکرد بهتری می‌شود. برای برنامه های تک رشته ای.

چه پایتون GIL را اختیاری کند، چه از مفسرهای فرعی استفاده کند یا رویکرد دیگری اتخاذ کند، سابقه طولانی تلاش‌ها و آزمایش‌ها نشان می‌دهد که هیچ راه آسانی برای حذف GIL وجود ندارد – بدون هزینه‌های هنگفت توسعه یا بازگرداندن پایتون به روش‌های دیگر. اما با بزرگ‌تر شدن مجموعه‌های داده‌ها، و هوش مصنوعی، یادگیری ماشین، و دیگر حجم‌های کاری پردازش داده، موازی کاری بیشتری را طلب می‌کنند، یافتن پاسخی برای GIL یک عنصر کلیدی برای تبدیل پایتون به زبانی برای آینده و نه فقط در حال خواهد بود. p>