مقدمه
زبان برنامه نویسی پایتون یک رابط است که می تواند به روش های مختلفی پیاده سازی شود. برخی از نمونه ها عبارتند از CPython که از زبان C استفاده می کند، Jython که با استفاده از جاوا پیاده سازی می شود و غیره.
علیرغم اینکه CPython محبوب ترین است، سریع ترین نیست. PyPy یک پیاده سازی جایگزین پایتون است که هم سازگار و هم سریع است. PyPy به کامپایل JIT بستگی دارد که زمان اجرای عملیات طولانی مدت را به طور چشمگیری کاهش می دهد.
در این آموزش، PyPy را برای مبتدیان معرفی می کنیم و تفاوت های آن را با CPython برجسته می کنیم. همچنین به مزایا و محدودیت های آن خواهیم پرداخت. سپس، نحوه دانلود و استفاده از PyPy برای اجرای یک اسکریپت ساده پایتون را بررسی خواهیم کرد.
به طور خاص، این آموزش موارد زیر را پوشش می دهد:
- مروری سریع بر CPython
- مقدمه ای بر PyPy و ویژگی های آن
- محدودیت های PyPy
- اجرای PyPy در اوبونتو
- زمان اجرای PyPy در مقابل CPython
مروری سریع بر CPython
قبل از بحث در مورد PyPy، مهم است که بدانید CPython چگونه کار می کند. در زیر می توانید تصویری از خط لوله اجرای یک اسکریپت پایتون پیاده سازی شده با استفاده از CPython را مشاهده کنید.
با توجه به اسکریپت پایتون .py، کد منبع ابتدا با استفاده از کامپایلر CPython به کد بایت کامپایل می شود. بایت کد تولید و در فایلی با پسوند pyc ذخیره می شود. سپس بایت کد با استفاده از مفسر CPython در یک محیط مجازی اجرا می شود.
استفاده از کامپایلر برای تبدیل کد منبع به کد بایت مزایایی دارد. اگر از کامپایلر استفاده نشود، مفسر مستقیماً روی کد منبع کار می کند و آن را خط به خط به کد ماشین ترجمه می کند. عیب انجام این کار این است که برای ترجمه هر خط از کد منبع به کد ماشین باید برخی از فرآیندها اعمال شود و چنین فرآیندهایی برای هر خط تکرار می شود. به عنوان مثال، تجزیه و تحلیل نحو برای هر خط به طور مستقل از خطوط دیگر اعمال می شود، و بنابراین مفسر زمان زیادی را برای ترجمه کد صرف می کند. کامپایلر این مشکل را حل می کند زیرا می تواند همه کدها را به یکباره پردازش کند و بنابراین تجزیه و تحلیل نحو تنها یک بار به جای هر خط کد اعمال می شود. بنابراین بایت کد تولید شده از کامپایلر به راحتی تفسیر می شود. توجه داشته باشید که کامپایل کردن کل کد منبع ممکن است در برخی موارد مفید نباشد، و ما یک مثال واضح از آن را هنگام بحث در مورد PyPy خواهیم دید.
پس از تولید بایت کد، توسط مفسر در حال اجرا در ماشین مجازی اجرا می شود. محیط مجازی سودمند است، زیرا بایت کد CPython را از دستگاه جدا می کند و بنابراین پایتون را به پلتفرم متقابل تبدیل می کند.
متأسفانه فقط استفاده از کامپایلر برای تولید بایت کد برای سرعت بخشیدن به اجرای CPython کافی نیست. مفسر با ترجمه کد، هر بار که اجرا می شود، به کد ماشین کار می کند. بنابراین، اگر یک خط L X ثانیه طول بکشد تا اجرا شود، اجرای آن 10 بار هزینه ای معادل X*10 ثانیه خواهد داشت. برای عملیات طولانی مدت، این در زمان اجرای آن بسیار پرهزینه است.
بر اساس اشکالات CPython، اکنون اجازه دهید نگاهی به PyPy بیندازیم.
مقدمه ای بر PyPy و ویژگی های آن
PyPy یک پیاده سازی Python شبیه به CPython است که هم سازگار و هم سریع است. “Compliant” به این معنی است که PyPy با CPython سازگار است، زیرا می توانید تقریباً از تمام دستورات CPython در PyPy استفاده کنید. همانطور که در اینجا ذکر شد، برخی تفاوت های سازگاری وجود دارد. قدرتمندترین مزیت PyPy سرعت آن است. PyPy بسیار سریعتر از CPython است. ما بعداً آزمایش هایی را خواهیم دید که در آن PyPy حدود 7 برابر سریعتر عمل می کند. در برخی موارد حتی ممکن است ده ها یا صدها بار سریعتر از CPython باشد. پس چگونه PyPy به سرعت خود دست می یابد؟
سرعت
PyPy از یک کامپایلر به موقع (JIT) استفاده می کند که می تواند سرعت اسکریپت های پایتون را به طور چشمگیری افزایش دهد. نوع کامپایل مورد استفاده در CPython پیش از زمان (AOT) است، به این معنی که تمام کدها قبل از اجرا به بایت کد ترجمه می شوند. JIT فقط کد را در زمان اجرا ترجمه می کند، تنها زمانی که به آن نیاز است.
کد منبع ممکن است حاوی بلوکهای کدی باشد که اصلاً اجرا نشدهاند، اما همچنان با استفاده از کامپایلر AOT ترجمه میشوند. این منجر به کندتر شدن زمان پردازش می شود. وقتی کد منبع بزرگ و حاوی هزاران خط است، استفاده از JIT تفاوت بزرگی ایجاد می کند. برای AOT، کل کد منبع ترجمه می شود و بنابراین زمان زیادی را می گیرد. برای JIT، فقط بخشهای مورد نیاز کد اجرا میشود و سرعت آن را بسیار بیشتر میکند.
پس از اینکه PyPy بخشی از کد را ترجمه کرد، در حافظه پنهان ذخیره می شود. این به این معنی است که کد فقط یک بار ترجمه می شود و بعداً از ترجمه استفاده می شود. مفسر CPython هر بار که کد اجرا می شود، ترجمه را تکرار می کند، که دلیل دیگری برای کندی آن است.
بی زحمت
PyPy تنها راه برای افزایش عملکرد اسکریپتهای پایتون نیست، اما سادهترین راه است. به عنوان مثال، Cython می تواند برای افزایش سرعت تخصیص انواع C به متغیرها استفاده شود. مشکل این است که Cython از توسعه دهنده می خواهد که به صورت دستی کد منبع را بررسی کرده و آن را بهینه کند. این خسته کننده است و با افزایش اندازه کد، پیچیدگی آن افزایش می یابد. هنگامی که از PyPy استفاده می شود، شما فقط کد معمولی پایتون را بسیار سریعتر بدون هیچ تلاشی اجرا می کنید.
بدون پشته
پایتون استاندارد از پشته C استفاده می کند. این پشته توالی توابعی که از یکدیگر فراخوانی می شوند را ذخیره می کند (بازگشت). از آنجایی که اندازه پشته محدود است، شما در تعداد فراخوانی تابع محدود هستید.
PyPy از Stackless Python استفاده می کند، پیاده سازی Python که از پشته C استفاده نمی کند. در عوض، فراخوانی های تابع را در پشته در کنار اشیا ذخیره می کند. اندازه پشته بزرگتر از اندازه پشته است و بنابراین می توانید فراخوانی های عملکرد بیشتری را انجام دهید.
پایتون بدون پشته از ریز رشته ها نیز پشتیبانی می کند که بهتر از رشته های پایتون معمولی هستند. در یک رشته پایتون بدون پشته، میتوانید هزاران کار به نام «تسکلت» را اجرا کنید که همه آنها روی یک رشته اجرا میشوند.
استفاده از tasklet ها امکان اجرای همزمان وظایف را فراهم می کند. همزمانی به این معنی است که دو کار به طور همزمان با به اشتراک گذاشتن منابع یکسان کار می کنند. یک کار برای مدتی اجرا می شود، سپس متوقف می شود تا فضایی برای اجرای وظیفه دوم ایجاد شود. توجه داشته باشید که این با موازی سازی متفاوت است، که شامل اجرای دو کار جداگانه اما همزمان است.
استفاده از Tasklet ها تعداد رشته های ایجاد شده را کاهش می دهد و در نتیجه هزینه های سربار مدیریت همه این رشته ها توسط سیستم عامل را کاهش می دهد. در نتیجه، سرعت بخشیدن به اجرا از طریق جابجایی بین دو رشته، زمان بیشتری نسبت به جابجایی بین دو وظیفه دارد.
استفاده از Stackless Python نیز راه را برای اجرای ادامه باز کرد. Continuations به ما این امکان را می دهد که وضعیت یک کار را ذخیره کرده و بعداً برای ادامه کار آن را بازیابی کنیم. توجه داشته باشید که پایتون Stackless با پایتون استاندارد تفاوتی ندارد. فقط قابلیت های بیشتری را اضافه می کند. هر آنچه در پایتون استاندارد موجود است در پایتون Stackless نیز در دسترس خواهد بود.
پس از بحث در مورد مزایای PyPy، اجازه دهید در مورد محدودیت های آن در بخش بعدی صحبت کنیم.
محدودیت های PyPy
در حالی که می توانید از CPython در هر ماشین و هر معماری CPU استفاده کنید، PyPy پشتیبانی نسبتاً محدودی دارد.
در اینجا معماری های CPU که توسط PyPy پشتیبانی و نگهداری می شوند (منبع):
- x86 (IA-32) و x86_64
- پلتفرم های ARM (ARMv6 یا ARMv7، با VFPv3)
- AArch64
- PowerPC 64bit، هر دو کوچک و بزرگ endian
- System Z (s390x)
PyPy نمی تواند روی همه توزیع های لینوکس کار کند، بنابراین باید مراقب باشید که از یکی از توزیع های پشتیبانی شده استفاده کنید. اجرای باینری لینوکس PyPy روی یک توزیع پشتیبانینشده، یک خطا برمیگرداند. PyPy فقط یک نسخه از Python 2 و Python 3 را پشتیبانی می کند که PyPy 2.7 و PyPy 3.6 هستند.
اگر کدی که در PyPy اجرا می شود پایتون خالص باشد، سرعت ارائه شده توسط PyPy معمولاً قابل توجه است. اما اگر کد شامل پسوندهای C مانند NumPy باشد، PyPy ممکن است در واقع زمان را افزایش دهد. پروژه PyPy به طور فعال توسعه یافته است و بنابراین ممکن است در آینده پشتیبانی بهتری برای برنامه های افزودنی C ارائه دهد.
PyPy توسط تعدادی از فریمورک های محبوب پایتون مانند Kivy پشتیبانی نمی شود. Kivy به CPython اجازه می دهد تا بر روی تمامی پلتفرم ها از جمله اندروید و iOS اجرا شود. این بدان معنی است که PyPy نمی تواند روی دستگاه های تلفن همراه اجرا شود.
اکنون که مزایا و محدودیت های PyPy را دیدیم، بیایید نحوه اجرای PyPy در اوبونتو را توضیح دهیم.
اجرای PyPy در اوبونتو
شما می توانید PyPy را در مک، لینوکس یا ویندوز اجرا کنید، اما ما قصد داریم در مورد اجرای آن در اوبونتو بحث کنیم. ذکر این نکته بسیار مهم است که باینری های لینوکس PyPy فقط در توزیع های لینوکس خاص پشتیبانی می شوند. شما می توانید باینری های PyPy موجود و توزیع های پشتیبانی شده آنها را در این صفحه بررسی کنید. به عنوان مثال، PyPy (یا پایتون 2.7 یا پایتون 3.6) فقط برای سه نسخه از اوبونتو پشتیبانی می شود: 18.04، 16.04 و 14.04. اگر جدیدترین نسخه اوبونتو را تا این تاریخ (19.10) دارید، نمی توانید PyPy را روی آن اجرا کنید. تلاش برای اجرای PyPy روی یک توزیع پشتیبانینشده، این خطا را برمیگرداند:
باینری های PyPy به صورت فایل های فشرده می آیند. تنها کاری که باید انجام دهید این است که فایلی را که دانلود کرده اید از حالت فشرده خارج کنید. در داخل پوشه غیرفشرده پوشه ای به نام bin وجود دارد که فایل اجرایی PyPy در آن یافت می شود. من از Python 3.6 استفاده می کنم و بنابراین فایل pypy3 نام دارد. برای Python 2.7، فقط pypy نامیده می شود.
برای CPython، اگر می خواهید پایتون 3 را از ترمینال اجرا کنید، به سادگی دستور python3 را وارد کنید. برای اجرای PyPy کافیست دستور pypy3 را صادر کنید.
همانطور که در شکل بعدی نشان داده شده است، با وارد کردن دستور pypy3 در ترمینال ممکن است پیام فرمان ‘pypy3’ not found را برگرداند. دلیل آن این است که مسیر PyPy به متغیر محیطی PATH اضافه نشده است. دستوری که در واقع کار می کند ./pypy3 است، با توجه به اینکه مسیر فعلی ترمینال در داخل دایرکتوری bin PyPy است. نقطه . به دایرکتوری فعلی اشاره می کند و / برای دسترسی به چیزی در دایرکتوری فعلی اضافه می شود. با صدور دستور ./pypy3 پایتون را با موفقیت اجرا میکند.
اکنون می توانید با استفاده از مزایای PyPy طبق معمول با پایتون کار کنید. به عنوان مثال، میتوانیم یک اسکریپت ساده پایتون ایجاد کنیم که 1000 عدد را جمع میکند و آن را با استفاده از PyPy اجرا کنیم. کد به شرح زیر است.
nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)
اگر این اسکریپت test.py نام دارد، می توانید به سادگی آن را با استفاده از دستور زیر اجرا کنید (با فرض اینکه فایل پایتون در داخل پوشه bin PyPy قرار دارد که همان محل دستور pypy3 است).
./pypy3 test.py
شکل بعدی نتیجه اجرای کد قبلی را نشان می دهد.
زمان اجرای PyPy در مقابل CPython
برای مقایسه زمان اجرای PyPy و CPython برای جمع 1000 عدد، کد برای اندازه گیری زمان به صورت زیر تغییر می کند.
import time
t1 = time.time()
nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)
t2 = time.time()
t = t2 - t1
print("Elapsed time is : ", t, " seconds")
برای PyPy زمان نزدیک به 0.00045 ثانیه است، در مقایسه با 0.0002 ثانیه برای CPython (من کد را روی دستگاه Core i7-6500U خود با فرکانس 2.5 گیگاهرتز اجرا کردم). در این مورد CPython در مقایسه با PyPy زمان کمتری می برد، که قابل انتظار است زیرا این کار واقعاً یک کار طولانی مدت نیست. اگر کد به جای 1000، 1 میلیون عدد را اضافه کند، در نهایت PyPy برنده خواهد شد. در این حالت برای Pypy 0.00035 ثانیه و برای CPython 0.1 ثانیه طول می کشد. مزیت PyPy اکنون آشکار است. این باید به شما ایده دهد که CPython چقدر برای اجرای کارهای طولانی مدت کندتر است.
نتیجه
این آموزش PyPy، سریعترین پیاده سازی پایتون را معرفی می کند. مزیت اصلی PyPy کامپایل آن بهموقع (JIT) است که کد ماشین کامپایل شده را برای جلوگیری از اجرای مجدد آن ذخیره میکند. محدودیتهای PyPy نیز برجسته شدهاند، یکی از اصلیترین آنها این است که برای کدهای پایتون خالص به خوبی کار میکند اما برای پسوندهای C کارآمد نیست.
ما همچنین نحوه اجرای PyPy در اوبونتو را دیدیم و زمان اجرای هر دو CPython و PyPy را با هم مقایسه کردیم و کارایی PyPy را برای کارهای طولانی مدت برجسته کردیم. در همین حال، CPython ممکن است همچنان PyPy را برای کارهای کوتاه مدت شکست دهد. در مقالات آینده مقایسه های بیشتری بین PyPy، CPython و Cython را بررسی خواهیم کرد.