Zawijanie biblioteki C w Pythonie: C, Cython lub ctypes?

284

Chcę wywołać bibliotekę C z aplikacji Python. Nie chcę zawijać całego interfejsu API, tylko funkcje i typy danych, które są odpowiednie w moim przypadku. Widzę, że mam trzy możliwości:

  1. Utwórz rzeczywisty moduł rozszerzeń w C. Prawdopodobnie przesada, a także chciałbym uniknąć narzutu związanego z nauką pisania rozszerzeń.
  2. Użyj Cython, aby udostępnić odpowiednie części z biblioteki C Pythonowi.
  3. Zrób wszystko w Pythonie, używając ctypesdo komunikacji z biblioteką zewnętrzną.

Nie jestem pewien, czy 2) czy 3) jest lepszym wyborem. Zaletą 3) jest to, że ctypesjest częścią standardowej biblioteki, a wynikowy kod byłby czystym Pythonem - chociaż nie jestem pewien, jak duża jest ta zaleta.

Czy są jakieś zalety / wady w obu przypadkach? Jakie podejście polecasz?


Edycja: Dzięki za wszystkie odpowiedzi, stanowią one dobry zasób dla każdego, kto chce zrobić coś podobnego. Oczywiście, decyzja musi zostać jeszcze podjęta w sprawie pojedynczej sprawy - nie ma jednej odpowiedzi typu „to jest właściwa”. W moim przypadku prawdopodobnie użyję ctypów, ale nie mogę się doczekać wypróbowania Cython w innym projekcie.

Ponieważ nie ma jednej prawdziwej odpowiedzi, zaakceptowanie jednej jest nieco arbitralne; Wybrałem odpowiedź FogleBirda, ponieważ zapewnia ona dobry wgląd w typy cypla, a obecnie jest również odpowiedzią najczęściej głosowaną. Proponuję jednak przeczytać wszystkie odpowiedzi, aby uzyskać dobry przegląd.

Dzięki jeszcze raz.

balpha
źródło
3
Do pewnego stopnia określona aplikacja (działanie biblioteki) może wpływać na wybór podejścia. Z powodzeniem używaliśmy ctypów do komunikowania się z bibliotekami DLL dostarczanymi przez dostawcę dla różnych elementów hardare (np. Oscyloskopów), ale niekoniecznie wybrałbym najpierw ctypy do rozmowy z biblioteką przetwarzania numerycznego, ze względu na dodatkowe koszty w porównaniu do Cython lub SWIG.
Peter Hansen
1
Teraz masz to, czego szukałeś. Cztery różne odpowiedzi. (Ktoś też znalazł SWIG). Oznacza to, że teraz masz 4 opcje zamiast 3.
Luka Rahne
@ralu Też tak myślę :-) Ale poważnie, nie spodziewałem się (ani nie chciałem) stołu pro / con ani jednej odpowiedzi z napisem „Oto, co musisz zrobić”. Na wszelkie pytania dotyczące podejmowania decyzji najlepiej odpowiedzieć „fanami” każdego możliwego wyboru, podając powody. Głosowanie społeczności wykonuje swoją część, podobnie jak moja własna praca (patrząc na argumenty, stosując je do mojej sprawy, czytając dostarczone źródła itp.). Krótko mówiąc: tutaj jest kilka dobrych odpowiedzi.
balpha
Więc jakie podejście zamierzasz zastosować? :)
FogleBird,
1
O ile mi wiadomo (proszę o poprawienie, jeśli się mylę), Cython jest widelcem Pyrex z większym rozwojem, co czyni Pyrex bardzo przestarzałym.
balpha

Odpowiedzi:

115

ctypes jest najlepszym rozwiązaniem, aby szybko to zrobić, i przyjemnością jest pracować, gdy wciąż piszesz Python!

Niedawno zapakowałem sterownik FTDI do komunikacji z układem USB za pomocą ctypów i było świetnie. Zrobiłem to wszystko i pracowałem w mniej niż jeden dzień roboczy. (Zaimplementowałem tylko te funkcje, których potrzebowaliśmy, około 15 funkcji).

