URL: https://linuxfr.org/users/serge_ss_paille/journaux/pythran-0-8-2-compilation-de-noyaux-scientifiques-ecrits-en-python Title: Pythran 0.8.2 — compilation de noyaux scientifiques écrits en Python Authors: serge_sans_paille Date: 2017年09月14日T20:36:50+02:00 License: CC By-SA Tags: python, c++ et pythran Score: 38 Mâtin, quel journal ! =====================> Pythran est bien vivant> Un vrai compilateur !> Et heure après heure,> Le bonheur vient en codant !>> Mais des mois s’écoulèrent,> Sans que je postasse,> Le moindre mot dans l’air,> Ça manque un peu de classe Et là, tout à coup, soudainement, surgit la version 0.8.2 de [Pythran](https://github.com/serge-sans-paille/pythran). Que s’est‐il passé depuis mon dernier soliloque sur LinuxFr.org ? Allez, plongeons dans le journal des modifications, mais avant ça, un petit rappel sur Pythran, par l’exemple. Vous voyez ce bout de code (trivia : il est extrait des sources mentionnées dans [ce billet](https://linuxfr.org/users/mathrack/journaux/un-python-qui-rivalise-avec-du-c)) : ```python ctypedef fused T: np.float64_t np.float32_t np.int64_t np.int32_t def cross1(np.ndarray[real_t, ndim=4] c, np.ndarray[real_t, ndim=4] a, np.ndarray[real_t, ndim=4] b): cdef unsigned int i, j, k cdef real_t a0, a1, a2, b0, b1, b2 for i in xrange(a.shape[1]): for j in xrange(a.shape[2]): for k in xrange(a.shape[3]): a0 = a[0,i,j,k] a1 = a[1,i,j,k] a2 = a[2,i,j,k] b0 = b[0,i,j,k] b1 = b[1,i,j,k] b2 = b[2,i,j,k] c[0,i,j,k] = a1*b2 - a2*b1 c[1,i,j,k] = a2*b0 - a0*b2 c[2,i,j,k] = a0*b1 - a1*b0 return c ``` C’est du [Cython](http://cython.org/) et ça permet de générer des modules natif pour Python à partir d’un langage hybride. Qu’on aime ou qu’on n’aime pas, une chose est sûre : c’est le standard de fait pour avoir de la perf en calcul scientifique quand on fait du Python ``.|`` (point barre) Problème : la version équivalente en Python + Numpy ressemble à ça : ```python def cross1(c, a, b): c[0] = a[0] * b[2] - a[2] * b[1] c[1] = a[2] * b[0] - a[0] * b[2] c[2] = a[0] * b[1] - a[1] * b[0] return c ``` Si la version Cython s’en sort haut la main en termes de temps d’exécution (_grosso modo_ un facteur 10 sur le cas qui m’intéresse), en termes de maintenance, ce n’est pas extra. Pythran essaie de s’attaquer au souci, en ajoutant quelques infos de type : ```python #pythran export cross1(float64[:,:,:,:], float64[:,:,:,:], float64[:,:,:,:]) #pythran export cross1(float32[:,:,:,:], float32[:,:,:,:], float32[:,:,:,:]) #pythran export cross1(int64[:,:,:,:], int64[:,:,:,:], int64[:,:,:,:]) #pythran export cross1(int32[:,:,:,:], int32[:,:,:,:], int32[:,:,:,:]) def cross1(c, a, b): c[0] = a[0] * b[2] - a[2] * b[1] c[1] = a[2] * b[0] - a[0] * b[2] c[2] = a[0] * b[1] - a[1] * b[0] return c ``` Puis, une phase de compilation statique et on retrouve les mêmes perfs que la version Cython. Voilà pour l’intro. Si vous voulez en savoir plus, je vous suggère la [présentation](http://serge-sans-paille.github.io/talks/pyparis-2017年09月12日.html#/) faite tantôt à _PyData Paris_. Le journal des changements, donc. Cython + Pythran = ♥ ==================== Commençons par un changement qui n’en est pas un. Grâce au travail d’[Adrien Guinet](https://twitter.com/adriengnt) financé par [OpenDreamKit](http://opendreamkit.org/), il est maintenant possible de coupler Cython et Pythran, ou du moins d’utiliser le moteur d’optimisation d’expressions de Pythran depuis Cython, en ajoutant une directive dans Cython. Concrètement, dans le code qui suit : ```python # cython: np_pythran=True import numpy as np cimport numpy as cnp def diffuse_numpy(cnp.ndarray[double, ndim=2] u, int N): """ Apply Numpy matrix for the Forward-Euler Approximation """ cdef cnp.ndarray[double, ndim=2] temp = np.zeros_like(u) mu = 0.1 for n in range(N): temp[1:-1, 1:-1] = u[1:-1, 1:-1] + mu * ( u[2:, 1:-1] - 2L * u[1:-1, 1:-1] + u[0:-2, 1:-1] + u[1:-1, 2:] - 2L * u[1:-1, 1:-1] + u[1:-1, 0:-2]) u[:, :] = temp[:, :] temp[:, :] = 0.0 ``` La grosse expression du bas est évaluée par ``pythonic``, l’implémentation C++ d’une partie du paquet ``numpy``. Annotations externes ==================== Il est désormais possible de mettre les annotations de type dans un fichier externe avec l’extension `.pythran` qui ressemblerait, pour le cas ``cross1`` précédent, à ça : ``` export cross1(float64[:,:,:,:], float64[:,:,:,:], float64[:,:,:,:]) export cross1(float32[:,:,:,:], float32[:,:,:,:], float32[:,:,:,:]) export cross1(int64[:,:,:,:], int64[:,:,:,:], int64[:,:,:,:]) export cross1(int32[:,:,:,:], int32[:,:,:,:], int32[:,:,:,:]) ``` C’est une demande utilisateur (car, oui, incroyable, il y a des utilisateurs de Pythran, et même depuis quelques mois des contributeurs **réguliers** ! Un serpent, une pomme... ça vous rappelle quelque chose ? ======================================================= Grâce au prosélytisme de [Loïc Gouarin](https://www.math.u-psud.fr/~gouarin/), nous avons fait une journée d’initiation à Pythran ouverte à tous cet été (et il devrait y en avoir une sur Lyon en novembre...) et, lors de cette journée, on a réglé pas mal de problèmes d’installation et de configuration sous macOS, ce qui ne devrait pas se reproduire vu les correctifs poussés. La prise en charge de Python 3 a également bénéficié de cette épreuve du feu. Vitesse de compilation ====================== Si Pythran hérite de C++, une étape de compilation finale parfois poussive, il y a eu un travail de fond pour améliorer le temps d’optimisation et de génération du code C++ intermédiaire (tu trouves ça intéressant ? Moi aussi, tellement que j’ai écrit [quelques centaines de lignes sur le sujet](http://serge-sans-paille.github.io/pythran-stories/)). Vitesse d’exécution du code généré ================================== Bon, c’est quand même le but de toute l’histoire, avoir du code natif qui trace. Et un avantage d’utiliser un compilateur pour du code de haut niveau, c’est que comme les sardines[^0], votre code se bonifie avec le temps. Exemple sur le code suivant compilé dans les deux cas avec GCC 6.3 en compilateur _back‐end_ et avec les drapeaux de compilation par défaut : ```python #pythran export slowparts(int, int, float [][][], float [][][], float [][], float [][], float [][][], float [][][], int) from numpy import zeros, power, tanh def slowparts(d, re, preDz, preWz, SRW, RSW, yxV, xyU, resid): """ computes the linear algebra intensive part of the gradients of the grae """ fprime = lambda x: 1 - power(tanh(x), 2) partialDU = zeros((d+1, re, 2*d, d)) for k in range(2*d): for i in range(d): partialDU[:,:,k,i] = fprime(preDz[k]) * fprime(preWz[i]) * (SRW[i,k] + RSW[i,k]) * yxV[:,:,i] return partialDU ``` Là entre la version 0.8.0 *Bloavez Mat* et la 0.8.2 *PyData Paris*, on a un temps d’exécution multiplié par 0,76, ce qui est plutôt agréable ! Le futur ======== Il est agréable de regarder en arrière (premier _commit_ en 2012) : ``` commit 6a0eaa62f5fa3784c0557e2bd365acb7ea576d24 Author: Serge Guelton Date: Thu Feb 2 17:12:51 2012 +0200 root commit. ``` Mais « l’escargot ne recule jamais » [^1] et Pythran avance donc tranquillou, vers une meilleure prise en charge de la vectorisation automatique, une meilleure gestion des *extended slices* et du *fuzzy slicing*, une détection des conteneurs qui peuvent se contenter de la *value semantic* et que sais‐je encore ? Allez, ssssssssssssss fait le serpent. [^0]: Et oui, il existe des sardines de garde, comme il existe des vins de garde ou des chiens de garde. [^1]: « L’escargot est naturellement héroïque : l’escargot ne recule jamais. », Alexandre Vialatte.

AltStyle によって変換されたページ (->オリジナル) /