Какво е Cython? Python със скоростта на C

Python има репутацията на един от най-удобните, богато оборудвани и направо полезни езици за програмиране. Скорост на изпълнение? Не толкова.

Въведете Cython. Езикът Cython е надмножество на Python, което се компилира в C, като дава повишаване на производителността, което може да варира от няколко процента до няколко порядъка в зависимост от задачата. За работа, която е свързана с родните типове обекти на Python, ускоренията няма да бъдат големи. Но за числени операции или всякакви операции, които не включват собствените вътрешни елементи на Python, печалбите могат да бъдат огромни. 

С Cython можете да заобиколите много от родните ограничения на Python или да ги надхвърлите изцяло - без да се налага да се отказвате от лекотата и удобството на Python. В тази статия ще разгледаме основните концепции зад Cython и ще създадем просто приложение на Python, което използва Cython за ускоряване на една от функциите си.

Свързано видео: Използване на Cython за ускоряване на Python

Компилирайте Python в C

Python кодът може да извършва обаждания директно в C модули. Тези C модули могат да бъдат или общи C библиотеки, или библиотеки, създадени специално за работа с Python. Cython генерира втория вид модул: C библиотеки, които говорят с вътрешните модули на Python и които могат да бъдат свързани със съществуващия код на Python.

Кодът на Cython изглежда много като Python код по дизайн. Ако захранвате компилатора на Cython с програма на Python (поддържат се и Python 2.x, и Python 3.x), Cython ще го приеме такъв, какъвто е, но нито едно от родните ускорения на Cython няма да влезе в игра. Но ако декорирате кода на Python с типови анотации в специалния синтаксис на Cython, Cython ще може да замести бързи C еквиваленти за бавни обекти на Python.

Имайте предвид, че подходът на Cython е  постепенен . Това означава, че разработчикът може да започне със  съществуващо приложение на Python и да го ускори, като направи точни промени в кода, вместо да пренаписва цялото приложение от нулата.

Този подход съвпада с естеството на проблемите с производителността на софтуера като цяло. В повечето програми по-голямата част от интензивния за процесора код е съсредоточен в няколко горещи точки - версия на принципа на Парето, известен също като правилото „80/20“. По този начин по-голямата част от кода в приложение на Python не трябва да бъде оптимизирана за производителност, а само няколко критични части. Можете постепенно да превеждате тези горещи точки в Cython и така получавате нужната ви производителност там, където е най-важно. Останалата част от програмата може да остане в Python за удобство на разработчиците.

Как да използвам Cython

Помислете за следния код, взет от документацията на Cython:

def f (x):

    връщане x ** 2-x

def integrate_f (a, b, N):

    s = 0

    dx = (ba) / N

    за i в обхват (N):

        s + = f (a + i * dx)

    връщане s * dx

Това е пример за играчка, не много ефективно изпълнение на интегрална функция. Като чист код на Python, той е бавен, защото Python трябва да преобразува напред-назад между машинните цифрови типове и собствените си вътрешни типове обекти.

Сега помислете за версията на Cython на същия код, като добавките на Cython са подчертани:

 cdef double f (double x):

    връщане x ** 2-x

def integrate_f (двойно a, двойно b, int N):

    cdef int i

    cdef двойно s, x, dx

    s = 0

    dx = (ba) / N

    за i в обхват (N):

        s + = f (a + i * dx)

    връщане s * dx

Ако изрично декларираме типовете променливи, както за параметрите на функциите, така и за променливите, използвани в тялото на функцията ( double, intи т.н.), Cython ще преведе всичко това в C. Можем да използваме cdefключовата дума и за дефиниране на функции, които са внедрен предимно в C за допълнителна скорост, въпреки че тези функции могат да бъдат извиквани само от други функции на Cython, но не и от Python скриптове. (В горния пример integrate_fможе да бъде извикан само от друг скрипт на Python.)

Обърнете внимание колко малко се е променил действителният ни  код . Всичко, което направихме, е да добавим декларации за тип към съществуващия код, за да получим значително подобрение на производителността.

Предимства на Cython

Освен че може да ускори кода, който вече сте написали, Cython предоставя още няколко предимства:

Работата с външни C библиотеки може да бъде по-бърза

Пакетите на Python като NumPy обгръщат C библиотеки в интерфейси на Python, за да улеснят работата им. Преминаването напред и назад между Python и C през тези обвивки може да забави нещата. Cython ви позволява директно да говорите с основните библиотеки, без Python да ви пречи. (Поддържат се и библиотеки на C ++.)