Wcześniej korzystaliśmy z modułu zewnętrznego, PyUSB , w tym samym celu. PyUSB to rzeczywisty moduł rozszerzeń C / Python. Ale PyUSB nie wypuszczał GIL podczas blokowania odczytów / zapisów, co przysparzało nam problemów. Napisałem więc własny moduł przy użyciu ctypes, który zwalnia GIL podczas wywoływania funkcji natywnych.

Należy zauważyć, że ctypy nie będą wiedziały o #definestałych i innych rzeczach w bibliotece, której używasz, tylko o funkcjach, więc będziesz musiał przedefiniować te stałe we własnym kodzie.

Oto przykład, w jaki sposób kod wyglądał (wiele wycinało, próbując pokazać sedno tego):

from ctypes import *

d2xx = WinDLL('ftd2xx')

OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3

...

def openEx(serial):
    serial = create_string_buffer(serial)
    handle = c_int()
    if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
        return Handle(handle.value)
    raise D2XXException

class Handle(object):
    def __init__(self, handle):
        self.handle = handle
    ...
    def read(self, bytes):
        buffer = create_string_buffer(bytes)
        count = c_int()
        if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
            return buffer.raw[:count.value]
        raise D2XXException
    def write(self, data):
        buffer = create_string_buffer(data)
        count = c_int()
        bytes = len(data)
        if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
            return count.value
        raise D2XXException

Ktoś zrobił kilka testów porównawczych dla różnych opcji.

Byłbym bardziej niezdecydowany, gdybym musiał owinąć bibliotekę C ++ dużą ilością klas / szablonów / etc. Ale ctypes działa dobrze z strukturami i może nawet wywoływać zwrotne w Pythonie.

FogleBird
źródło
5
Dołączanie do pochwał za ctypes, ale zauważ jeden (nieudokumentowany) problem: ctypes nie obsługuje rozwidlenia. Jeśli rozwiniesz się z procesu za pomocą ctypów, a procesy nadrzędne i potomne będą nadal używać ctypów, natkniesz się na paskudny błąd, który ma związek z ctypami korzystającymi z pamięci współdzielonej.
Oren Shemesh
1
@OrenShemesh Czy jest jakaś dalsza lektura na ten temat, którą możesz mi wskazać? Myślę, że mogę być bezpieczny z projektem Obecnie pracuję nad, ponieważ wierzę tylko zastosowania procesu macierzystego ctypes(dla pyinotify), ale chciałbym zrozumieć problem dokładniej.
zigg
Ten fragment bardzo mi pomaga. One thing to note is that ctypes won't know about #define constants and stuff in the library you're using, only the functions, so you'll have to redefine those constants in your own code.Muszę więc zdefiniować stałe, które są w winioctl.h
swdev
co z wydajnością? ctypesjest znacznie wolniejszy niż rozszerzenie c, ponieważ wąskim gardłem jest interfejs Pythona do C
TomSawyer
154

Ostrzeżenie: przed nami opinia twórcy rdzenia Cython.

Prawie zawsze polecam Cython zamiast ctypów. Powodem jest to, że ma znacznie płynniejszą ścieżkę aktualizacji. Jeśli użyjesz ctypów, na początku wiele rzeczy będzie prostych i na pewno fajnie jest napisać kod FFI w zwykłym Pythonie, bez kompilacji, budowania zależności i tym podobnych. Jednak w pewnym momencie prawie na pewno okaże się, że musisz często wywoływać bibliotekę C, albo w pętli, albo w dłuższej serii współzależnych wywołań, i chciałbyś to przyspieszyć. W tym momencie zauważysz, że nie możesz tego zrobić z ctypami. Lub, gdy potrzebujesz funkcji wywołania zwrotnego i okaże się, że Twój kod wywołania w Pythonie staje się wąskim gardłem, chciałbyś go przyspieszyć i / lub przenieść również w dół do C. Ponownie, nie możesz tego zrobić z ctypami.

