Kamil Szcześniak
CFFI
C Foreign Function Interface
„mostly done by Armin Rigo with help from Maciej Fijałkowski”
CFFI
Wykonywanie funkcji z zewnętrznych bibliotek C API
Program
w Pythonie
Biblioteka
w napisana w C
Rozszerzanie Pythona o gotowe biblioteki
libFFI
Minimalne API
Definicje języka C
Wykonywanie funkcji z zewnętrznych bibliotek C API
Konkurencyjna biblioteka ctypes
- skomplikowane API
- brak weryfikacji zgodności ABI
Zewnętrzne moduły dla C Pythona
- wymagają kompilacji plików C
- generator SWIG
- Cython - nauka nowego języka
- PyPy tylko eksperymentalnie
CFFI
Instalacja
CPython 2.6 2.7 3.x
PyPy 2.0+
wbudowana
wymaga libffi
sudo apt-get install libffi-dev
sudo pip install cffi
CFFI
Mnimalistyczne API
from cffi import FFI
myunit = FFI()
myunit.cdef("int mylibInitialize(char *p)")
1. inicjalizacja
2. zdefiniowanie API
3. załadowanie biblioteki
mylib= myunit.dlopen("mytest")
wykonanie kodu
ret= mylib.mylibInitialize(sys.argv[1])
Zgodność ABI
brak jakiejkolwiek weryfikacji
CFFI
Mnimalistyczne API
from cffi import FFI
myunit = FFI()
myunit.cdef("int mylibInitialize(char *p)")
1. inicjalizacja
2. zdefiniowanie API
3. załadowanie i weryfikacja biblioteki
mylib= myunit.verify('#include "mylib.h"',libraries=["mylib"])
wykonanie kodu
ret= mylib.mylibInitialize(sys.argv[1])
Wymaga kompilatora przy pierwszym uruchomieniu
CFFI
Mnimalistyczne API
from cffi import FFI
myunit = FFI()
myunit.cdef(open('./mylibcopy.h').read())
1. inicjalizacja
2. zdefiniowanie API
3. załadowanie i weryfikacja biblioteki
mylib= myunit.verify('#include "mylib.h"',libraries=["mylib"])
wykonanie kodu
ret= mylib.mylibInitialize(sys.argv[1])
Wymaga kompilatora przy pierwszym uruchomieniu
CFFI
Przekazywanie struktur danych do C
Program
w Pythonie
Biblioteka
w napisana w C
CData
rzutowanie typów prostych
myunit.cast("int", 123)
myunit.cast("double", 5.5)
myunit.cast("enum MyReturnValue", -1)
myunit.cast("void*", 0x0)
CFFI
Przekazywanie struktur danych do C
Program
w Pythonie
Biblioteka
w napisana w C
CData
przydzielanie pamięci typom wskaźnikowym
i3= myunit.new("short[]", [1,2,3])
c4= myunit.new("char[]", "abc")
w4= myunit.new("wchar_t[]", u"ąbć")
i1= myunit.new("long long*", 10**18)
s1= myunit.new("struct{int x,y;}*", {'x':5, 'y':10})
u1= myunit.new("union{char *a; int *b}*", {'a':c4})
CFFI
Przekazywanie struktur danych do C
Program
w Pythonie
Biblioteka
w napisana w C
CData
tworzenie wskaźników na funkcje
add= myunit.callback("int(int, int)",lambda a,b:a+b)
@myunit.callback("int(int[],int)")
def sumtable(table, size):
ret=0
for i in range(size):
ret += table[i]
return ret
CFFI
Przekazywanie struktur danych do C
Program
w Pythonie
Biblioteka
w napisana w C
CData
Typy proste automatycznie wracają do Pythona
Na wskaźnikach działa arytmetyka
Funkcje naturalnie można wywoływać
Dereferencja poprzez dostęp tablicowy
Dodatkowe operatory sizeof, addressof, offsetof, alignof
CFFI
Przekazywanie struktur danych do C
Program
w Pythonie
Biblioteka
w napisana w C
CData
Wskaźniki na pamięć przydzieloną w C wymagają GC
ptr = myunit.gc(libc.malloc(42), libc.free)
CFFI
Przekazywanie struktur danych do C
Program
w Pythonie
Biblioteka
w napisana w C
CData
Dostęp do ciągu znaków
myunit.cdef( 'char *getSymbol(int);' )
print myunit.string( mylib.getSymbol(666), 100 )
print myunit.string(myunit.cast('enum MyEnum', 7))
CFFI
Przekazywanie struktur danych do C
Program
w Pythonie
Biblioteka
w napisana w C
CData
Dostęp binarny do pamięci poprzez buffor
s= myunit.cast("struct myStruct*", ptr)
buf= myunit.buffer(s)
data = zlib.compress(buf[:])
buf[:] = zlib.decompress(data)
PyPy + CFFI
def algo(tablica)
last=tablica[len(table)-1]
for i,item in enumerate(table):
item.a=i
item.b=last.a+item.a
item.c=last.b+item.a
item.x[:4]=b"0123"
last=item
def algo(tab):
for i in range(len(tab)):
tab[i].a=i
tab[i].b=tab[last].a+tab[i].a
tab[i].c=tab[last].b+tab[i].a
tab[i].x[:4] = b"0123"
last=i
[mS]
[T]
PyPy + CFFI
class Element(object):
__slots__ = ['a','b','c','x','p']
algo([ Element() for x in range(T) ])
algo(ffi.new('struct Element[]', T))
[T]
[mS]
PyPy + CFFI
def algo(tablica)
last=tablica[len(table)-1]
for i,item in enumerate(table):
item.a= i
item.b= last.a+item.a
item.c= last.b+item.a
item.p= last
last= item
def algo(tab):
for i in range(len(tab)):
tab[i].a= i
tab[i].b= tab[last].a+tab[i].a
tab[i].c= tab[last].b+tab[i].a
tab[i].p= tab[last]
last= i
[mS]
[T]
PyPy + CFFI
def algo(tablica)
last=tablica[len(table)-1]
for i,item in enumerate(table):
item.a= i
item.b= last.a + item.a
item.c= last.b + item.a
item.p= ffi.addressof(last)
last=item
def algo(tab):
for i in range(len(tab)):
tab[i].a= i
tab[i].b= tab[last].a+tab[i].a
tab[i].c= tab[last].b+tab[i].a
tab[i].p= tab+i
last=i
[mS]
[T]
Dziękuję za uwagę
Pytania?
cffi
By Kamil “Arpegius” Szcześniak
cffi
Biblioteka CFFI pomaga pisać wrapery na biblioteki napisane w C. Posiada podobne możliwości co ctypes, jednak zawiera dużo ułatwień. Wbudowana w PyPy biblioteka posiada specjalny backend, z którego informacje o typach są wykorzystywane przez JIT. W prezentacji pobawię się kilkoma strukturami i popularnymi bibliotekami, a także pokaże jakie są możliwości optymalizacji PyPy.
- 429