Можете да използвате както C, така и управление на паметта на Python

Ако използвате обекти на Python, те се управляват от паметта и се събират боклук по същия начин, както в обикновения Python. Но ако искате да създадете и управлявате свои собствени структури на ниво C и да използвате malloc/ freeда работите с тях, можете да го направите. Само не забравяйте да почистите след себе си.

При необходимост можете да изберете безопасност или скорост 

Cython автоматично извършва проверки по време на изпълнение за често срещани проблеми, които се появяват в C, като достъп извън границите на масив, чрез декоратори и директиви на компилатора (напр. @boundscheck(False)). Следователно, C кодът, генериран от Cython, е много по-безопасен по подразбиране от ръчно валцувания C код, макар и потенциално за сметка на суровата производителност.

Ако сте уверени, че няма да ви трябват тези проверки по време на изпълнение, можете да ги деактивирате за допълнително увеличаване на скоростта или в цял модул, или само в избрани функции.

Cython също така ви позволява да имате достъп до Python структури, които използват буферния протокол за директен достъп до данни, съхранявани в паметта (без междинно копиране). Прегледите на паметта на Cython ви позволяват да работите с тези структури с висока скорост и с нивото на безопасност, подходящо за задачата. Например необработените данни, лежащи в основата на Python низ, могат да се четат по този начин (бързо), без да се налага да преминавате през Python време на изпълнение (бавно).

Кодът на Cython C може да се възползва от освобождаването на GIL

Глобалното заключване на интерпретатора на Python, или GIL, синхронизира нишки в интерпретатора, защитавайки достъпа до обекти на Python и управлявайки спора за ресурси. Но GIL беше широко критикуван като препъни камък за по-добре работещия Python, особено в многоядрени системи.

Ако имате раздел от код, който не прави препратки към обекти на Python и изпълнява продължителна операция, можете да го маркирате с  with nogil:директивата, за да му позволите да работи без GIL. Това освобождава интерпретатора на Python да прави други неща и позволява на кода на Cython да използва множество ядра (с допълнителна работа).

Cython може да използва синтаксис за подсказване на тип Python 

Python има синтаксис за подсказване на типа, който се използва главно от линтери и проверки на код, а не от интерпретатора CPython. Cython има свой собствен персонализиран синтаксис за декорации на кодове, но с последните ревизии на Cython можете да използвате Python синтаксис за подсказване на тип, за да предоставите и основни подсказки за тип на Cython. 

Cython може да се използва за скриване на чувствителния код на Python

Модулите на Python са тривиално лесни за декомпилиране и проверка, но компилираните двоични файлове не са. Когато разпространявате приложение на Python до крайните потребители, ако искате да защитите някои от неговите модули от случайно подслушване, можете да го направите, като ги компилирате с Cython. Имайте предвид обаче, че това е страничен ефект от възможностите на Cython, а не една от предвидените му функции.

Ограничения на цитон

Имайте предвид, че Cython не е вълшебна пръчка. Той не превръща автоматично всеки екземпляр на пикантния код на Python в бърз C код. За да се възползвате максимално от Cython, трябва да го използвате разумно - и да разберете ограниченията му:

Малко ускорение за конвенционален код на Python

Когато Cython срещне Python код, той не може да се преведе напълно в C, той трансформира този код в поредица от C извиквания към вътрешните елементи на Python. Това се свежда до изваждането на интерпретатора на Python от цикъла за изпълнение, което по подразбиране дава код с умерена скорост от 15 до 20 процента. Имайте предвид, че това е най-добрият сценарий; в някои ситуации може да не видите подобрение на производителността или дори влошаване на производителността.

Малко ускорение за родните структури от данни на Python

Python предоставя множество структури от данни - низове, списъци, кортежи, речници и т.н. Те са изключително удобни за разработчиците и идват със собствено автоматично управление на паметта. Но те са по-бавни от чисто C.

Cython ви позволява да продължите да използвате всички структури от данни на Python, макар и без много ускорение. Това е, отново, защото Cython просто извиква C API в изпълнението на Python, които създават и манипулират тези обекти. По този начин структурите на данни на Python се държат подобно на оптимизирания за Cython код на Python като цяло: Понякога получавате тласък, но само малко. За най-добри резултати използвайте C променливи и структури. Добрата новина е, че Cython улеснява работата с тях.