Z Cython, OTOH, masz całkowitą swobodę, aby owijanie i wywoływanie kodu było tak cienkie lub grube, jak chcesz. Możesz zacząć od prostych wywołań do kodu C ze zwykłego kodu Python, a Cython przetłumaczy je na rodzime wywołania C, bez dodatkowego narzutu i przy bardzo niskim narzutu konwersji parametrów Pythona. Kiedy zauważysz, że potrzebujesz jeszcze większej wydajności w pewnym momencie, w którym wykonujesz zbyt wiele drogich wywołań do swojej biblioteki C, możesz zacząć dodawać adnotacje do otaczającego kodu Python typami statycznymi i pozwolić Cython zoptymalizować go bezpośrednio w C. Lub możesz rozpocząć przepisywanie części kodu C w Cython, aby uniknąć połączeń oraz aby specjalizować się i zaciskać pętle algorytmicznie. A jeśli potrzebujesz szybkiego oddzwonienia, po prostu napisz funkcję z odpowiednim podpisem i przekaż ją bezpośrednio do rejestru zwrotnego C. Ponownie, bez narzutu, i daje to zwykłą wydajność połączeń w języku C. A w znacznie mniej prawdopodobnym przypadku, gdy naprawdę nie możesz uzyskać wystarczająco szybkiego kodu w Cython, nadal możesz rozważyć przepisanie naprawdę krytycznych części w C (lub C ++ lub Fortran) i nazwać go z kodu Cython naturalnie i natywnie. Ale tak naprawdę staje się to ostatecznością zamiast jedynej opcji.

Tak więc ctypes miło jest robić proste rzeczy i szybko coś uruchomić. Jednak jak tylko wszystko zacznie rosnąć, najprawdopodobniej dojdziesz do momentu, w którym zauważysz, że lepiej używać Cython od samego początku.

Stefan Behnel
źródło
4
+1 to są dobre punkty, wielkie dzięki! Chociaż zastanawiam się, czy przeniesienie tylko wąskich gardeł do Cython jest tak dużym obciążeniem. Ale zgadzam się, że jeśli spodziewasz się jakichkolwiek problemów z wydajnością, równie dobrze możesz używać Cython od samego początku.
balpha 16.04.11
Czy nadal dotyczy to programistów doświadczonych zarówno w języku C, jak i Python? W takim przypadku można argumentować, że Python / ctypes jest lepszym wyborem, ponieważ wektoryzacja pętli C (SIMD) jest czasami prostsza. Ale poza tym nie mogę wymyślić żadnych wad Cython.
Alex van Houten
Dziękuję za odpowiedź! Jedną rzeczą, z którą miałem problem w odniesieniu do Cython, jest poprawne wykonanie procesu kompilacji (ale to również ma związek ze mną, że nigdy wcześniej nie pisałem modułu Python) - powinienem go wcześniej skompilować lub dołączyć pliki źródłowe Cython do sdist i podobnych pytań. Napisałem o tym blog na wypadek, gdyby ktoś miał podobne problemy / wątpliwości: martinsosic.com/development/2016/02/08/…
Martinsos
Dziękuję za odpowiedź! Jedną wadą, gdy używam Cython, jest to, że przeciążanie operatora nie jest w pełni zaimplementowane (np __radd__.). Jest to szczególnie denerwujące, gdy planujesz, aby twoja klasa współdziałała z wbudowanymi typami (np. intI float). Ponadto, magiczne metody w cytonie są w ogóle trochę błędne.
Monolith
100

Cython jest całkiem fajnym narzędziem samym w sobie, wartym nauki i zaskakująco zbliżonym do składni Pythona. Jeśli wykonujesz jakiekolwiek obliczenia naukowe za pomocą Numpy, to Cython jest dobrym rozwiązaniem, ponieważ integruje się z Numpy w celu szybkiego działania macierzy.

