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