باسلام و عرض ادب. در این مطلب در خدمت شما هستیم با مقاله معرفی زبان برنامه نویسی Cython از وب سایت آموزش برنامه نویسی سورس باران. cython (به فارسی : سایتون ) یک زبان برنامه نویسی برای توسعه ماژول پایتون میباشد. نحوه کار زبان برنامه نویسی سایتون به این شکل است که ابتدا برنامه نویس ماژول خود را با قوانین نوشتاری سایتون و با پسوند .pyx مینویسد سپس یک فایل برای نصب آن با پایتون میسازد که سایتون را فراخوانی کرده باشد سایتون ماژول را به C ترجمه میکند و متن توسط کامپایلر تبدیل به ماژول قابل استفاده میشود. لطفا تا انتها با ما همراه باشید…
زبان برنامه نویسی Cython
سایتون (Cython) توسعه داده شده است تا امکان ساخت افزونههای C را برای پایتون سادهتر کند و به کدهای موجود پایتون اجازه دهد تا به C تبدیل شوند. علاوه بر این، سایتون این امکان را برای کدهای بهینه شده فراهم میکند تا بدون وابستگیهای خارجی با پایتون همراه شوند. در این مقاله ما مراحلی را که لازم است تا کدهای موجود پایتون به سایتون تبدیل شده و در یک اپلیکیشن کاربردی مورد استفاده قرار گیرند، بررسی خواهیم کرد.پایتون یک زبان برنامه نویسی قدرتمند است که یادگیری و کار با آن حتی برای افراد مبتدی راحت است، اما از طرفی همیشه اجرای آن به ویژه وقتی با ریاضیات و آمار سر و کار دارید با سرعت انجام نمیشود. کتابخانههای ثالثی مثل NumPy که کتابخانههای C را پوشش میدهد میتواند عملکرد بعضی از عملیاتها را به طور قابل توجهی بهبود بخشد، اما بعضی اوقات شما مستقیما به سرعت خام و قدرت C در پایتون نیاز دارید.
این یک مثال ساده برای ساخت یک ماژول با سایتون است که بتواند عبارت “hello world” را چاپ کند. این متن اصلی ماژول است که قرار است توسط سایتون به C ترجمه شود.
# hello.pyx def say_hello(): print "Hello World!"
این فایل نصبی است که سایتون را فراخوانی میکند
# setup.py
from distutils.core import setup
from Cython.Build import cythonize
setup(name = 'Hello world app',
ext_modules = cythonize("*.pyx"))
این فایلی است که ماژول را در پایتون فراخوانی میکند.
# launch.py
# This code is always interpreted, like normal Python.
# It is not compiled to C.
import hello
hello.say_hello()
سپس این دو دستور در خط فرمان (sh/bash) ماژول را ساخته و فراخوانی میکنند
مثالی ۲ از برنامه نویسی Cython
اجازه دهید در ابتدا با یک مثال ساده که از اسناد سایتون گرفته شده کار را آغاز کنیم، یک پیاده سازی نه چندان کارآمد از یک تابع انتگرال :
def f(x): return x**2-x def integrate_f(a, b, N): s = 0 dx = (b-a)/N for i in range(N): s += f(a+i*dx) return s * dx
خواندن و درک این کد راحت است، اما اجرای آن به کندی صورت میگیرد. این به دلیل آن است که پایتون باید به طور دائم بین انواع شیهای خود و انواع مقادیر عددی ماشین در رفت و آمد باشد. حالا نسخه سایتون همین کد را در نظر بگیرید که در اینجا زیر بخشهای اضافه شده سایتون خط کشیده شده است :
cdef f(double x): return x**2-x def integrate_f(double a, double b, int N): cdef int i cdef double s, x, dx s = 0 dx = (b-a)/N for i in range(N): s += f(a+i*dx) return s * dx
این بخشهای اضافی به ما اجازه میدهد در میان این کد صریحا انواعی از متغیرها را تعریف کنیم، به این شکل کامپایلر سایتون میتواند این بخشهای اضافه را به C تفسیر کند.
نوع توابع cdef و cpdef
کلیدواژه cdef استفاده از نوع سایتون یا C را مشخص میکند. همچنین از آن برای تعریف توابعی که شما مایلید در پایتون داشته باشید، استفاده میشود. توابعی که در سایتون با استفاده از کلیدواژه پایتون def نوشته میشوند برای سایر کدهای پایتون قابل دسترس هستند، اما با محدودیتهای وضعیت عملکرد پایتون مواجه میشوند. توابعی که از کلیدواژه cdef استفاده میکنند تنها برای سایر کدهای سایتون و C قابل دسترس هستند، اما خیلی سریعتر اجرا میشوند. اگر شما توابعی دارید که تنها به صورت داخلی در خود ماژول سایتون فراخوانی میشوند، از cdef استفاده کنید.
سومین کلیدواژه cpdef است که امکان سازگاری با کدهای پایتون و کدهای C را به شیوهای که کدهای C میتوانند با حداکثر سرعت به این تابع دسترسی داشته باشد فراهم میکند. هر چند این سهولت در کار پیامدهایی را نیز در پی دارد: توابع cpdef کدهای بیشتری تولید میکنند و نسبت به cdef فراخوانی آنها منابع بیشتری نیاز دارد. سایر کلیدواژههای سایتون سایر کلمات کلیدی در سایتون کنترل جنبههای مختلف چرخه برنامه را که در پایتون وجود ندارد، برعهده دارند:
- Gil و nogil مدیریت منابعی را برعهده دارند که برای معین کردن بخشهایی از کد که به Global Interpreter Lock پایتون نیاز دارد (با gil:) یا نیاز ندارد (با nogil:) استفاده میشود. کدهای C که هیچ فراخوانی به API پایتون ندارند، در یک بلوک nogil سریعتر اجرا میشوند، به ویژه اگر یک عملیات طولانی را اجرا کنند.
- از cimport برای نظارت بر ورود نوع داده، تابع، متغیرها و نوع افزونه C به سایتون استفاده میشود. برای نمونه، اپلیکیشنهای سایتون که از ماژول NumPy اصلی C استفاده میکنند، از cimport برای فراهم کردن امکان دسترسی به این توابع استفاده میکنند.
- Include کد منبع یک فایل سایتون را به همان شیوه موجود در C به کدهای دیگر اضافه میکند. توجه داشته باشید که سایتون از یک روش پیچیدهتری برای اشتراک گذاشتن عبارات بین فایلهای سایتون نسبت به include استفاده میکند.
- از ctypedef برای ارجاع به هدر فایلهای خارجی C استفاده میشود.
- از Extern با cdef استفاده میشود تا به توابع C یا متغیرهای پیدا شده در سایر ماژولها مراجعه کند.
- از public/api استفاده میشود تا عباراتی را در ماژولهای سایتون ایجاد کند که میتوان در سایر کدهای C به آن دسترسی داشت.
- از inline استفاده میشود تا یک تابع را به صورت خطی تعریف کرد، و یا کدهای آن را به منظور افزایش سرعت در بدنه تابع فراخوانی شده به کار گرفت. برای نمونه تابع f در کد مثال بالا میتواند به inline مجهز شود تا سرعت فراخوانی را افزایش دهد، زیرا از آن تنها در یک مکان استفاده میشود.
نیازی نیست که پیشاپیش از تمام کلمات کلیدی سایتون آگاه باشید. کدهای سایتون به شکلی در نظر گرفته شدهاند که میتوان آنها را به تدریج نوشت. ابتدا شما کد پایتون را مینویسید، سپس ضمایم سایتون را به منظور افزایش سرعت به آن اضافه میکنید. به این شکل شما میتوانید به تدریج کلیدواژه فرامین دستوری سایتون را برداشته و بر اساس نیاز خود استفاده کنید.
کامپایل Cython
حالا که با نحوه عملکرد یک برنامه ساده سایتون آشنا شدید، در ادامه قصد داریم مراحل مورد نیاز را برای کامپایل سایتون به یک فایل باینری قابل اجرا بررسی کنیم. برای ساخت یک برنامه قابل اجرای سایتون ما به سه چیز نیاز خواهیم داشت:
- مفسر پایتون، در صورت امکان از آخرین نسخه منتشر شده استفاده کنید.
- شما میتوانید با استفاده از روش مدیر پکیج pip به صورت pip install cython سایتون را به پایتون اضافه کنید.
- یک کامپایلر C
اگر شما از ویندوز مایکروسافت به عنوان پلتفرم توسعه خود استفاده میکنید باید گزینه شماره ۳ را مد نظر داشته باشید. برخلاف لینوکس، ویندوز به عنوان یک تجهیزات استاندارد به کامپایلر C مجهز نیست. برای دستیابی به این ویژگی یک کپی از Microsoft Visual Studio Community Edition انتخاب کنید که با کامپایلر C مایکروسافت همراه است و هزینهای برای شما به همراه ندارد. برنامههای سایتون از پسوند فایل .pyx استفاده میکنند. در یک دایرکتوری جدید یک فایل به نام num.pyx ایجاد کنید که شامل کد سایتون نمایش داده شده در مثال بالا است و یک فایل دیگر به نام main.py بسازید که شامل کد زیر است :
from num import integrate_f print (integrate_f(1.0, 10.0, 2000))
این یک برنامه عادی پایتون است که تابع integrate_f موجود در num.pyx را فراخوانی میکند. کد پایتون با کد سایتون تنها به شکل یک ماژول دیگر برخورد میکند، بنابراین شما به جز وارد کردن این ماژول کامپایل شده و اجرای توابع آن نیاز نیست کار خاص دیگری انجام دهید.
سرانجام، یک فایل دیگر با نام setup.py با کد زیر اضافه کنید:
from distutils.core import setup from distutils.extension import Extension from Cython.Build import cythonize ext_modules = [ Extension( r’num’, [r’num.pyx’] ), ] setup( name=’num’, ext_modules=cythonize(ext_modules), )
setup.py در حالت عادی توسط پایتون برای نصب ماژول همراه با آن استفاده میشود و همچنین میتوان از آن برای کامپایل مستقیم افزونههای C در پایتون استفاده کرد. در اینجا ما از setup.py برای کامپایل کدهای سایتون استفاده میکنیم.
اگر شما از لینوکس استفاده میکنید و یک کامپایلر C نصب شده دارید میتوانید فایل .pyx را با اجرای این فرمان به C کامپایل کنید:
> python setup.py build_ext --inplace
اگر از ویندوز استفاده میکنید باید یک فایل بچ به نام compile.bat اضافه کنید تا فرآیند کامپایل را خودکارسازی کند:
@SETLOCAL set DISTUTILS_USE_SDK=1 call “C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat” amd64 python setup.py build_ext --inplace --compiler=msvc
توجه داشته باشید که مسیر درست به vcvarsall.bat در خط ۳ به نسخه ویژوال استودیو که شما نصب کردهاید، بستگی دارد. فرض این مثال بر این است که شما از Visual Studio 2017 Community استفاده کردهاید. اگر عمل کامپایل با موفقیت انجام شود، شما باید فایلهای جدیدی را در این دایرکتوری مشاهده کنید: num.c (فایل C تولید شده توسط سایتون) و یک فایل با پسوند .o (در لینوکس ) یا .pyd (در ویندوز). این همان فایل باینری است که فایل C به آن کامپایل شده است. شما ممکن است یک دایرکتوری به نام \build را نیز مشاهده کنید که شامل مصنوعاتی از فرآیند ساخت است. فرمان python main.py را اجرا کنید تا به عنوان پاسخ برگشتی چیزی شبیه به این را مشاهده کنید:
۲۸۳٫۲۹۷۵۳۰۳۷۵
این همان خروجی تابع انتگرال کامپایل شده که توسط کد اصلی پایتون ما فراخوانی شده است. با پارامترهای این تابع در main.py بازی کنید تا ببینید این خروجی چگونه تغییر میکند. توجه داشته باشید هر زمان که شما تغییراتی را به فایل .pyx اعمال میکنید باید آن را دوباره کامپایل کنید. مسلما هر تغییری که شما روی کدهای معمول پایتون اعمال کنید فورا تاثیرگذار خواهد بود.نتیجه فایل کامپایل شده هیچ وابستگی بجز نسخه پایتونی که برای آن کامپایل شده ندارد و به همین دلیل میتوان آن را به یک binary wheel الصاق کرد. توجه داشته باشید که اگر شما در کد خود به سایر کتابخانهها مثل NumPy (در ادامه به آن خواهیم پرداخت) ارجاع داشته باشید باید آنها را به عنوان بخشی از عناصر مورد نیاز اپلیکیشن خود فراهم کنید.
چگونه از سایتون استفاده کنیم
حالا که متوجه شدیم چگونه میتوان یک کد را به سایتون تبدیل کرد، مرحله بعدی این است که ببینیم چگونه اپلیکیشن پایتون شما میتواند از مزایای سایتون استفاده کند. و دقیقا کجا باید آن را به کار بگیرید؟ برای دریافت نتیجه بهتر، از سایتون برای بهینه سازی این نوع از توابع پایتون استفاده کنید :
- توابعی که در چرخههای فشرده اجرا میشوند یا در یک بخش مهم و حیاتی از یک کد نیاز به مقدار زیادی زمان پردازش دارند.
- توابعی که به انجام اعمال عددی میپردازند.
- توابعی که با شیهایی کار میکنند که در زبان C وجود دارند. مثل انواع عددی، آرایهها یا ساختارها به جای انواع شیهای پایتون مثل لیستها، دیکشنریها یا تاپلها.
پایتون نسبت به سایر زبانهای غیر تفسیری اصولا در چرخهها و انجام اعمال عددی کارایی پایینتری دارد. هر چه شما بیشتر از انواع عددی تبدیل شده به C در کد خود استفاده کنید، محاسبات عددی سریعتر انجام خواهد شد. استفاده از انواع شیهای پایتون در سایتون به خودی خود مشکلساز نیست. توابع سایتون که از شیهای پایتون استفاده میکنند همچنان کامپایل میشوند و زمانی که عملکرد اولویت اصلی نیست شیهای پایتون ترجیح داده میشوند. اما هر کدی که از شیهای پایتون استفاده میکند با مشکلات عملکرد ذاتی پایتون مواجه میشود زیرا سایتون کدها را مستقیم بر اساس API و ABIهای پایتون تولید خواهد کرد. یکی دیگر از اهداف ارزشمند بهینهسازی با سایتون کدهای پایتونی است که مستقیم با یک کتابخانه C در تعامل است. شما میتوانید از کد wrapper پایتون صرف نظر کرده و مستقیم با کتابخانهها در ارتباط باشید. اما سایتون نمیتواند به طور خودکار فراخوانی مناسب را برای ارتباط با این کتابخانهها تولید کند. شما باید سایتون را به شکلی تنظیم کنید که در هدر فایلهای کتابخانهها به توابع ارجاع داده شود. روش انجام این کار استفاده از دستور cdef extern from است.
یکی از کتابخانههای خارجی C که سایتون میتواند مستقیم از آن استفاده کند NumPy است. برای بهرهمند شدن از دسترسی سریع سایتون به آرایههای NumPy از cimport numpy استفاده کنید و سپس از دستورالعملهای cdef برای مشخص کردن متغیرهای NumPy از قبیل cdef np.array یا np.ndarray استفاده کنید.
توضیحات انگلیسی و منابع
What is Cython? Python at the speed of C
Python has a reputation for being one of the most convenient, richly outfitted, and downright useful programming languages. Execution speed? Not so much.
Enter Cython. The Cython language is a superset of Python that compiles to C, yielding performance boosts that can range from a few percent to several orders of magnitude, depending on the task at hand. For work that is bound by Python’s native object types, the speedups won’t be large. But for numerical operations, or any operations not involving Python’s own internals, the gains can be massive. This way, many of Python’s native limitations can be routed around or transcended entirely.
With Cython, you can skirt many of Python’s native limitations or transcend them entirely—without having to give up Python’s ease and convenience. In this article, we’ll walk through the basic concepts behind Cython and create a simple Python application that uses Cython to accelerate one of its functions.
Table of Contents
- Compile Python to C
- How to use Cython
- Cython advantages
- Cython limitations
- Cython NumPy
Compile Python to C
Python code can make calls directly into C modules. Those C modules can be either generic C libraries or libraries built specifically to work with Python. Cython generates the second kind of module: C libraries that talk to Python’s internals, and that can be bundled with existing Python code.
Cython code looks a lot like Python code, by design. If you feed the Cython compiler a Python program, it will accept it as-is, but none of Cython’s native accelerations will come into play. But if you decorate the Python code with type annotations in Cython’s special syntax, Cython will be able to substitute fast C equivalents for slow Python objects.
Note that Cython’s approach is incremental. That means a developer can begin with an existing Python application, and speed it up by making spot changes to the code, rather than rewriting the whole application from the ground up.
This approach dovetails with the nature of software performance issues generally. In most programs, the vast majority of CPU-intensive code is concentrated in a few hot spots—a version of the Pareto principle, also known as the “۸۰/۲۰” rule. Thus most of the code in a Python application doesn’t need to be performance-optimized, just a few critical pieces. You can incrementally translate those hot spots into Cython, and so get the performance gains you need where it matters most. The rest of the program can remain in Python for the convenience of the developers.
How to use Cython
Consider the following code, taken from Cython’s documentation:
Python has a reputation for being one of the most convenient, richly outfitted, and downright useful programming languages. Execution speed? Not so much.
Enter Cython. The Cython language is a superset of Python that compiles to C, yielding performance boosts that can range from a few percent to several orders of magnitude, depending on the task at hand. For work that is bound by Python’s native object types, the speedups won’t be large. But for numerical operations, or any operations not involving Python’s own internals, the gains can be massive. This way, many of Python’s native limitations can be routed around or transcended entirely.
With Cython, you can skirt many of Python’s native limitations or transcend them entirely—without having to give up Python’s ease and convenience. In this article, we’ll walk through the basic concepts behind Cython and create a simple Python application that uses Cython to accelerate one of its functions.
Table of Contents
- Compile Python to C
- How to use Cython
- Cython advantages
- Cython limitations
- Cython NumPy
Compile Python to C
Python code can make calls directly into C modules. Those C modules can be either generic C libraries or libraries built specifically to work with Python. Cython generates the second kind of module: C libraries that talk to Python’s internals, and that can be bundled with existing Python code.
Cython code looks a lot like Python code, by design. If you feed the Cython compiler a Python program, it will accept it as-is, but none of Cython’s native accelerations will come into play. But if you decorate the Python code with type annotations in Cython’s special syntax, Cython will be able to substitute fast C equivalents for slow Python objects.
Note that Cython’s approach is incremental. That means a developer can begin with an existing Python application, and speed it up by making spot changes to the code, rather than rewriting the whole application from the ground up.
This approach dovetails with the nature of software performance issues generally. In most programs, the vast majority of CPU-intensive code is concentrated in a few hot spots—a version of the Pareto principle, also known as the “۸۰/۲۰” rule. Thus most of the code in a Python application doesn’t need to be performance-optimized, just a few critical pieces. You can incrementally translate those hot spots into Cython, and so get the performance gains you need where it matters most. The rest of the program can remain in Python for the convenience of the developers.
How to use Cython
Consider the following code, taken from Cython’s documentation:
<span class="kwd">def</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">x</span><span class="pun">):</span> <span class="pln"> </span><span class="kwd">return</span><span class="pln"> x</span><span class="pun">**</span><span class="lit">2</span><span class="pun">-</span><span class="pln">x</span> <span class="kwd">def</span><span class="pln"> integrate_f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">,</span><span class="pln"> b</span><span class="pun">,</span><span class="pln"> N</span><span class="pun">):</span> <span class="pln"> s </span><span class="pun">=</span> <span class="lit">0</span> <span class="pln"> dx </span><span class="pun">=</span> <span class="pun">(</span><span class="pln">b</span><span class="pun">-</span><span class="pln">a</span><span class="pun">)/</span><span class="pln">N</span> <span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">N</span><span class="pun">):</span> <span class="pln"> s </span><span class="pun">+=</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">+</span><span class="pln">i</span><span class="pun">*</span><span class="pln">dx</span><span class="pun">)</span> <span class="pln"> </span><span class="kwd">return</span><span class="pln"> s </span><span class="pun">*</span><span class="pln"> dx</span>
This is a toy example, a not-very-efficient implementation of an integral function. As pure Python code, it’s slow, because Python must convert back and forth between machine-native numerical types and its own internal object types.
Now consider the Cython version of the same code, with Cython’s additions underscored:
Python has a reputation for being one of the most convenient, richly outfitted, and downright useful programming languages. Execution speed? Not so much.
Enter Cython. The Cython language is a superset of Python that compiles to C, yielding performance boosts that can range from a few percent to several orders of magnitude, depending on the task at hand. For work that is bound by Python’s native object types, the speedups won’t be large. But for numerical operations, or any operations not involving Python’s own internals, the gains can be massive. This way, many of Python’s native limitations can be routed around or transcended entirely.
With Cython, you can skirt many of Python’s native limitations or transcend them entirely—without having to give up Python’s ease and convenience. In this article, we’ll walk through the basic concepts behind Cython and create a simple Python application that uses Cython to accelerate one of its functions.
Table of Contents
- Compile Python to C
- How to use Cython
- Cython advantages
- Cython limitations
- Cython NumPy
Compile Python to C
Python code can make calls directly into C modules. Those C modules can be either generic C libraries or libraries built specifically to work with Python. Cython generates the second kind of module: C libraries that talk to Python’s internals, and that can be bundled with existing Python code.
Cython code looks a lot like Python code, by design. If you feed the Cython compiler a Python program, it will accept it as-is, but none of Cython’s native accelerations will come into play. But if you decorate the Python code with type annotations in Cython’s special syntax, Cython will be able to substitute fast C equivalents for slow Python objects.
Note that Cython’s approach is incremental. That means a developer can begin with an existing Python application, and speed it up by making spot changes to the code, rather than rewriting the whole application from the ground up.
This approach dovetails with the nature of software performance issues generally. In most programs, the vast majority of CPU-intensive code is concentrated in a few hot spots—a version of the Pareto principle, also known as the “۸۰/۲۰” rule. Thus most of the code in a Python application doesn’t need to be performance-optimized, just a few critical pieces. You can incrementally translate those hot spots into Cython, and so get the performance gains you need where it matters most. The rest of the program can remain in Python for the convenience of the developers.
How to use Cython
Consider the following code, taken from Cython’s documentation:
def f(x):
return x**2-x
def integrate_f(a, b, N):
s = 0
dx = (b-a)/N
for i in range(N):
s += f(a+i*dx)
return s * dx
This is a toy example, a not-very-efficient implementation of an integral function. As pure Python code, it’s slow, because Python must convert back and forth between machine-native numerical types and its own internal object types.
Now consider the Cython version of the same code, with Cython’s additions underscored:
cdef f(double x):
return x**2-x
def integrate_f(double a, double b, int N):
cdef int i
cdef double s, x, dx
s = 0
dx = (b-a)/N
for i in range(N):
s += f(a+i*dx)
return s * dx
If we explicitly declare the variable types, both for the function parameters and the variables used in the body of the function (double
, int
, etc.), Cython will translate all of this into C. We can also use the cdef
keyword to define functions that are implemented primarily in C for additional speed, although those functions can only be called by other Cython functions and not by Python scripts.
Cython advantages
Aside from being able to speed up the code you’ve already written, Cython grants several other advantages:
Working with external C libraries can be faster
Python packages like NumPy wrap C libraries in Python interfaces to make them easy to work with. However, going back and forth between Python and C through those wrappers can slow things down. Cython lets you talk to the underlying libraries directly, without Python in the way. (C++ libraries are also supported.)
You can use both C and Python memory management
If you use Python objects, they’re memory-managed and garbage-collected the same as in regular Python. But if you want to create and manage your own C-level structures, and use malloc
/free
to work with them, you can do so. Just remember to clean up after yourself.
You can opt for safety or speed as needed
Cython automatically performs runtime checks for common problems that pop up in C, such as out-of-bounds access on an array, by way of decorators and compiler directives (e.g., @boundscheck(False)
). Consequently, C code generated by Cython is much safer by default than hand-rolled C code.
If you’re confident you won’t need those checks at runtime, you can disable them for additional speed gains, either across an entire module or only on select functions.
Cython also allows you to natively access Python structures that use the “buffer protocol” for direct access to data stored in memory (without intermediate copying). Cython’s “memoryviews” let you work with those structures at high speed, and with the level of safety appropriate to the task.
Cython C code can benefit from releasing the GIL
Python’s Global Interpreter Lock, or GIL, synchronizes threads within the interpreter, protecting access to Python objects and managing contention for resources. But the GIL has been widely criticized as a stumbling block to a better-performing Python, especially on multicore systems.
If you have a section of code that makes no references to Python objects and performs a long-running operation, you can mark it with the with nogil:
directive to allow it to run without the GIL. This frees up the Python interpeter to do other things, and allows Cython code to make use of multiple cores (with additional work).
Cython can use Python type hinting syntax
Python has a type-hinting syntax that is used mainly by linters and code checkers, rather than the CPython interpreter. Cython has its own custom syntax for code decorations, but with recent revisions of Cython you can use Python type-hinting syntax to provide type hints to Cython as well.
Cython limitations
Keep in mind that Cython isn’t a magic wand. It doesn’t automatically turn every instance of poky Python code into sizzling-fast C code. To make the most of Cython, you must use it wisely—and understand its limitations:
Little speedup for conventional Python code
When Cython encounteres Python code it can’t translate completely into C, it transforms that code into a series of C calls to Python’s internals. This amounts to taking Python’s interpreter out of the execution loop, which gives code a modest 15 to 20 percent speedup by default. Note that this is a best-case scenario; in some situations, you might see no performance improvement, or even a performance degradation.
Little speedup for native Python data structures
Python provides a slew of data structures—strings, lists, tuples, dictionaries, and so on. They’re hugely convenient for developers, and they come with their own automatic memory management. But they’re slower than pure C.
Cython lets you continue to use all of the Python data structures, although without much speedup. This is, again, because Cython simply calls the C APIs in the Python runtime that create and manipulate those objects. Thus Python data structures behave much like Cython-optimized Python code generally: You sometimes get a boost, but only a little.
Cython code runs fastest when “pure C”
If you have a function in C labeled with the cdef
keyword, with all of its variables and inline function calls to other things that are pure C, it will run as fast as C can go. But if that function references any Python-native code, like a Python data structure or a call to an internal Python API, that call will be a performance bottleneck.
Fortunately, Cython provides a way to spot these bottlenecks: a source code report that shows at a glance which parts of your Cython app are pure C and which parts interact with Python. The better optimized the app, the less interaction there will be with Python.
A source code report generated for a Cython application. Areas in white are pure C; areas in yellow show interaction with Python’s internals. A well-optimized Cython program will have as little yellow as possible. The expanded last line shows the C code underyling its corresponding Cython code.
Cython NumPy
Cython improves the use of C-based third-party number-crunching libraries like NumPy. Because Cython code compiles to C, it can interact with those libraries directly, and take Python’s bottlenecks out of the loop.
But NumPy, in particular, works well with Cython. Cython has native support for specific constructions in NumPy and provides fast access to NumPy arrays. And the same familiar NumPy syntax you’d use in a conventional Python script can be used in Cython as-is.
However, if you want to create the closest possible bindings between Cython and NumPy, you need to further decorate the code with Cython’s custom syntax. The cimport
statement, for instance, allows Cython code to see C-level constructs in libraries at compile time for the fastest possible bindings.
Since NumPy is so widely used, Cython supports NumPy “out of the box.” If you have NumPy installed, you can just state cimport numpy
in your code, then add further decoration to use the exposed functions.
Cython profiling and performance
You get the best performance from any piece of code by profiling it and seeing firsthand where the bottlenecks are. Cython provides hooks for Python’s cProfile module, so you can use Python’s own profiling tools to see how your Cython code performs. No need to switch between toolsets; you can continue working in the Python world you know and love.
It helps to remember in all cases that Cython isn’t magic—that sensible real-world performance practices still apply. The less you shuttle back and forth between Python and Cython, the faster your app will run.
For instance, if you have a collection of objects you want to process in Cython, don’t iterate over it in Python and invoke a Cython function at each step. Pass the entire collection to your Cython module and iterate there. This technique is used often in libraries that manage data, so it’s a good model to emulate in your own code.
We use Python because it provides programmer convenience and enables fast development. Sometimes that programmer productivity comes at the cost of performance. With Cython, just a little extra effort can give you the best of both worlds.
If we explicitly declare the variable types, both for the function parameters and the variables used in the body of the function (double
, int
, etc.), Cython will translate all of this into C. We can also use the cdef
keyword to define functions that are implemented primarily in C for additional speed, although those functions can only be called by other Cython functions and not by Python scripts.
Cython advantages
Aside from being able to speed up the code you’ve already written, Cython grants several other advantages:
Working with external C libraries can be faster
Python packages like NumPy wrap C libraries in Python interfaces to make them easy to work with. However, going back and forth between Python and C through those wrappers can slow things down. Cython lets you talk to the underlying libraries directly, without Python in the way. (C++ libraries are also supported.)
You can use both C and Python memory management
If you use Python objects, they’re memory-managed and garbage-collected the same as in regular Python. But if you want to create and manage your own C-level structures, and use malloc
/free
to work with them, you can do so. Just remember to clean up after yourself.
You can opt for safety or speed as needed
Cython automatically performs runtime checks for common problems that pop up in C, such as out-of-bounds access on an array, by way of decorators and compiler directives (e.g., @boundscheck(False)
). Consequently, C code generated by Cython is much safer by default than hand-rolled C code.
If you’re confident you won’t need those checks at runtime, you can disable them for additional speed gains, either across an entire module or only on select functions.
Cython also allows you to natively access Python structures that use the “buffer protocol” for direct access to data stored in memory (without intermediate copying). Cython’s “memoryviews” let you work with those structures at high speed, and with the level of safety appropriate to the task.
Cython C code can benefit from releasing the GIL
Python’s Global Interpreter Lock, or GIL, synchronizes threads within the interpreter, protecting access to Python objects and managing contention for resources. But the GIL has been widely criticized as a stumbling block to a better-performing Python, especially on multicore systems.
If you have a section of code that makes no references to Python objects and performs a long-running operation, you can mark it with the with nogil:
directive to allow it to run without the GIL. This frees up the Python interpeter to do other things, and allows Cython code to make use of multiple cores (with additional work).
Cython can use Python type hinting syntax
Python has a type-hinting syntax that is used mainly by linters and code checkers, rather than the CPython interpreter. Cython has its own custom syntax for code decorations, but with recent revisions of Cython you can use Python type-hinting syntax to provide type hints to Cython as well.
Cython limitations
Keep in mind that Cython isn’t a magic wand. It doesn’t automatically turn every instance of poky Python code into sizzling-fast C code. To make the most of Cython, you must use it wisely—and understand its limitations:
Little speedup for conventional Python code
When Cython encounteres Python code it can’t translate completely into C, it transforms that code into a series of C calls to Python’s internals. This amounts to taking Python’s interpreter out of the execution loop, which gives code a modest 15 to 20 percent speedup by default. Note that this is a best-case scenario; in some situations, you might see no performance improvement, or even a performance degradation.
Little speedup for native Python data structures
Python provides a slew of data structures—strings, lists, tuples, dictionaries, and so on. They’re hugely convenient for developers, and they come with their own automatic memory management. But they’re slower than pure C.
Cython lets you continue to use all of the Python data structures, although without much speedup. This is, again, because Cython simply calls the C APIs in the Python runtime that create and manipulate those objects. Thus Python data structures behave much like Cython-optimized Python code generally: You sometimes get a boost, but only a little.
Cython code runs fastest when “pure C”
If you have a function in C labeled with the cdef
keyword, with all of its variables and inline function calls to other things that are pure C, it will run as fast as C can go. But if that function references any Python-native code, like a Python data structure or a call to an internal Python API, that call will be a performance bottleneck.
Fortunately, Cython provides a way to spot these bottlenecks: a source code report that shows at a glance which parts of your Cython app are pure C and which parts interact with Python. The better optimized the app, the less interaction there will be with Python.
A source code report generated for a Cython application. Areas in white are pure C; areas in yellow show interaction with Python’s internals. A well-optimized Cython program will have as little yellow as possible. The expanded last line shows the C code underyling its corresponding Cython code.
Cython NumPy
Cython improves the use of C-based third-party number-crunching libraries like NumPy. Because Cython code compiles to C, it can interact with those libraries directly, and take Python’s bottlenecks out of the loop.
But NumPy, in particular, works well with Cython. Cython has native support for specific constructions in NumPy and provides fast access to NumPy arrays. And the same familiar NumPy syntax you’d use in a conventional Python script can be used in Cython as-is.
However, if you want to create the closest possible bindings between Cython and NumPy, you need to further decorate the code with Cython’s custom syntax. The cimport
statement, for instance, allows Cython code to see C-level constructs in libraries at compile time for the fastest possible bindings.
Since NumPy is so widely used, Cython supports NumPy “out of the box.” If you have NumPy installed, you can just state cimport numpy
in your code, then add further decoration to use the exposed functions.
Cython profiling and performance
You get the best performance from any piece of code by profiling it and seeing firsthand where the bottlenecks are. Cython provides hooks for Python’s cProfile module, so you can use Python’s own profiling tools to see how your Cython code performs. No need to switch between toolsets; you can continue working in the Python world you know and love.
It helps to remember in all cases that Cython isn’t magic—that sensible real-world performance practices still apply. The less you shuttle back and forth between Python and Cython, the faster your app will run.
For instance, if you have a collection of objects you want to process in Cython, don’t iterate over it in Python and invoke a Cython function at each step. Pass the entire collection to your Cython module and iterate there. This technique is used often in libraries that manage data, so it’s a good model to emulate in your own code.
We use Python because it provides programmer convenience and enables fast development. Sometimes that programmer productivity comes at the cost of performance. With Cython, just a little extra effort can give you the best of both worlds.
This is a toy example, a not-very-efficient implementation of an integral function. As pure Python code, it’s slow, because Python must convert back and forth between machine-native numerical types and its own internal object types.
Now consider the Cython version of the same code, with Cython’s additions underscored:
<span class="pln">cdef</span> <span class="kwd">double</span><span class="pln"> f</span><span class="pun">(</span><span class="kwd">double</span><span class="pln"> x</span><span class="pun">):</span> <span class="pln"> </span><span class="kwd">return</span><span class="pln"> x</span><span class="pun">**</span><span class="lit">2</span><span class="pun">-</span><span class="pln">x</span> <span class="kwd">def</span><span class="pln"> integrate_f</span><span class="pun">(</span><span class="kwd">double</span><span class="pln"> a</span><span class="pun">,</span> <span class="kwd">double</span><span class="pln"> b</span><span class="pun">,</span> <span class="kwd">int</span><span class="pln"> N</span><span class="pun">):</span> <span class="pln"> </span><span class="pln">cdef </span><span class="kwd">int</span><span class="pln"> i</span> <span class="pln"> </span><span class="pln">cdef </span><span class="kwd">double</span><span class="pln"> s</span><span class="pun">,</span><span class="pln"> x</span><span class="pun">,</span><span class="pln"> dx</span> <span class="pln"> s </span><span class="pun">=</span> <span class="lit">0</span> <span class="pln"> dx </span><span class="pun">=</span> <span class="pun">(</span><span class="pln">b</span><span class="pun">-</span><span class="pln">a</span><span class="pun">)/</span><span class="pln">N</span> <span class="pln"> </span><span class="kwd">for</span><span class="pln"> i </span><span class="kwd">in</span><span class="pln"> range</span><span class="pun">(</span><span class="pln">N</span><span class="pun">):</span> <span class="pln"> s </span><span class="pun">+=</span><span class="pln"> f</span><span class="pun">(</span><span class="pln">a</span><span class="pun">+</span><span class="pln">i</span><span class="pun">*</span><span class="pln">dx</span><span class="pun">)</span> <span class="pln"> </span><span class="kwd">return</span><span class="pln"> s </span><span class="pun">*</span><span class="pln"> dx</span>
If we explicitly declare the variable types, both for the function parameters and the variables used in the body of the function (double
, int
, etc.), Cython will translate all of this into C. We can also use the cdef
keyword to define functions that are implemented primarily in C for additional speed, although those functions can only be called by other Cython functions and not by Python scripts.
Cython advantages
Aside from being able to speed up the code you’ve already written, Cython grants several other advantages:
Working with external C libraries can be faster
Python packages like NumPy wrap C libraries in Python interfaces to make them easy to work with. However, going back and forth between Python and C through those wrappers can slow things down. Cython lets you talk to the underlying libraries directly, without Python in the way. (C++ libraries are also supported.)
You can use both C and Python memory management
If you use Python objects, they’re memory-managed and garbage-collected the same as in regular Python. But if you want to create and manage your own C-level structures, and use malloc
/free
to work with them, you can do so. Just remember to clean up after yourself.
You can opt for safety or speed as needed
Cython automatically performs runtime checks for common problems that pop up in C, such as out-of-bounds access on an array, by way of decorators and compiler directives (e.g., @boundscheck(False)
). Consequently, C code generated by Cython is much safer by default than hand-rolled C code.
If you’re confident you won’t need those checks at runtime, you can disable them for additional speed gains, either across an entire module or only on select functions.
Cython also allows you to natively access Python structures that use the “buffer protocol” for direct access to data stored in memory (without intermediate copying). Cython’s “memoryviews” let you work with those structures at high speed, and with the level of safety appropriate to the task.
Cython C code can benefit from releasing the GIL
Python’s Global Interpreter Lock, or GIL, synchronizes threads within the interpreter, protecting access to Python objects and managing contention for resources. But the GIL has been widely criticized as a stumbling block to a better-performing Python, especially on multicore systems.
If you have a section of code that makes no references to Python objects and performs a long-running operation, you can mark it with the with nogil:
directive to allow it to run without the GIL. This frees up the Python interpeter to do other things, and allows Cython code to make use of multiple cores (with additional work).
Cython can use Python type hinting syntax
Python has a type-hinting syntax that is used mainly by linters and code checkers, rather than the CPython interpreter. Cython has its own custom syntax for code decorations, but with recent revisions of Cython you can use Python type-hinting syntax to provide type hints to Cython as well.
Cython limitations
Keep in mind that Cython isn’t a magic wand. It doesn’t automatically turn every instance of poky Python code into sizzling-fast C code. To make the most of Cython, you must use it wisely—and understand its limitations:
Little speedup for conventional Python code
When Cython encounteres Python code it can’t translate completely into C, it transforms that code into a series of C calls to Python’s internals. This amounts to taking Python’s interpreter out of the execution loop, which gives code a modest 15 to 20 percent speedup by default. Note that this is a best-case scenario; in some situations, you might see no performance improvement, or even a performance degradation.
Little speedup for native Python data structures
Python provides a slew of data structures—strings, lists, tuples, dictionaries, and so on. They’re hugely convenient for developers, and they come with their own automatic memory management. But they’re slower than pure C.
Cython lets you continue to use all of the Python data structures, although without much speedup. This is, again, because Cython simply calls the C APIs in the Python runtime that create and manipulate those objects. Thus Python data structures behave much like Cython-optimized Python code generally: You sometimes get a boost, but only a little.
Cython code runs fastest when “pure C”
If you have a function in C labeled with the cdef
keyword, with all of its variables and inline function calls to other things that are pure C, it will run as fast as C can go. But if that function references any Python-native code, like a Python data structure or a call to an internal Python API, that call will be a performance bottleneck.
Fortunately, Cython provides a way to spot these bottlenecks: a source code report that shows at a glance which parts of your Cython app are pure C and which parts interact with Python. The better optimized the app, the less interaction there will be with Python.
Cython NumPy
Cython improves the use of C-based third-party number-crunching libraries like NumPy. Because Cython code compiles to C, it can interact with those libraries directly, and take Python’s bottlenecks out of the loop.
But NumPy, in particular, works well with Cython. Cython has native support for specific constructions in NumPy and provides fast access to NumPy arrays. And the same familiar NumPy syntax you’d use in a conventional Python script can be used in Cython as-is.
However, if you want to create the closest possible bindings between Cython and NumPy, you need to further decorate the code with Cython’s custom syntax. The cimport
statement, for instance, allows Cython code to see C-level constructs in libraries at compile time for the fastest possible bindings.
Since NumPy is so widely used, Cython supports NumPy “out of the box.” If you have NumPy installed, you can just state cimport numpy
in your code, then add further decoration to use the exposed functions.
Cython profiling and performance
You get the best performance from any piece of code by profiling it and seeing firsthand where the bottlenecks are. Cython provides hooks for Python’s cProfile module, so you can use Python’s own profiling tools to see how your Cython code performs. No need to switch between toolsets; you can continue working in the Python world you know and love.
It helps to remember in all cases that Cython isn’t magic—that sensible real-world performance practices still apply. The less you shuttle back and forth between Python and Cython, the faster your app will run.
For instance, if you have a collection of objects you want to process in Cython, don’t iterate over it in Python and invoke a Cython function at each step. Pass the entire collection to your Cython module and iterate there. This technique is used often in libraries that manage data, so it’s a good model to emulate in your own code.
We use Python because it provides programmer convenience and enables fast development. Sometimes that programmer productivity comes at the cost of performance. With Cython, just a little extra effort can give you the best of both worlds.
The post معرفی زبان برنامه نویسی Cython appeared first on آموزش برنامه نویسی.