Cython jest nadzbiorem języka Python. Możesz wrzucić do niego dowolny prawidłowy plik Pythona, a on wypluje prawidłowy program C. W takim przypadku Cython po prostu odwzoruje wywołania Pythona na bazowy interfejs API CPython. Powoduje to może 50% przyspieszenie, ponieważ Twój kod nie jest już interpretowany.

Aby uzyskać optymalizację, musisz zacząć informować Cython o dodatkowych faktach na temat swojego kodu, takich jak deklaracje typów. Jeśli powiesz wystarczająco dużo, może sprowadzić kod do czystego C. To znaczy, że pętla for w Pythonie staje się pętlą for w C. Tutaj zobaczysz znaczny wzrost prędkości. Możesz również link do zewnętrznych programów C.

Korzystanie z kodu Cython jest również niezwykle łatwe. Myślałem, że instrukcja utrudnia to. Dosłownie po prostu robisz:

$ cython mymodule.pyx
$ gcc [some arguments here] mymodule.c -o mymodule.so

a następnie możesz import mymodulew swoim kodzie Python i całkowicie zapomnieć, że kompiluje się do C.

W każdym razie, ponieważ Cython jest tak łatwy w konfiguracji i zaczyna korzystać, sugeruję wypróbowanie go, aby sprawdzić, czy odpowiada Twoim potrzebom. To nie będzie strata, jeśli okaże się, że nie jest narzędziem, którego szukasz.

carl
źródło
1
Nie ma problemu. Zaletą Cython jest to, że możesz nauczyć się tylko tego, czego potrzebujesz. Jeśli chcesz tylko niewielkiej poprawy, wszystko, co musisz zrobić, to skompilować pliki w języku Python i gotowe.
carl
18
„Możesz wrzucić do niego dowolny prawidłowy plik Python, który wypluje prawidłowy program C.” <- Niezupełnie, istnieją pewne ograniczenia: docs.cython.org/src/userguide/limitations.html Prawdopodobnie nie stanowi problemu w większości przypadków użycia, ale po prostu chciałem być kompletny.
Randy Syring
7
Z każdym wydaniem problemów jest coraz mniej, do tego stopnia, że ​​na tej stronie jest teraz napisane: „większość problemów rozwiązano w wersji 0.15”.
Henry Gomersall
3
Aby dodać, istnieje NAJWYŻSZY sposób na importowanie kodu cytonu: napisz kod cytonu jako mymod.pyxmoduł, a następnie zrób to, import pyximport; pyximport.install(); import mymoda kompilacja odbędzie się za kulisami.
Kaushik Ghose
3
@kaushik Jeszcze prostsze jest pypi.python.org/pypi/runcython . Po prostu użyj runcython mymodule.pyx. I w przeciwieństwie do pyximport, możesz go używać do bardziej wymagających zadań łączenia. Jedynym zastrzeżeniem jest to, że jestem tym, który napisał za to 20 linii bash i może być stronniczy.
RussellStewart
42

Do wywoływania biblioteki C z aplikacji Python istnieje także cffi, która jest nową alternatywą dla ctypów . Daje świeży wygląd FFI:

  • radzi sobie z problemem w fascynujący, czysty sposób (w przeciwieństwie do ctypów )
  • nie wymaga pisania kodu innego niż Python (jak w SWIG, Cython , ...)
Robert Zaremba
źródło
zdecydowanie sposób na pakowanie , tak jak chciał OP. cython brzmi świetnie do samodzielnego pisania gorących pętli, ale dla interfejsów cffi jest po prostu ulepszeniem od ctypów.
latające owce
21

Wyrzucę jeszcze jedną: SWIG

Łatwo się go nauczyć, robi wiele rzeczy dobrze i obsługuje wiele innych języków, więc czas spędzony na nauce może być bardzo przydatny.

Jeśli używasz SWIG, tworzysz nowy moduł rozszerzenia Pythona, ale SWIG wykonuje za ciebie większość ciężkich zadań.

Chris Arguin
źródło
18

