Slide 1

Slide 1 text

Extending Python with Compiled Languages Emil Sharifullin @litleleprikon 1

Slide 2

Slide 2 text

Why? 2

Slide 3

Slide 3 text

Show me the Code http:!//bit.ly/ExtendingPython 3

Slide 4

Slide 4 text

Extending with C 4

Slide 5

Slide 5 text

C/C++ • Python native libraries • Basically a C shared libraries • Python working perfectly with C/C!++ shared libraries • C functions are needed to be wrapped with python-compatible functions using python C API 5

Slide 6

Slide 6 text

Code typedef struct { int length; int *values; } Array; Array *SieveOfEratosthenes(int n) 6 C/CExtExample.c

Slide 7

Slide 7 text

Add Python Support • Include Python.h • Wrap functions with python-compatible wrappers • Define array of methods • Define PyModuleDef • Create function PyInit_ that returns PyModuleDef 7

Slide 8

Slide 8 text

Add Python Support #define PY_SSIZE_T_CLEAN #include #if PY_MAJOR_VERSION >= 3 … … … #endif C/CExtExample.c 8

Slide 9

Slide 9 text

Add Python Support static PyObject *CEratosphenes_SieveOfEratosphenes( PyObject *self, PyObject *args) C/CExtExample.c 9

Slide 10

Slide 10 text

Add Python Support if (!PyArg_ParseTuple(args, "I", &n)) { return NULL; } C/CExtExample.c 10

Slide 11

Slide 11 text

Add Python Support PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, …); PyAPI_FUNC(int) PyArg_ParseTupleAndKeywords( PyObject *, PyObject *, const char *, char **, ...); https://docs.python.org/3/c-api/arg.html 11

Slide 12

Slide 12 text

Add Python Support PyObject *result = PyList_New(0); … PyList_Append(result, PyLong_FromLong(p)); C/CExtExample.c 12

Slide 13

Slide 13 text

