پرش به محتوای اصلی
پرش به محتوای مقاله

توان عملیاتی Runloom در پایتون ۳.۱۳ با زبان Go برابری کرد

·۱۹ تیر ۱۴۰۵۴ دقیقه مطالعه
بافت نخ‌آزاد پایتون ۳.۱۳+ برای اجرای هم‌زمان سبک‌وزن شبیه Goroutines در Go
بافت نخ‌آزاد پایتون ۳.۱۳+ برای اجرای هم‌زمان سبک‌وزن شبیه Goroutines در Go
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

دستیابی به توان عملیاتی برابر با زبان Go در پایتون بدون نیاز به کلمات کلیدی async/await. نوآوری اصلی در استفاده از اسمبلی دست‌نویس برای تعویض زمینه در نسخه‌های free-threaded پایتون ۳.۱۳ است.

تصور کنید توسعه‌دهندگان پایتون بتوانند میلیون‌ها تسک هم‌زمان را روی تمام هسته‌های CPU اجرا کنند، بدون اینکه حتی یک بار کلمه async/await را بنویسند. این وعده‌ی Runloom است؛ محیط اجرای جدیدی برای نسخه‌های free-threaded پایتون ۳.۱۳t و بالاتر که با پیاده‌سازی یک زمان‌بند «سرقت‌کار» (work-stealing scheduler) به سبک زبان Go، کدهای مسدودکننده (blocking) را مانند فیبرهای تعاونی مدیریت می‌کند. طبق مستندات منتشرشده در گیت‌هاب این پروژه در ۱۰ ژوئیه ۲۰۲۶، این سیستم با استفاده از اسمبلی دست‌نویس برای تعویض زمینه (context switching)، به عملکردی نزدیک به زبان‌های نیتیو دست یافته است.

سال‌ها بود که قفل مفسر جهانی (GIL) در پایتون، برنامه‌نویسان را مجبور می‌کرد بین پیچیدگی asyncio یا سربار سنگین رشته‌های سیستم‌عامل (OS threads) یکی را انتخاب کنند. در حالی که asyncio مقیاس‌پذیر است، اما نیازمند بازنویسی کامل کد در بلوک‌های async def است. همان‌طور که در تحلیل‌های پیشین ما درباره‌ی مدیریت حافظه در مدل‌های زبانی اشاره کردیم، هرگونه سربار اضافی در لایه‌های زیرین می‌تواند کارایی کل سیستم را به شدت کاهش دهد. Runloom این بازی را با یک «مونکی پچ» (monkey patch) تغییر می‌دهد؛ ابزاری که باعث می‌شود فراخوانی‌های استاندارد و مسدودکننده — مثل urllib.request.urlopen — به‌جای متوقف کردن کل رشته‌ی سیستم‌عامل، به‌طور شفاف گوروتین (goroutine) را پارک کنند.

جزئیات پیاده‌سازی و معماری

این سیستم به‌طور خاص برای پایتون ۳.۱۳t و ۳.۱۴t (در حالت GIL-off) طراحی شده است. برای دستیابی به بهره‌وری در چند‌هسته‌ای، Runloom از یک snapshot از PyThreadState برای هر گوروتین استفاده می‌کند که شامل cframe، پشته داده (datastack)، اطلاعات استثناها (exc_info)، متغیرهای محیطی (contextvars) و بازگشت‌ها (recursion) است. این معماری تضمین می‌کند که میلیون‌ها گوروتینِ رها شده بتوانند بدون برخورد با مشکل «پرتگاه زنجیره فریم‌ها»، از رشته‌های مرکزی (hub threads) مشترک شوند.

