Sommaire
- Mâtin, quel journal !
- Cython + Pythran = ♥
- Annotations externes
- Un serpent, une pomme… ça vous rappelle quelque chose ?
- Vitesse de compilation
- Vitesse d’exécution du code généré
- Le futur
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. 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) :
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 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 :
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 :
#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 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 financé par OpenDreamKit, 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 :
# 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, 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).
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 sardines1, 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 :
#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 <serge.guelton@hpc-project.com>
Date: Thu Feb 2 17:12:51 2012 +0200
root commit.
Mais « l’escargot ne recule jamais » 2 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.
# Typos
Posté par serge_sans_paille (site web personnel) . Évalué à 3.
Si un modérateur passe dans le coin : il manque un s à scientifique dans le titre, et dans la note de bas de page numéro 1, il faut remplacer chines par chiens sinon c'est moins drôle.
[^] # Re: Typos
Posté par Loïs Taulelle ࿋ (site web personnel) . Évalué à 2.
Y'a aussi "lanage hybride" et "qu j'ai".
Proverbe Alien : Sauvez la terre ? Mangez des humains !
[^] # Re: Typos
Posté par Snark . Évalué à 2.
Et tout à la fin, "de regardé en arrière" -> "de regarder en arrière"
[^] # Re: Typos
Posté par Jehan (site web personnel, Mastodon) . Évalué à 8.
Il manque aussi un 's' à:
:-D
Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]
[^] # Re: Typos
Posté par refreketu . Évalué à 2.
s/sur/sûre/
[^] # Re: Typos
Posté par claudex . Évalué à 4.
J'ai tout corrigé merci à tous.
Je me disais que passais à côté de la blague.
« Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche
[^] # Re: Typos
Posté par kantien . Évalué à 5.
Pour calmer des chiens de garde, il faut leur caresser l'échine ? :-P
Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
# Question trollesque (et sincère)
Posté par foobarbazz . Évalué à 2.
Why?
Je veux dire… Quel est l'avantage par rapport au C++ moderne par exemple ?
[^] # Re: Question trollesque (et sincère)
Posté par G.bleu (site web personnel) . Évalué à 10.
C++ est beaucoup plus complexe que Python, dans le domaine scientifique les utilisateurs ne sont majoritairement pas des développeurs de métier donc cette complexité s'accentue encore d'un cran.
L'avantage de Cython&co c'est de pouvoir faire une seconde passe d'optimisation sur du code
dégueulassede chercheur une fois qu'il est fonctionnellement correct au lieu de tout devoir redévelopper en C++.À titre personnel j'ai des projets d'utilisation de ces outils pour le jeu vidéo avec mon binding Python sur Godot afin de pouvoir scripter rapide en Python, puis d'avoir une passe d'optimisation suffisamment simple et rapide, avant de réécrire en gros C++ qui tache si ce n'est toujours pas assez ;-)
[^] # Re: Question trollesque (et sincère)
Posté par paco81 . Évalué à 1.
Je peux faire un retour d'expérience sur cython… J'avais considéré pythran qui pour certains cas est vraiment idéal, mais malheureusement l'absence des classes utilisateur était trop pénalisante pour mon utilisation.
L'intérêt de cython dans mon cas c'est d'avoir dans une même classe :
- des méthodes non optimisées, typiquement le constructeur, qui gèrent la lecture, préparation et écriture de données. Et pour ça, le bon vieux python de base est idéal, pour la compacité du code et pour pouvoir s'interfacer très facilement avec n'importe quel type de donnée.
- des méthodes optimisées, qui concentrent la majeur partie du temps d'exécution. Et c'est pas très grave si ça ne s'écrit pas exactement comme du python standard (mais ce serait encore mieux si c'était le cas #pythran #utopiste). Le fait de pouvoir visualiser quelles lignes du code sont non-optimales car elles sont transformées en beaucoup de lignes de C++, est très utile aussi (cython annotate).
Bref, le fait que Cython + Pythran = ♥ est une excellente nouvelle !
Encore bravo pour ton travail, Serge :)
[^] # Re: Question trollesque (et sincère)
Posté par wilk . Évalué à 2.
Autre question dans le genre :
Est-ce que gonum ( https://www.gonum.org/ ) c'est à dire « Gonum is a set of packages designed to make writing numeric and scientific algorithms productive, performant, and scalable. » change la donne sur les besoins en perf des applis scientifiques ?
# un temps d'exécution multiplié par 0.76
Posté par Victor . Évalué à -1.
ça va moins vite donc ?
[^] # Re: un temps d'exécution multiplié par 0.76
Posté par jyes . Évalué à 5.
¿?
Ben non, si le temps est plus court, ça va plus vite… Captain Obvious à la rescousse ?
[^] # Re: un temps d'exécution multiplié par 0.76
Posté par kantien . Évalué à 4.
Bah non ça va plus vite : si tu mets moins de temps pour faire la même chose (O.76 seconde au lieu de 1 seconde), tu vas plus vite. Il a réduit le temps d'exécution de 24% et donc augmenté la vitesse de 31% (
1 / 0.76 ~ 1.31
).Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.
[^] # Re: un temps d'exécution multiplié par 0.76
Posté par serge_sans_paille (site web personnel) . Évalué à 3.
Si l'original prend 1s, le nouveau temps est de 0.76s. Ça prend moins de temps, ça va plus vite.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.