Add Python Support C/CExtExample.c static PyMethodDef Methods[] = { {“sieve_of_eratosphenes", CExtExample_SieveOfEratosphenes, METH_VARARGS, "Return list of prime numbers until given number."}, {NULL, NULL, 0, NULL} "/* Sentinel "*/ }; 13

Slide 14

Slide 14 text

Add Python Support C/CExtExample.c static PyModuleDef CExtExample = { PyModuleDef_HEAD_INIT, "CExtExample", "Example module written in C", -1, Methods}; 14

Slide 15

Slide 15 text

Add Python Support C/CExtExample.c PyObject *PyInit_CExtExample(void) { return PyModule_Create(&CExtExample); } 15

Slide 16

Slide 16 text

Setup   esharifu  $ ~/projects  python3 %--version Python 3.7.5   esharifu  $ ~/projects  clang -v Apple clang version 11.0.0 (clang-1100.0.20.17) Target: x86_64-apple-darwin18.6.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/ usr/bin 16

Slide 17

Slide 17 text

Building from setuptools import setup, Extension extension = Extension('CExtExample', sources=['CExtExample.c']) setup(name='CExtExample', version='1.0', description='This is Example module written in C', ext_modules=[extension]) C/setup.py 17

Slide 18

Slide 18 text

Building $ ~/p/E/C  nm CExtExample.cpython-37m-darwin.so 0000000000001050 d _CExtExample 0000000000000dd0 t _CExtExample_SieveOfEratosphenes 00000000000010c0 d _Methods 0000000000000db0 T _PyInit_CExtExample U _PyList_Append U _PyList_New U _PyLong_FromLong U _PyModule_Create2 U %__PyArg_ParseTuple_SizeT U _free U _malloc U dyld_stub_binder 18

Slide 19

Slide 19 text

Memory Management • Do not use malloc/free on python objects • Use Py_New instead • Python have it’s own allocator and own private heap • You can use malloc/free on c structures 19

Slide 20

Slide 20 text

Pros & Cons • Freedom of GIL • Manual memory management • High speed 20

Slide 21

Slide 21 text

Extending with Go 21

Slide 22

Slide 22 text

Extending with Go 22

Slide 23

Slide 23 text

Go • Compiling Go code to shared libraries • Using CGo • CGo !!= Go • Use Go shared library in C extension 23

Slide 24

Slide 24 text

Code func SieveOfEratosphenes(n int) []int { … } Go/GoExtExample.go 24

Slide 25

Slide 25 text

Add Python Support • Import C • Include Python.h • Wrap go functions to CGo-compatible wrappers • Include Python.h • Wrap functions with python-compatible wrappers • Define array of methods • Define PyModuleDef • Create function PyInit_ that returns PyModuleDef 25

Slide 26

Slide 26 text

Add Python Support pt. 1 // #cgo pkg-config: python3 // #include import "C" Go/GoExtExample.go 26

Slide 27

Slide 27 text

Add Python Support pt. 1 // SieveOfEratosphenes calculates prime numbers //export SieveOfEratosphenes func func SieveOfEratosphenes(n int, result *[]int) { … } Go/GoExtExample.go 27

Slide 28

Slide 28 text

Setup   esharifu  $ ~/projects  python3 %--version Python 3.7.5   esharifu  $ ~/projects  go version go version go1.13.1 darwin/amd64 28

Slide 29

Slide 29 text

Building pt. 1 go build -v -buildmode=c-shared -o GoExtExampleGo.so \ GoExtExample.go Go/Makefile 29

Slide 30

Slide 30 text

Add Python Support pt. 2 #define PY_SSIZE_T_CLEAN #include #include "GoExtExampleGo.h" #if PY_MAJOR_VERSION >= 3 Go/GoExtExample.c 30

Slide 31

Slide 31 text

Add Python Support pt. 2 Go/GoExtExample.c static PyObject *GoExtExample_SieveOfEratosphenes(PyObject *self, PyObject *args) { int n; if (!PyArg_ParseTuple(args, "I", &n)) { return NULL; } GoSlice primes = {}; SieveOfEratosphenes((GoInt)n, &primes); PyObject *result = PyList_New(0); for (int i = 0; i < primes.len; i"++) { GoInt current = ((GoInt *)(primes.data))[i]; PyList_Append(result, PyLong_FromLong(current)); } return result; } 31

Slide 32

Slide 32 text

Building pt. 2 gcc GoExtExample.c -dynamiclib GoExtExampleGo.so -o \ GoExtExample.so \ $(python3-config —cflags) \ $(python3-config --ldflags) 32

Slide 33

Slide 33 text

Pros & Cons • Unable to use Go tooling • Unable return go structs from go funcs and multiple values • Possible memory leaks • Blocking goroutines during CGo calls • Unable to cross-compile • A bunch of side effects from CGo 33

Slide 34

Slide 34 text

Memory Management • https:!//karthikkaranth.me/blog/calling-c- code-from-go/ • Python and Go both have private heaps • GC of both languages operates only inside it’s private heap and knows only about objects it allocated 34

Slide 35

Slide 35 text

Memory Management 35

Slide 36

Slide 36 text

Benchmarks import pytest import GoExtExample @pytest.mark.parametrize("input", [1000, 100000, 1000000]) def test_go(benchmark, input): benchmark(GoExtExample.sieve_of_eratosphenes, input) Go/test_benchmark.py 36

Slide 37

Slide 37 text

Benchmarks ms 0 10 20 30 40 Sieve Limit 1000 100000 1000000 C Go 37

Slide 38

Slide 38 text

Benchmarks Ms 0 3000 6000 9000 12000 Sieve Limit 1000 100000 1000000 C Go Python 38

Slide 39

Slide 39 text

Why? • Speed(Not Guaranteed) • Avoiding GIL • Step-by-step migration from Python • Using language-specific libraries 39

Slide 40

Slide 40 text

Why Not? • Easy way to shot to your leg • Harder Memory Management 40

Slide 41

Slide 41 text

Thank You! •https:!//docs.python.org/3/extending/ extending.html •https:!//docs.python.org/3.7/c-api/index.html •https:!//hackernoon.com/extending-python-3- in-go-78f3a69552ac 41 Emil Sharifullin [email protected] @litleleprikon