ساختار زمان‌بندی این سیستم از مدل M:N استفاده می‌کند. هر hub دارای یک صف Chase-Lev و یک سیستم ارسال MPSC است تا اطمینان حاصل شود گوروتین‌های بیدار شده به hub مبدأ خود بازگردانده می‌شوند. همچنین مکانیسم ایزولاسیون و بازیابی تعبیه شده است؛ بنابراین اگر یک فراخوانی مسدودکننده غیرمنتظره باعث توقف یک hub شود، محیط اجرا آن را به‌طور خودکار شناسایی و بازیابی می‌کند.

طبق گزارش‌های فنی روی یک ماشین ۶۴ هسته‌ای، Runloom در توان عملیاتی (Throughput) — یعنی مقدار داده‌ای که سیستم در واحد زمان پردازش می‌کند، شبیه به سرعت عبور خودروها از یک اتوبان در ساعت پیک — با زبان Go برابری کرده است. یافته‌های این پروژه نشان می‌دهد نرخ ایجاد تسک در لایه C (c_entry) به ۲.۲۹ میلیون در ثانیه رسیده که حتی از ۲.۱۰ میلیونِ Go بیشتر است. در حالی که استفاده از API سطح پایتون (runloom.fiber) سرعت را به ۱.۳۵ میلیون می‌رساند (حدود ۰.۶۵ برابر Go).

در تست‌های echo با هندلر پایتونی، Runloom به ۵۹۶ هزار درخواست در ثانیه دست یافت که تقریباً با ۶۰۳ هزار درخواستِ Go برابر است. نکته قابل توجه این است که با استفاده از هندلر C، این عدد حتی از Go پیشی گرفت.

مشخصات فنی دقیق

  • تعویض زمینه: استفاده از اسمبلی دست‌نویس برای x86_64 SysV و aarch64 با تأخیر حدود ۸۰ نانوثانیه در هر تعویض (swap)؛ در ویندوز از Windows Fibers یا POSIX ucontext استفاده می‌شود. زمان رهاسازی (Yield) حدود ۷۵ نانوثانیه است که با Gosched در زبان Go برابری می‌کند.
  • زمان‌بند: مدل M:N با استفاده از صف‌های Chase-Lev برای هر hub. زمان رفت و برگشت (RT) کانال‌ها حدود ۵۰ نانوثانیه است.
  • ورودی/خروجی شبکه: پشتیبانی یکپارچه از netpoll شامل epoll، kqueue، IOCP، WSAPoll و select. گوروتین‌ها از طریق مکانیسم سه‌حالته park-commit که فاقد مشکل lost-wake است، پارک می‌شوند.
  • ردپای حافظه: یک فیبر پارک‌شده ۸.۸ کیلوبایت حافظه می‌گیرد که حدود ۳.۳ برابر Go (۲.۷ کیلوبایت) است؛ این شکاف به‌دلیل وجود فریم‌های ارزیابی CPython (eval frame) است.
  • اتصالات: نرخ چرخش اتصال (ایجاد اتصال جدید برای هر درخواست) حدود ۷۵ تا ۷۸ هزار در ثانیه است که دقیقاً با Go مطابقت دارد.

باید توجه داشت که Runloom سرعت اجرای کد پایتون را در هر هسته افزایش نمی‌دهد؛ بلکه اجازه می‌دهد CPython تمام هسته‌های موجود را با مدل برنامه‌نویسی مسدودکنندهِ آشنا اشباع کند. سرعت ۸۰ هزار عملیات خالص پایتونی در هر ثانیه به‌ازای هر هسته ثابت می‌ماند، اما اکنون یک پروسه می‌تواند هم‌زمان روی تمام هسته‌ها به این حد برسد.

این محیط اجرا همچنین یک پل برای کدهای قدیمی asyncio از طریق runloom.aio.run(main()) ارائه می‌دهد. این مسیر اجازه می‌دهد بدون بازنویسی کد، آن را منتقل کنید، هرچند که در این حالت روی یک زمان‌بند تک‌رشته‌ای اجرا شده و افزایش سرعت چند‌هسته‌ای را فراهم نمی‌کند.