Osobiście napisałbym moduł rozszerzeń w C. Nie daj się zastraszyć rozszerzeniami Python C - wcale nie są trudne do napisania. Dokumentacja jest bardzo przejrzysta i pomocna. Kiedy po raz pierwszy napisałem rozszerzenie C w Pythonie, myślę, że zajęło mi około godziny, aby wymyślić, jak je napisać - wcale nie dużo czasu.

mipadi
źródło
Zawijanie biblioteki C. Możesz znaleźć kod tutaj: github.com/mdippery/lehmer
mipadi
1
@forivall: Kod nie był tak naprawdę przydatny, a istnieją lepsze generatory liczb losowych. Mam tylko kopię zapasową na moim komputerze.
mipadi
2
Zgoda. C-API Pythona nie jest tak przerażające, jak się wydaje (zakładając, że znasz C). Jednak w przeciwieństwie do Pythona i jego zasobów bibliotek, zasobów i programistów, pisząc rozszerzenia w C, jesteś w zasadzie sam. Prawdopodobnie jest to jedyna wada (inna niż te, które zwykle pochodzą z pisania w C).
Noob Saibot
1
@mipadi: cóż, ale różnią się między Pythonem 2.xi 3.x, więc wygodniej jest użyć Cython do napisania twojego rozszerzenia, poprosić Cython o ustalenie wszystkich szczegółów, a następnie skompilowanie wygenerowanego kodu C dla Python 2.x lub 3.x w razie potrzeby.
0xC0000022L,
2
@mipadi wygląda na to, że link github nie działa i nie jest dostępny na archive.org, czy masz kopię zapasową?
jrh
11

ctypes jest świetny, gdy masz już skompilowany obiekt blob biblioteki do obsługi (taki jak biblioteki systemu operacyjnego). Narzut związany z wywoływaniem jest jednak poważny, więc jeśli będziesz wykonywać wiele wywołań do biblioteki, a mimo to będziesz pisać kod C (a przynajmniej go kompilować), powiedziałbym cython . To niewiele więcej pracy, a użycie wynikowego pliku pyd będzie znacznie szybsze i bardziej pytoniczne.

Ja osobiście używam cytonu do szybkiego przyspieszania kodu pytona (pętle i porównania liczb całkowitych to dwa obszary, w których cyton szczególnie świeci), a kiedy jest więcej zaangażowany kod / zawijanie innych bibliotek, zwrócę się do Boost.Python . Boost.Python może być skomplikowany w konfiguracji, ale gdy już zaczniesz działać, upraszcza pakowanie kodu C / C ++.

cython jest również świetny w pakowaniu numpy (czego dowiedziałem się z postępowania SciPy 2009 ), ale nie użyłem numpy, więc nie mogę tego komentować.

Ryan Ginstrom
źródło
11

Jeśli masz już bibliotekę ze zdefiniowanym interfejsem API, myślę, że ctypesjest to najlepsza opcja, ponieważ wystarczy wykonać niewielką inicjalizację, a następnie mniej więcej wywołać bibliotekę tak, jak kiedyś.

Myślę, że Cython lub tworzenie modułu rozszerzenia w C (co nie jest bardzo trudne) jest bardziej przydatne, gdy potrzebujesz nowego kodu, np. Wywoływanie tej biblioteki i wykonywanie skomplikowanych, czasochłonnych zadań, a następnie przekazywanie wyniku do Pythona.

Inne podejście, w przypadku prostych programów, polega bezpośrednio na wykonaniu innego procesu (skompilowanego zewnętrznie), wypisaniu wyniku na standardowe wyjście i wywołaniu go za pomocą modułu podprocesu. Czasami jest to najłatwiejsze podejście.

Na przykład, jeśli utworzysz program konsoli C, który działa mniej więcej w ten sposób

$miCcode 10
Result: 12345678

Możesz to nazwać z Python

>>> import subprocess
>>> p = subprocess.Popen(['miCcode', '10'], shell=True, stdout=subprocess.PIPE)
>>> std_out, std_err = p.communicate()
>>> print std_out
Result: 12345678

