04 - Cython שילוב של C עם Python לה

Download Report

Transcript 04 - Cython שילוב של C עם Python לה

‫שיאון שחורי‬
‫‪MilOSS-il 2012‬‬
‫מוטיבציה‬
‫‪ python ‬זה קל ו ‪ C‬זה מהיר‪ .‬למה לא לשלב?‬
‫‪ ‬יש כבר קוד קיים ב ‪ .C‬אנחנו רוצים להשתמש‬
‫בו‪ ,‬ולבסס מעליו קוד חדש ב ‪python‬‬
‫‪ ‬מסקנה‪ :‬צריך ממשק (דו כיווני) בין ‪ python‬ל ‪C‬‬
‫‪ ‬פתרונות ידועים (לי)‪ :‬שימוש בתהליכים נפרדים‪,‬‬
‫‪ C/Python API ,ctypes‬או ‪cython‬‬
‫פתרונות (תהליכים)‬
‫‪ ‬תהליכים שונים‬
‫‪ ‬אחד כתוב ב ‪ ,C‬השני ב ‪python‬‬
‫‪ ‬העברת מידע באמצעות ‪ ,socket( IO‬קבצים וכו')‬
‫‪ ‬מה קיבלנו?‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫לא צריך להכיר טכנולוגיה חדשה ‪‬‬
‫צריך לנהל תהליך ‪ C‬מלא ‪‬‬
‫לא יעיל ‪‬‬
‫מסורבל ‪‬‬
‫צריך להגדיר ממשקים בין התהליכים ‪‬‬
‫פתרונות (‪)ctypes‬‬
‫‪ ‬יצירת ‪ dll‬ושימוש ב ‪ctypes‬‬
‫‪ ‬כתיבת ‪ dynamic library‬ב ‪C‬‬
‫‪ ‬טעינה של הספרייה באמצעות ‪ctypes‬‬
‫‪ ‬קריאה לפונקציות שנכתבו ב ‪C‬‬
‫פתרונות (‪)ctypes‬‬
‫‪ ‬מה קיבלנו?‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫תהליך בודד ‪‬‬
‫יעיל ‪‬‬
‫קל ‪‬‬
‫הגדרות כפולות (ולאו דווקא תואמות) ‪‬‬
‫קישוריות חד כיוונית (אי אפשר לקרוא לקוד ‪python‬‬
‫מתוך ‪ C‬בקלות) ‪‬‬
‫פתרונות (‪)C/Python API‬‬
‫‪ ‬שימוש ב ‪C/Python API‬‬
‫‪ ‬קידוד קוד ב ‪ C‬תוך קריאה לפונקציות מתוך ‪Python.h‬‬
‫‪ ‬בניית ‪ extension‬באמצעות ‪distutils‬‬
‫‪ ‬שימוש מתוך פייתון באמצעות ‪import‬‬
‫‪ ‬מה קיבלנו?‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫תהליך בודד ‪‬‬
‫יעיל ‪‬‬
‫הגדרת טיפוסים בודדת ‪‬‬
‫קישוריות דו כיווניות ‪‬‬
‫קידוד מסורבל (למשל‪ ,‬שימוש באובייקטים פייתונים מתוך‬
‫‪ )C‬‬
‫פתרונות (‪)Cython‬‬
‫‪ ‬שימוש ב ‪Cython‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫מבוסס ‪pyrex‬‬
‫כתיבת קבצי ‪ :.pyx‬קבצי ‪( python‬פחות או יותר) עם‬
‫פקודות מיוחדות‬
‫בניית ‪ extension‬באמצעות ‪distutils‬‬
‫שימוש מתוך פייתון באמצעות ‪import‬‬
‫‪ ‬מה קיבלנו?‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫תהליך בודד ‪‬‬
‫יעיל ‪‬‬
‫הגדרת טיפוסים בודדת ‪( ‬כמעט ‪)‬‬
‫קישוריות דו כיווניות ‪‬‬
‫קידוד נוח ‪‬‬
‫ בסיסי‬Cython
#include <stdio.h>
bla.h
inline int my_func(int x)
{
printf(“my func %d\n”, x);
return 0;
}
cdef extern from “bla.h”:
int my_func(int)
bla.pyx
def pymy_func(x):
return my_func(x)
import bla
print bla.pymy_func(10)
main.py
Distutils setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
setup.py
setup(cmdclass={'build_ext': build_ext},
ext_modules=[Extension(“bla", ['bla.pyx'])])
$ python setup.py build_ext --inplace
‫בנייה‬
running build_ext
cythoning bla.pyx to bla.c
building 'bla' extension
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall Wstrict-prototypes -fPIC -I/usr/include/python2.6 -c bla.c -o
build/temp.linux-i686-2.6/bla.o
gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions build/temp.linuxi686-2.6/bla.o -o /mnt/filer_home/users/t_sion/bla.so
‫דוגמה‪ :‬קידוד פשוט‬
‫‪ ‬הבעיה‪ :‬יש מערך גדול של בתים‪ ,‬ורוצים לקודד‬
‫אותו עם הקוד הפשוט הבא‪ :‬כל בית מוחלף‬
‫בשני בתים‪ :‬הבית המקורי וה ‪ not‬שלו‬
‫מימוש פייתוני‬
import array
pyencode.py
lookup = {}
for i in range(256):
lookup[chr(i)] = chr(i) + chr(i ^ 0xFF)
def encode(arr):
out = array.array('c', '\x00' * len(arr) * 2)
for i in xrange(len(arr)):
data = lookup[arr[i]]
out[2*i] = data[0]
out[2*i + 1] = data[1]
return out
‫ המשך‬- ‫מימוש פייתוני‬
In [10]: s = array.array('c', '\x01\xf3\x34\x45' * (10 ** 6))
In [11]: import pyencode
In [12]: %timeit -n 3 pyencode.encode(s)
3 loops, best of 3: 2.84 s per loop
‫ פשוט‬cython ‫מימוש‬
‫ למודול‬cython ‫ רק מקומפל עם‬,‫ אותו קוד‬
cyencode_simple
In [13]: import cyencode_simple
In [14]: %timeit -n 3 cyencode_simple.encode(s)
3 loops, best of 3: 1.62 s per loop
‫ מתקדם‬cython ‫מימוש‬
import array
cdef unsigned short lookup[256]
cdef unsigned char *_ptr
for i in range(256):
_ptr = <unsigned char*>(&lookup[i])
_ptr[0] = <int>i
_ptr[1] = <int>(i ^ 0xFF)
cyencode.py
def encode(arr):
cdef int i
cdef unsigned char *in_ptr, *out_ptr, *p
in_ptr = <unsigned char*><unsigned int>(arr.buffer_info()[0])
out = array.array('c', '\x00' * len(arr) * 2)
out_ptr = <unsigned char*><unsigned int>(out.buffer_info()[0])
for i in range(len(arr)):
p = <unsigned char*>(&lookup[<int>in_ptr[i]])
out_ptr[2*i] = p[0]
out_ptr[2*i + 1] = p[1]
return out
‫ המשך‬- ‫ מתקדם‬cython ‫מימוש‬
In [15]: import cyencode
In [16]: %timeit -n 3 cyencode.encode(s)
3 loops, best of 3: 26.8 ms per loop
C ‫ מתקדם – הצצה לקוד‬cython ‫מימוש‬
for (__pyx_t_6 = 0; __pyx_t_6 < __pyx_t_4; __pyx_t_6+=1) {
__pyx_v_i = __pyx_t_6;
cyencode.c
/* "cyencode.pyx":17
*
p = <unsigned char*>(&lookup[<int>in_ptr[i]]) # <<<<<<<
*/
__pyx_v_p = ((unsigned char *)(&(__pyx_v_8cyencode_lookup[
((int)(__pyx_v_in_ptr[__pyx_v_i]))])));
/* "cyencode.pyx":18
*
out_ptr[2*i] = p[0]
# <<<<<<<
*/
(__pyx_v_out_ptr[(2 * __pyx_v_i)]) = (__pyx_v_p[0]);
/* "cyencode.pyx":19
*
out_ptr[2*i + 1] = p[1]
# <<<<<<<
*/
(__pyx_v_out_ptr[((2 * __pyx_v_i) + 1)]) = (__pyx_v_p[1]);
}
Pitfalls
GIL 
Reference counting 
cdef functions ‫ טיפול בשגיאות ב‬
‫פיצ'רים נוספים‬
‫‪def,cdef,cpdef ‬‬
‫‪ – namespaces ‬איך לתת שמות זהים בפייתון‬
‫וב ‪C‬‬
‫‪ ‬קבצי ‪.pxd‬‬
‫‪ cinit ‬ו ‪dealloc‬‬
‫‪ ‬אינטגרציה עם ‪numpy‬‬
‫מידע נוסף‬
http://docs.cython.org :cython ‫ תיעוד‬
‫ טוב‬tutorial ‫ כולל‬
sion.schori@gmail :‫ במייל שלי‬