Кодът на Cython работи най-бързо, когато е „чист C“

Ако имате функция в C, обозначена с cdefключовата дума, с всички нейни променливи и вградени извиквания на функции към други неща, които са чисти C, тя ще работи толкова бързо, колкото C може да отиде. Но ако тази функция се позовава на който и да е Python-роден код, като структура на данни на Python или повикване към вътрешен API на Python, това повикване ще бъде пречка за изпълнението.

За щастие, Cython предоставя начин да откриете тези тесни места: доклад за изходния код, който показва с един поглед кои части от вашето приложение Cython са чисти C и кои части взаимодействат с Python. Колкото по-добре е оптимизирано приложението, толкова по-малко взаимодействие ще има с Python.

Cython NumPy 

Cython подобрява използването на C-базирани библиотеки за чупене на номера на трети страни като NumPy. Тъй като кодът на Cython се компилира в C, той може да взаимодейства директно с тези библиотеки и да извади пречките на Python от цикъла.

Но NumPy, по-специално, работи добре с Cython. Cython има естествена поддръжка за специфични конструкции в NumPy и осигурява бърз достъп до масиви NumPy. И същият познат синтаксис на NumPy, който бихте използвали в конвенционален скрипт на Python, може да се използва в Cython такъв, какъвто е.

Ако обаче искате да създадете възможно най-близките обвързвания между Cython и NumPy, трябва допълнително да украсите кода с персонализирания синтаксис на Cython. В  cimportизявление, например, позволява Cython код, за да видите конструкции C-ниво в библиотеките по време на компилация за възможно най-високата автомати.

Тъй като NumPy е толкова широко използван, Cython поддържа NumPy „извън кутията“. Ако имате инсталиран NumPy, можете просто да посочите  cimport numpy кода си, след което да добавите допълнителна украса, за да използвате изложените функции. 

Cython профилиране и производителност

Получавате най-доброто представяне от който и да е парче код, като го профилирате и видите от първа ръка къде са пречките. Cython предоставя куки за модула cProfile на Python, така че можете да използвате собствените инструменти за профилиране на Python, като cProfile, за да видите как се представя вашият код на Cython. 

Помага да се помни във всички случаи, че Cython не е магия - че все още се прилагат разумни практики в реалния свят. Колкото по-малко прехвърляте между Python и Cython, толкова по-бързо ще работи приложението ви.

Например, ако имате колекция от обекти, които искате да обработите в Cython, не итерирайте върху нея в Python и извиквайте функция Cython на всяка стъпка. Предайте цялата колекция на вашия модул Cython и повторете там. Тази техника се използва често в библиотеки, които управляват данни, така че е добър модел да подражавате в собствения си код.

Използваме Python, защото осигурява удобство на програмиста и позволява бързо развитие. Понякога производителността на програмиста се дължи на производителността. С Cython само малко допълнителни усилия могат да ви дадат най-доброто от двата свята.

Прочетете повече за Python

  • Какво е Python? Мощно, интуитивно програмиране
  • Какво е PyPy? По-бърз Python без болка
  • Какво е Cython? Python със скоростта на C
  • Урок за Cython: Как да ускорим Python
  • Как да инсталирате Python по интелигентен начин
  • Най-добрите нови функции в Python 3.8
  • По-добро управление на проекти на Python с поезия
  • Virtualenv и venv: Обяснени са виртуалните среди на Python
  • Python virtualenv и venv правят и не
  • Обяснение на нишките на Python и подпроцесите
  • Как да използвам Python дебъгер
  • Как да използвам timeit за профилиране на Python код
  • Как да използвам cProfile за профилиране на Python код
  • Започнете с async в Python
  • Как да използвам asyncio в Python
  • Как да конвертирате Python в JavaScript (и обратно)
  • Python 2 EOL: Как да оцелеем в края на Python 2
  • 12 Pythons за всяка нужда от програмиране
  • 24 библиотеки на Python за всеки разработчик на Python
  • 7 сладки IDE на Python, които може да сте пропуснали
  • 3 основни недостатъка на Python и техните решения
  • 13 сравнени уеб рамки на Python
  • 4 Тестови рамки на Python, за да смачкате вашите грешки
  • 6 страхотни нови функции на Python, които не искате да пропуснете
  • 5 Python дистрибуции за овладяване на машинно обучение
  • 8 страхотни библиотеки на Python за обработка на естествен език