Dzięki niewielkiemu formatowaniu ciągów możesz wziąć wynik w dowolny sposób. Możesz także przechwycić standardowe wyjście błędu, więc jest dość elastyczne.

Khelben
źródło
Chociaż w tej odpowiedzi nie ma nic niepoprawnego, ludzie powinni zachować ostrożność, jeśli kod ma zostać otwarty dla innych jako wywołanie podprocesu z shell=True może łatwością doprowadzić do pewnego rodzaju exploita, gdy użytkownik naprawdę otrzyma powłokę. Jest w porządku, gdy programista jest jedynym użytkownikiem, ale na świecie jest mnóstwo irytujących sztuczek, które tylko czekają na coś takiego.
Ben
7

Jest jeden problem, który kazał mi używać ctypów, a nie cytonów i który nie jest wspomniany w innych odpowiedziach.

Przy użyciu ctypów wynik nie zależy od kompilatora, którego używasz. Możesz napisać bibliotekę, używając mniej więcej dowolnego języka, który można skompilować w natywnej bibliotece współdzielonej. Nie ma większego znaczenia, który system, język i kompilator. Jednak Cython jest ograniczony infrastrukturą. Na przykład, jeśli chcesz używać kompilatora Intel w systemie Windows, znacznie trudniej jest uruchomić cyton: powinieneś „wyjaśnić” kompilator cytonowi, skompilować coś z tym dokładnym kompilatorem itp. Co znacznie ogranicza przenośność.

Misza
źródło
4

Jeśli celujesz w system Windows i chcesz zawinąć niektóre zastrzeżone biblioteki C ++, możesz wkrótce odkryć, że różne wersje msvcrt***.dll (Visual C ++ Runtime) są nieco niezgodne.

Oznacza to, że możesz nie być w stanie używać, Cythonponieważ wynik wrapper.pydjest powiązany z msvcr90.dll (Python 2.7) lub msvcr100.dll (Python 3.x) . Jeśli biblioteka, którą pakujesz, jest powiązana z inną wersją środowiska wykonawczego, oznacza to, że nie masz szczęścia.

Następnie, aby wszystko działało, musisz utworzyć otoki C dla bibliotek C ++, połącz dll otoki z tą samą wersją msvcrt***.dllco twoja biblioteka C ++. A potem użyjctypes aby dynamicznie załadować ręcznie walcowane dll otoki w czasie wykonywania.

Istnieje więc wiele drobnych szczegółów, które zostały szczegółowo opisane w następującym artykule:

„Beautiful Native Libraries (in Python) ”: http://lucumr.pocoo.org/2013/8/18/beautiful-native-libraries/

iljau
źródło
Ten artykuł nie ma nic wspólnego z problemami, które poruszasz przy kompatybilności kompilatorów Microsoft. Uzyskiwanie rozszerzeń Cython działających w systemie Windows naprawdę nie jest bardzo trudne. Byłem w stanie używać MinGW do prawie wszystkiego. Dobra dystrybucja w Pythonie jednak pomaga.
IanH
2
+1 za wzmiankę o możliwym problemie w systemie Windows (który również mam obecnie ...). @IanH ogólnie mniej dotyczy Windows, ale jest bałagan, jeśli utkniesz z daną biblioteką zewnętrzną, która nie pasuje do twojej dystrybucji Pythona.
sebastian
2

Wiem, że to stare pytanie, ale ta sprawa pojawia się w Google, gdy wyszukujesz takie rzeczy ctypes vs cython, a większość odpowiedzi tutaj jest napisana przez tych, którzy są biegli już cythonlub cktórzy mogą nie odzwierciedlać faktycznego czasu, jaki potrzebujesz zainwestować, aby je poznać wdrożyć swoje rozwiązanie. Jestem kompletnym początkującym w obu. Nigdy wcześniej nie dotykałem cythoni mam bardzo małe doświadczeniec/c++ .

Przez ostatnie dwa dni szukałem sposobu na przeniesienie dużej części kodu pod kątem wydajności na coś niższego niż Python. Zaimplementowałem mój kod zarówno w, jak ctypesiCython , który składał się zasadniczo z dwóch prostych funkcji.

