Python and Science: the great mystery
Python isn't known for its baffling performance
Scientific computing <3 performance (think Fortran)
Yet they love each other, which paves the way for accelerating solutions
Serge « sans paille » Guelton
Compiler Engineer / Wood Craft Lover
Groupe Calcul — 24th September 2021
A non-intrusive compiler for scientific kernels written in Python
import numpy #pythran export log_likelihood(float64[], float64, # float64) def log_likelihood(data, mean, sigma): s = (data - mean) ** 2 / (2 * (sigma ** 2)) pdfs = numpy.exp(- s) pdfs /= numpy.sqrt(2 * numpy.pi) * sigma return numpy.log(pdfs).sum()
# native parallel vectorized module pythran log_likelihood.py -fopenmp \ -march=native -DUSE_XSIMD
Upside:
Upside:
Upside:
Enough speak about others,
Let's dive into Pythran specificities
$ pip install pythran $ conda install -c conda-forge pythran $ dnf install pythran ... $ git clone https://github.com/serge-sans-paille/pythran.git
Only comments, a tiny language to describe signatures and overloads:
#pythran export kernel(int) #pythran export kernel(float) #pythran export kernel(complex128?) #pythran export kernel(int[] or float[]) #pythran export kernel(int[], int[:,:])
Some types are copied:
#pythran export kernel(int set or int list or int:str dict)Shape fine-tuning:
#pythran export kernel(int8[:, 3])Strided and transposed views
#pythran export kernel(int8[::,:]) #pythran export kernel(int8[:,:].T)
def foo(x, y): assert type(y) is int if isinstance(x, int): return np.ones(10) else: return x * y foo(5, 8) foo(np.arange(10), 3)
def foo(x, y): if x is None: x = 1 return x foo(None) foo(3) foo(4.)
Mostly OpenMP3, bits of OpenMP4
# inspired by https://software.intel.com/en-us/node/695675
def min_abs(omp_in, omp_out):
return min(abs(omp_in), omp_out)
import numpy as np
def find_min_abs(data):
'return the smallest magnitude among all the integers in data[N]'
result = abs(data[0])
#omp declare reduction(minabs : int : \
omp_out = min_abs(omp_in, omp_out)) \
initializer(omp_priv=omp_orig)
#omp parallel for reduction(minabs: result)
for d in data:
if abs(d) < result:
result = abs(d)
return resultAlmost always partial support!
Pythran dependencies:
Requires an extension, provides a new cell magic:
> %load_ext pythran.magic > %%pythran #pythran export foo(int) def foo(n): return n > print(foo(3))
$ pythran kernel.pySensible to common C++ flags: -O2, -ffast-math etc
from distutils.core import setup from pythran.dist import PythranExtension, PythranBuildExt module1 = PythranExtension('demo', sources = ['a.py']) setup(name = 'demo', version = '1.0', description = 'This is a demo package', cmdclass={"build_ext": PythranBuildExt}, ext_modules = [module1])
https://pythran.readthedocs.io
#pythran on iirc.oftc.net