راه های زیادی برای افزایش عملکرد برنامه پایتون وجود دارد. در اینجا ۱۰ نکته کدنویسی سخت برای پایتون سریعتر آورده شده است.
به طور کلی، مردم از Python استفاده می کنند زیرا راحت و برنامه نویس پسند است، نه به این دلیل که سریع است. انبوهی از کتابخانه های شخص ثالث و وسعت پشتیبانی صنعتی از پایتون به شدت به دلیل نداشتن عملکرد خام Java یا C جبران می کند. سرعت توسعه بر سرعت اجرا ارجحیت دارد.
اما در بسیاری از موارد، لزومی ندارد که یک یا/یا گزاره باشد. برنامههای پایتون با بهینهسازی مناسب میتوانند با سرعت شگفتانگیزی اجرا شوند – شاید نه به سرعت جاوا یا C، اما به اندازه کافی برای برنامههای کاربردی وب، تجزیه و تحلیل دادهها، ابزارهای مدیریت و اتوماسیون و بسیاری از اهداف دیگر سریع اجرا شوند. با بهینهسازیهای مناسب، حتی ممکن است متوجه تعادل بین عملکرد برنامه و بهرهوری برنامهنویس نشوید.
بهینه سازی عملکرد پایتون به هیچ عاملی بستگی ندارد. در عوض، این در مورد اعمال تمام بهترین شیوه های موجود و انتخاب آنهایی است که به بهترین وجه با سناریوی موجود مطابقت دارند. (افرادی که در Dropbox هستند یکی از چشمگیرترین نمونه ها از قدرت را دارند. از بهینه سازی های پایتون.)
در این مقاله، ۱۰ بهینه سازی رایج پایتون را مورد بحث قرار می دهم. برخی از معیارهای کشویی هستند که نیاز به کمی بیشتر از تغییر یک آیتم به مورد دیگر دارند (مانند تغییر مفسر پایتون). دیگران بازدهی بزرگتری ارائه میکنند اما به کار دقیقتری نیز نیاز دارند.
۱۰ روش برای اجرای سریعتر برنامه های پایتون
- اندازه گیری، اندازه گیری، اندازه گیری
- داده های مکرر استفاده شده را به خاطر بسپارید (کش)
- ریاضی را به NumPy منتقل کنید
- ریاضی را به Numba منتقل کنید
- از کتابخانه C استفاده کنید
- تبدیل به Cython
- موازی با چند پردازش بروید
- بدانید که کتابخانه های شما چه می کنند
- بدانید پلتفرم شما در حال انجام چه کاری است
- اجرا با PyPy
اندازه گیری، اندازه گیری، اندازه گیری
طبق ضرب المثل قدیمی، نمی توانید چیزی را که اندازه نمی گیرید از دست بدهید. به همین ترتیب، نمیتوانید بفهمید که چرا برنامههای پایتون بهطور غیربهینه اجرا میشود، بدون اینکه بدانید کندی در کجاست.
با نمایه سازی ساده از طریق ماژول cProfile
داخلی پایتون شروع کنید و به پروفایلر قدرتمندتر. اغلب، بینشهایی که با بازرسی سطح عملکرد پایه از یک برنامه به دست میآیند، چشماندازی بیش از اندازه کافی ارائه میدهند. (شما می توانید داده های نمایه را برای یک تابع از طریق profilehooks
ماژول بکشید. )
چرا یک بخش خاص از برنامه بسیار کند است و نحوه تعمیر آن ممکن است نیاز به حفاری بیشتری داشته باشد. نکته این است که تمرکز را محدود کنید، یک خط پایه با اعداد سخت ایجاد کنید، و هر زمان که ممکن است در مورد انواع سناریوهای استفاده و استقرار آزمایش کنید. پیش از موعد بهینه سازی نکنید حدس زدن شما را به جایی نمی رساند.
مثال Dropbox (پیوند داده شده در بالا) نشان می دهد که پروفایل چقدر مفید است. توسعهدهندگان نوشتند: «این اندازهگیری بود که به ما گفت که فرار از HTML در ابتدا آهسته بود، و بدون اندازهگیری عملکرد، هرگز حدس نمیزدیم که درونیابی رشتهای تا این حد کند بوده است».
داده های مکرر استفاده شده را به خاطر بسپارید (کش)
هرگز کار را هزار بار انجام ندهید، وقتی می توانید آن را یک بار انجام دهید و نتایج را ذخیره کنید. اگر تابعی دارید که به طور مکرر نامیده می شود و نتایج قابل پیش بینی را برمی گرداند، پایتون گزینه هایی را برای ذخیره نتایج در حافظه در اختیار شما قرار می دهد. تماسهای بعدی که نتیجه یکسانی را نشان میدهند تقریباً بلافاصله برمیگردند.
نمونه های مختلف نحوه انجام این کار را نشان می دهد. حافظه نویسی مورد علاقه من تقریباً به همان اندازه که می شود. اما پایتون این قابلیت را دارد. یکی از کتابخانههای بومی پایتون، functools
، دارای @functools.lru_cache
دکوراتور، که n جدیدترین تماسهای یک تابع را در حافظه پنهان ذخیره میکند. این زمانی مفید است که مقداری که در حافظه پنهان ذخیره می کنید تغییر کند اما در یک پنجره زمانی خاص نسبتا ثابت است. فهرستی از مواردی که اخیراً استفاده شده است در طول یک روز مثال خوبی خواهد بود.
توجه داشته باشید که اگر مطمئن هستید که تنوع تماسهای تابع در یک محدوده معقول باقی میماند (مثلاً ۱۰۰ نتیجه مختلف در حافظه پنهان)، میتوانید از @functools.cache، که عملکرد بیشتری دارد.
ریاضی را به NumPy منتقل کنید
اگر ریاضی مبتنی بر ماتریس یا آرایه انجام میدهید و نمیخواهید مفسر پایتون مانع شود، از NumPy استفاده کنید. NumPy با استفاده از کتابخانه های C برای کارهای سنگین، پردازش آرایه سریع تری را نسبت به پایتون بومی ارائه می دهد. همچنین داده های عددی را کارآمدتر از ساختارهای داده داخلی پایتون ذخیره می کند.
یکی دیگر از مزایای NumPy استفاده کارآمدتر از حافظه برای اشیاء بزرگ، مانند لیستهایی با میلیونها آیتم است. به طور متوسط، اشیاء بزرگ مانند NumPy در صورتی که در پایتون معمولی بیان شوند، حدود یک چهارم حافظه مورد نیاز را اشغال می کنند. توجه داشته باشید که به شروع با ساختار داده مناسب برای یک شغل کمک می کند – که به خودی خود یک بهینه سازی است .
بازنویسی الگوریتمهای پایتون برای استفاده از NumPy کمی کار میبرد زیرا اشیاء آرایه باید با استفاده از نحو NumPy اعلام شوند. بعلاوه، بزرگترین افزایشها از طریق استفاده از تکنیکهای «پخشسازی» اختصاصی NumPy به دست میآید، که در آن یک تابع یا رفتار در یک آرایه اعمال میشود. وقت بگذارید و به بررسی اسناد NumPy بپردازید تا دریابید که چه عملکردهایی در دسترس هستند و چگونه از آنها به خوبی استفاده کنید.
همچنین، در حالی که NumPy برای شتاب دادن به ریاضیات مبتنی بر ماتریس یا آرایه مناسب است، سرعت مفیدی برای ریاضیات انجام شده خارج از آرایه ها یا ماتریس های NumPy ارائه نمی دهد. ریاضیاتی که شامل اشیاء معمولی پایتون میشوند، افزایش سرعت را مشاهده نمیکنند.
ریاضی را به Numba منتقل کنید
یکی دیگر از کتابخانه های قدرتمند برای افزایش سرعت عملیات ریاضی Numba است. مقداری کد پایتون برای دستکاری عددی بنویسید و آن را با کامپایلر JIT Numba (فقط به موقع) بپیچید و کد حاصل با سرعت اصلی ماشین اجرا می شود. Numba نه تنها شتابهای مبتنی بر GPU (هم CUDA و هم ROC) را ارائه میکند، بلکه دارای یک حالت خاص «nopython
» است که سعی میکند با تکیه نکردن به مفسر پایتون، عملکرد را به حداکثر برساند. هر کجا که ممکن است.
Numba همچنین با NumPy دست در دست هم کار میکند، بنابراین میتوانید بهترینها را از هر دو دنیا دریافت کنید—NumPy برای همه عملیاتهایی که میتواند حل کند و Numba برای بقیه.
از کتابخانه C استفاده کنید
استفاده NumPy از کتابخانه های نوشته شده در C استراتژی خوبی برای تقلید است. اگر یک کتابخانه C موجود است که آنچه شما نیاز دارید انجام می دهد، پایتون و اکوسیستم آن چندین گزینه برای اتصال به کتابخانه و افزایش سرعت آن ارائه می دهند.
متداولترین راه برای انجام این کار، کتابخانه ctypes پایتون است. از آنجایی که ctypes
به طور گسترده با سایر برنامه های کاربردی پایتون (و زمان اجرا) سازگار است، بهترین مکان برای شروع است، اما با تنها بازی در شهر فاصله زیادی دارد. پروژه CFFI رابط ظریف تری را برای C. Cython فراهم می کند (به پایین مراجعه کنید) همچنین می توان از آن استفاده کرد برای نوشتن کتابخانههای C خود یا بستهبندی کتابخانههای خارجی موجود، البته به قیمت یادگیری نشانهگذاری Cython.
یک نکته در اینجا: با به حداقل رساندن تعداد سفرهای رفت و برگشتی که در مرز بین C و پایتون انجام می دهید، بهترین نتایج را دریافت خواهید کرد. هر بار که دادهها را بین آنها ارسال میکنید، این یک عملکرد عالی است. اگر انتخابی بین فراخوانی یک کتابخانه C در یک حلقه تنگ در مقابل ارسال کل ساختار داده به کتابخانه C و انجام پردازش درون حلقه در آنجا دارید، گزینه دوم را انتخاب کنید. رفت و برگشت کمتری بین دامنهها انجام خواهید داد.
تبدیل به Cython
اگر سرعت میخواهید، از C استفاده کنید نه پایتون. اما برای Pythonistas، نوشتن کد C مجموعهای از حواسپرتیها را به همراه دارد – یادگیری نحو C، بحث و جدل در زنجیره ابزار C (چه مشکلی با فایلهای هدر من اکنون وجود دارد؟)، و غیره.
Cython به کاربران پایتون اجازه می دهد تا به راحتی به سرعت C دسترسی داشته باشند. کد موجود پایتون را می توان به صورت تدریجی به C تبدیل کرد—ابتدا با کامپایل کد مذکور به C با Cython، سپس با افزودن حاشیه نویسی نوع برای سرعت بیشتر.
سایتون یک عصای جادویی نیست. کد تبدیل شده به Cython، بدون حاشیهنویسی، معمولاً بیش از ۱۵ تا ۵۰ درصد سریعتر اجرا نمیشود. این به این دلیل است که بیشتر بهینهسازیها در آن سطح بر کاهش سربار مفسر پایتون تمرکز دارند. بزرگترین دستاوردها زمانی حاصل می شود که متغیرهای شما را بتوان به عنوان نوع C حاشیه نویسی کرد – به عنوان مثال، یک عدد صحیح ۶۴ بیتی در سطح ماشین به جای نوع int
پایتون. سرعتهای حاصل میتوانند مرتبهای سریعتر باشند.
کدهای متصل به CPU بیشترین سود را از Cython میبرند. اگر پروفایل کرده اید (پروفایل کرده اید، اینطور نیست؟) و متوجه شده اید که بخش های خاصی از کد شما از اکثریت قریب به اتفاق زمان CPU استفاده می کند، آن ها کاندیدای عالی برای تبدیل Cython هستند. کدهایی که به ورودی/خروجی محدود میشوند، مانند عملیات طولانیمدت شبکه، سود کمی از Cython میبینند یا هیچ سودی ندارند.
مانند استفاده از کتابخانه های C، نکته مهم دیگر برای افزایش عملکرد این است که تعداد رفت و برگشت به Cython را به حداقل برسانید. حلقه ای ننویسید که یک تابع “Cythonized” را به طور مکرر فراخوانی کند. حلقه را در Cython پیاده سازی کنید و داده ها را به یکباره ارسال کنید.
موازی با چند پردازش پیش بروید
برنامههای سنتی پایتون – آنهایی که در CPython پیادهسازی میشوند – در هر زمان فقط یک رشته را اجرا میکنند تا از مشکلات حالتی که هنگام استفاده از رشتههای متعدد به وجود میآیند جلوگیری شود. این قفل بدنام جهانی مترجم (GIL) است. دلایل خوبی برای وجود آن وجود دارد، اما این باعث نمیشود که کمتر دلخراش باشد.
یک برنامه CPython میتواند چند رشتهای باشد، اما به دلیل GIL، CPython واقعاً اجازه نمیدهد آن رشتهها به صورت موازی روی چندین هسته اجرا شوند. GIL در طول زمان به طرز چشمگیری کارآمدتر شده است، و کارهایی برای حذف کامل آن در حال انجام است، اما در حال حاضر مشکل اصلی همچنان باقی است.
یک راهحل رایج، ماژول چندپردازش است که چندین نمونه از مفسر پایتون در هسته های جداگانه. حالت را می توان از طریق حافظه مشترک یا فرآیندهای سرور به اشتراک گذاشت و داده ها را می توان بین نمونه های فرآیند از طریق صف یا لوله منتقل کرد.
هنوز باید حالت را به صورت دستی بین فرآیندها مدیریت کنید. بعلاوه، برای شروع چند نمونه پایتون و ارسال اشیا از میان آنها، هزینه کمی وجود ندارد. اما برای فرآیندهای طولانیمدت که از موازیسازی میان هستهها بهره میبرند، کتابخانه چند پردازشی مفید است.
بهعلاوه، ماژولها و بستههای پایتون که از کتابخانههای C استفاده میکنند (مانند NumPy یا Cython) میتوانند به طور کامل از GIL اجتناب کنند. این دلیل دیگری است که آنها برای افزایش سرعت توصیه می شوند.
بدانید که کتابخانه های شما چه می کنند
چقدر راحت است که به سادگی include foobar
را تایپ کنید و به کار برنامه نویسان بی شمار دیگر ضربه بزنید! اما باید توجه داشته باشید که کتابخانه های شخص ثالث می توانند عملکرد برنامه شما را تغییر دهند، نه همیشه برای بهتر شدن.
گاهی اوقات این به روشهای واضحی ظاهر میشود، مانند زمانی که یک ماژول از یک کتابخانه خاص یک گلوگاه ایجاد میکند. (باز هم، نمایه سازی کمک خواهد کرد.) گاهی اوقات این امر کمتر آشکار است. برای مثال، Pyglet را در نظر بگیرید، یک کتابخانه مفید برای ایجاد برنامه های گرافیکی پنجره ای. Pyglet به طور خودکار یک حالت اشکال زدایی را فعال می کند، که به طور چشمگیری بر عملکرد تأثیر می گذارد تا زمانی که صریحاً غیرفعال شود. ممکن است هرگز متوجه این موضوع نشوید مگر اینکه اسناد کتابخانه را بخوانید، بنابراین وقتی با کتابخانه جدیدی شروع به کار کردید، بخوانید و مطلع شوید.
بدانید پلتفرم شما در حال انجام چه کاری است
Python چند پلتفرم اجرا میشود، اما این بدان معنا نیست که ویژگیهای هر سیستم عامل (ویندوز، لینوکس، macOS) در پایتون کاملاً انتزاع شده است. بیشتر اوقات، آگاهی از ویژگیهای پلتفرم مانند قراردادهای نامگذاری مسیر، که توابع کمکی برای آنها وجود دارد، سودمند است. برای مثال، ماژول pathlib، قراردادهای مسیر خاص پلتفرم را انتزاعی می کند. کنترل کنسول نیز بین ویندوز و سایر سیستم عامل ها بسیار متفاوت است. بنابراین محبوبیت کتابخانه های انتزاعی مانند rich.
در برخی از پلتفرمها، ویژگیهای خاصی اصلاً پشتیبانی نمیشوند، و این میتواند بر نحوه نوشتن پایتون تأثیر بگذارد. به عنوان مثال، ویندوز مفهوم انشعاب فرآیند را ندارد، بنابراین برخی از عملکردهای چند پردازشی در آنجا متفاوت عمل می کنند.
در نهایت، نحوه نصب و اجرای خود پایتون بر روی پلتفرم نیز مهم است. برای مثال، در لینوکس، pip
معمولاً جدا از پایتون نصب میشود. در ویندوز، به طور خودکار با پایتون نصب می شود.
اجرا با PyPy
CPython، رایجترین پیادهسازی پایتون، سازگاری را بر سرعت خام اولویت میدهد. برای برنامه نویسانی که می خواهند سرعت را در اولویت قرار دهند، PyPy وجود دارد، یک پیاده سازی پایتون مجهز به یک کامپایلر JIT برای تسریع در اجرای کد.
از آنجایی که PyPy به عنوان جایگزینی برای CPython طراحی شده است، یکی از سادهترین راهها برای افزایش سریع عملکرد است. بسیاری از برنامه های رایج پایتون دقیقاً همانطور که هستند روی PyPy اجرا می شوند. به طور کلی، هرچه برنامه بیشتر به Python “vanilla” متکی باشد، به احتمال زیاد بدون تغییر روی PyPy اجرا می شود.
با این حال، استفاده از بهترین مزیت PyPy ممکن است نیاز به آزمایش و مطالعه داشته باشد. متوجه خواهید شد که برنامه های طولانی مدت بیشترین دستاوردهای عملکرد را از PyPy می گیرند، زیرا کامپایلر اجرا را در طول زمان تجزیه و تحلیل می کند تا نحوه سرعت بخشیدن به کارها را تعیین کند. برای اسکریپتهای کوتاهی که صرفاً اجرا و خارج میشوند، احتمالاً بهتر است از CPython استفاده کنید، زیرا افزایش عملکرد برای غلبه بر سربار JIT کافی نخواهد بود.
توجه داشته باشید که پشتیبانی PyPy از Python نسبت به جدیدترین نسخههای زبان عقب مانده است. زمانی که پایتون ۳.۱۲ جاری بود، PyPy فقط تا نسخه ۳.۱۰ را پشتیبانی می کرد. همچنین، برنامههای پایتون که از ctypes
استفاده میکنند ممکن است همیشه آنطور که انتظار میرود رفتار نکنند. اگر در حال نوشتن چیزی هستید که ممکن است در PyPy و CPython اجرا شود، ممکن است منطقی باشد که موارد استفاده را به طور جداگانه برای هر مترجم انجام دهید.
پست های مرتبط
۱۰ نکته برای افزایش سرعت برنامه های پایتون
۱۰ نکته برای افزایش سرعت برنامه های پایتون
۱۰ نکته برای افزایش سرعت برنامه های پایتون