Miałem ogromną listę ciągów do przetworzenia. Zawiadomienie listi string. Oba typy nie odpowiadają idealnie typom c, ponieważ ciągi Pythona są domyślnie Unicode, a cciągi nie są. Listy w pythonie po prostu NIE są tablicami c.

Oto mój werdykt. Zastosowanie cython. Bardziej płynnie integruje się z Pythonem i ogólnie ułatwia pracę. Gdy coś pójdzie nie tak, ctypespo prostu rzuca ci segfault, przynajmniej cythonda ci kompilowane ostrzeżenia ze śledzeniem stosu, gdy tylko jest to możliwe, i możesz łatwo zwrócić prawidłowy obiekt python cython.

Oto szczegółowe informacje o tym, ile czasu potrzebowałem na zainwestowanie w nich obu, aby wdrożyć tę samą funkcję. Nawiasem mówiąc, zrobiłem bardzo mało programowania w C / C ++:

  • Typy:

    • Około 2h po zbadaniu, jak przekształcić moją listę łańcuchów Unicode na typ zgodny z AC.
    • Około godziny, w jaki sposób poprawnie zwrócić ciąg z funkcji ac. Tutaj faktycznie podałem własne rozwiązanie SO po napisaniu funkcji.
    • Około pół godziny na napisanie kodu w c, skompiluj go do biblioteki dynamicznej.
    • 10 minut na napisanie kodu testowego w pythonie, aby sprawdzić, czy ckod działa.
    • Około godziny wykonania testów i zmiany ckodu.
    • Następnie podłączyłem ckod do rzeczywistej bazy kodu i zobaczyłem, że ctypesnie działa dobrze z multiprocessingmodułem, ponieważ jego moduł obsługi nie jest domyślnie możliwy do odebrania.
    • Około 20 minut zmieniłem kod, aby nie używać multiprocessingmodułu, i ponowiłem próbę.
    • Następnie druga funkcja w moim c kodzie wygenerowała awarie w mojej bazie kodu, mimo że przeszła przez mój kod testowy. Cóż, to prawdopodobnie moja wina, że ​​nie sprawdziłem dobrze z przypadkowymi przypadkami, szukałem szybkiego rozwiązania.
    • Przez około 40 minut próbowałem ustalić możliwe przyczyny tych segfaultów.
    • Podzieliłem swoje funkcje na dwie biblioteki i spróbowałem ponownie. Wciąż miałem awarie dla mojej drugiej funkcji.
    • Zdecydowałem się puścić drugą funkcję i użyć tylko pierwszej funkcji ckodu, a przy drugiej lub trzeciej iteracji używającej go pętli pytona miałem problem z UnicodeErrornieszyfrowaniem bajtu w pewnej pozycji, chociaż kodowałem i dekodowałem wszystko wyraźnie.

W tym momencie postanowiłem poszukać alternatywy i postanowiłem sprawdzić cython:

  • Cython
    • 10 minut czytania cytonu witaj świecie .
    • 15 minut na sprawdzenie SO, jak używać cytonu setuptoolszamiast distutils.
    • 10 min czytania na temat typów cytonów i typów python. Nauczyłem się, że mogę używać większości wbudowanych typów python do pisania statycznego.
    • 15 minut ponownej adnotacji mojego kodu python z typami cytonów.
    • 10 minut modyfikacji mojego w setup.pycelu użycia skompilowanego modułu w mojej bazie kodu.
    • Podłączony moduł bezpośrednio do multiprocessingwersji bazy kodu. To działa.

Dla przypomnienia, oczywiście nie mierzyłem dokładnego czasu mojej inwestycji. Może się zdarzyć, że mój sposób postrzegania czasu był nieco uważny z powodu wysiłku umysłowego wymaganego podczas pracy z typami. Ale to powinno przekazać atmosferę radzenia sobie z cythonictypes

Kaan E.
źródło