این تغییر به معنای حذف «مالیات async» برای اپلیکیشن‌های پایتونی با هم‌زمانی بالا است. با انتقال منطق زمان‌بندی به یک اکستنشن C و بهره‌گیری از بیلد‌های بدون GIL در پایتون ۳.۱۳t و ۳.۱۴t، این محیط اجرا مشکل پرتگاه زنجیره فریم‌ها را که معمولاً در بازگشت‌های عمیق یا میلیون‌ها تسک رها شده رخ می‌دهد، از بین می‌برد.

از دیدگاه عملی، این یعنی یک توسعه‌دهنده می‌تواند یک خزنده‌ی وب (crawler) ساده را با استفاده از urllib بنویسد و شاهد مقیاس‌پذیری آن روی ۶۴ هسته باشد، بدون اینکه حتی یک کلمه await به کار ببرد. هزینه این کار، سربار حافظه بالاتر است که توسط eval frame پایتون ایجاد شده و اصلی‌ترین تفاوت با زبان‌های کامپایل‌شده‌ای مثل Go است.

کاربران در حال حاضر می‌توانند wheelهای پیش‌ساخته را برای لینوکس (x86_64/aarch64)، مک (arm64/x86_64) و ویندوز (AMD64) برای نسخه‌های پایتون ۳.۱۱ تا ۳.۱۴ نصب کنند. برای حداکثر عملکرد، تنظیم runloom.optimize("throughput") اولویت را به نرخ ایجاد تسک از طریق fiber_fast می‌دهد، در حالی که حالت runloom.optimize("memory") اندازه پشته‌ها را برای کاهش مصرف RSS بهینه می‌کند.

برای شروع کار با این سیستم، توسعه‌دهندگان کافی است تابع runloom.monkey.patch() را فراخوانی کنند تا کتابخانه استاندارد تعاونی شود و سپس توابع خود را در runloom.fiber() قرار دهند. نقطه ورود runloom.run(n, main) تعیین می‌کند که چه تعداد رشته‌ی مرکزی (hub threads) روی هسته‌های واقعی CPU ایجاد شوند.

گام بعدی شما

  • اگر از پایتون ۳.۱۳t استفاده می‌کنید، کتابخانه Runloom را برای تست مقیاس‌پذیری تسک‌های I/O-bound نصب کنید.
  • برای بهینه‌سازی، بین حالت throughput (برای حداکثر نرخ ایجاد تسک) و حالت memory (برای کاهش مصرف RAM) یکی را انتخاب کنید.
  • فراخوانی runloom.monkey.patch() را در ابتدای برنامه قرار دهید تا کتابخانه‌های استاندارد به‌طور تعاونی عمل کنند.

اما داستان سخت‌افزاری این تحول حتی شگفت‌انگیزتر است — به تحلیل ما درباره‌ی تراشه‌های Blackwell و اثر آن بر استنتاج مدل‌ها مراجعه کنید.

چرا این موضوع مهم است؟

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

تأثیر برای ایران

برای توسعه‌دهندگان ایرانی که در پروژه‌های High-concurrency با محدودیت سخت‌افزاری مواجه‌اند، این ابزار امکان بهره‌برداری حداکثری از CPUهای موجود بدون نیاز به مهاجرت costly به زبان Go را فراهم می‌کند.

·نگاه ما
تحریریه دات‌هوش

Runloom با حذف «مالیات async»، تضاد دیرینه بین سادگی کدنویسی پایتون و کارایی زبان‌های کامپایل‌شده مثل Go را می‌شکند. این ابزار ثابت می‌کند که مشکل اصلی پایتون در مقیاس‌پذیری، نه در نحو (Syntax) بلکه در مدل زمان‌بندی و GIL بود. اکنون برنامه‌نویسان می‌توانند بدون تغییر پارادایم ذهنی خود به سمت برنامه‌نویسی ناهمگام، از قدرت کامل سخت‌افزارهای چند‌هسته‌ای بهره ببرند.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.