Journal Pythran 0.7 - PyDataParis

Posté par (page perso) . Licence CC by-sa
25
21
avr.
2015

Termi' Nal (humour du 42ème degré),

Pythran compilateur open source dédié au Python scientifique, a sorti sa version 0.7 il y a peu, à l'occasion du premier événement PyData organisé en France: PyData Paris. ±6 mois s'étaient écoulés depuis la dernière sortie de ce compilateur dont vous pouvez suivre les aventures trépidantes à travers le tag pythran.

Nouveautés

  • Meilleur support de Numpy, plus de fonctions, et une meilleur implémentations pour certaines d'entre elles.
  • Meilleur support de l'indicage étendu, y compris les aberrations du genre a[-1:1:2,:,3][4]
  • Effort sur l'interface utilisateur (messages d'erreurs lors des échecs de compilation ou d'installation)
  • Support du stockage FORTRAN style et pas seulement C style pour les tableaux en entrée de Pythran
  • Analyse (succincte, mais quand même) de la plage de valeur prise par certaines variables pour faire sauter certains tests

Communauté

La grosses surprise de cette version, c'est d'avoir reçu de nombreuses contributions extérieures non francophones. Principalement des rapports de bugs (on va pas dire des rapports de vermine, ce ne serait pas poli). Support archlinux, retours sur l'indicage, les expressions masque et leurs performances, le support de linalg…

C'est vraiment super motivant. Imaginez qu'actuellement du code compilé par Pythran tourne sur un petit robot autonome sous-marin en mer du nord ! C'est la folie !

La franchouillarde société Numscale non contente de nous fournir un moteur de Ferarri avec la lib C++ NT², met aussi des heures de dev pour un portage sous Windows, ce qui améliore la portabilité du code généré, mais demande des, disons, ajustements pour palier au support partiel de C++11 par VS20XY.

D'ailleurs mon alter ego Pierrick Brunet, qui s'est déchiré pour cette release, cherche du boulot. Contactez le !

Cas concret

Jetez un coup d'œil à ce sympathique code source tiré de stackoverflow :

import numpy as np
def GrayScott(counts, Du, Dv, F, k):
  n = 300
  U = np.zeros((n+2,n+2), dtype=np.float32)
  V = np.zeros((n+2,n+2), dtype=np.float32)
  u, v = U[1:-1,1:-1], V[1:-1,1:-1]
  r = 20
  u[:] = 1.0
  U[n/2-r:n/2+r,n/2-r:n/2+r] = 0.50
  V[n/2-r:n/2+r,n/2-r:n/2+r] = 0.25
  u += 0.15*np.random.random((n,n))
  v += 0.15*np.random.random((n,n))

  for i in range(counts):
    Lu = ( U[0:-2,1:-1] + U[1:-1,0:-2] - 4*U[1:-1,1:-1] + U[1:-1,2:] + U[2: ,1:-1] )
    Lv = ( V[0:-2,1:-1] + V[1:-1,0:-2] - 4*V[1:-1,1:-1] + V[1:-1,2:] + V[2: ,1:-1] )
    uvv = u*v*v
    u += Du*Lu - uvv + F*(1 - u)
    v += Dv*Lv + uvv - (F + k)*v
  return V

Il compile désormais avec Pythran en ajoutant l'annotation

#pythran export GrayScott(int, float, float, float, float)

et c'est une grande victoire, car il représente bien ce qu'on peut trouver comme type de code Python/numpy :

  • des appels de fonction externe numpy.random.random
  • de l'indicage étendu à gogo u, v = U[1:-1,1:-1], V[1:-1,1:-1] et même composé avec u[:] = 1.0
  • une boucle externe explicite qui entoure des boucles implicites (cf. les calculs de Lu et Lv)

Donc arriver à compiler ça sans demander à l'utilisateur d'expliciter les calculs comme il faudrait le faire en Cython, c'est chouette !

Les perfs sont d'ailleurs au rendez vous !

$ python -m timeit -s 'from grayscott import GrayScott' 'GrayScott(40, 0.16, 0.08, 0.04, 0.06)'
10 loops, best of 3: 52.9 msec per loop
$ cython grayscott.pyx
$ gcc grayscott.c `python-config --cflags --libs` -shared -fPIC -o grayscott.so -O3 -march=native
$ python -m timeit -s 'from grayscott import GrayScott' 'GrayScott(40, 0.16, 0.08, 0.04, 0.06)'
10 loops, best of 3: 36.4 msec per loop
$ pythran grayscott.py -O3 -march=native
$ python -m timeit -s 'from grayscott import GrayScott' 'GrayScott(40, 0.16, 0.08, 0.04, 0.06)'
10 loops, best of 3: 20.3 msec per loop

La suite

Au prochain opus, forcément, avec certainement le support de numpy.linalg et numpy.random !

Pythran — C++ for snakes

  • # micro bench dans une loupe

    Posté par . Évalué à 10.

    J'ai juste une remarque sur ton bench : "10 boucles et moyennes de 3 meilleurs".

    Si tu traces une courbe des temps d’exécution de chaque boucle, tu verras un temps décroissant sur les 3 ou 4 premières exécution puis un plateau, et parfois des pics (allocation mémoire, switch de contexte…).

    En fait, tu remplit tes caches, et ensuite, ils sont "chaud", le code est ainsi le plus rapide. Mais c'est loin de la réalité. Dans la réalité, tu ne ré-exécutes jamais le même code sur les même données 2 fois de suite.

    Ce genre de bench ne permet pas d'estimer tout code d'optimisation d'usage du cache (tiling, accès linéaire à la mémoire,…).

    A l'inverse, si tu nettoies complètement les caches (boucles entre 2 exécutions sur des données externes). Tu te places dans un pire cas : le cache "froid". Ce n'est peut être pas non plus réaliste.

    Dans tous les cas, je préfère utiliser une courbe (ici tu aurais 10 points). Tu as 3 cas, donc 3 courbes. Tu peux voir l'efficacité de la 1er exécution, l'augmentation de perf ensuite, et visualiser tous problèmes avec les données (point aberrant dû à changement de contexte par exemple)

    "La première sécurité est la liberté"

    • [^] # Re: micro bench dans une loupe

      Posté par (page perso) . Évalué à 3.

      Tu as raison ! Les choix faits par timeit nous donnent uniquement une info de type performance de crête, pas grand chose sur les effets de cache et rien sur la variabilité.

    • [^] # Re: micro bench dans une loupe

      Posté par . Évalué à 3.

      Tu as raison pour les micro-benches, mais si d'un côté tu ne vas sans doute pas exécuter 10000 fois le même noyau de code d'affilée, tu vas par contre réutiliser les mêmes structures de données régulièrement, en leur faisant subir plusieurs types de traitements. De ce côté-là, ça signifie que oui, tu auras au moins une partie des structures de données présentes dans les caches.

      Personnellement j'aime faire les deux :

      1. Une série de tests où je vide systématiquement les caches avant de faire tourner le code
      2. Une série de tests où je répète le même code plusieurs fois de suite pour voir quel est la meilleure performance possible en pratique (vs. la performance théorique de la machine par exemple).

      Dans le cas précis de Python, c'est beaucoup plus compliqué, car le ramasse-miettes, les mécanismes dynamiques de typage, etc., font que des traitements annexes au code lui-même peuvent se déclencher de façon complètement imprévisible.

      • [^] # Re: micro bench dans une loupe

        Posté par . Évalué à 3.

        Oui, dans la réalité le cache n'est ni chaud ni froid, mais tiède (sauf donnés >10Mo). L'idéal est de bencher un code réel.

        En général, je teste le pire cas : cache froid (== cela sera toujours plus rapide en vrai, et tu vois les effets d'optimisation type tilling). Je fais une courbe, et jamais de moyenne, cela permet de voir visuellement tous les effets que tu cites en python (gc…).

        "La première sécurité est la liberté"

  • # Question naïve

    Posté par . Évalué à 0.

    Attention, question vraiment naïve d'un noob :

    Les logiciels de calcul matriciels genre Scilab ne sont-ils pas plus optimisés pour ce genre de calcul ?

    Moi qui fais de temps en temps du calcul "scientifique" je trouve gonflant de devoir déclarer le type de mes valeurs lorsque je le fais avec un "langage de programmation".

    Puis devoir taper sans-cesse le nom des modules pour les fonctions usuels genre math.floor() math.ceil(), np.exp(), bref je ne trouve pas ça pratique.

    Quels sont donc les avantages du travail scientifique en python ?

    • [^] # Re: Question naïve

      Posté par . Évalué à 3.

      taper sans-cesse le nom des modules pour les fonctions usuels genre math.floor() math.ceil()

      Si au lieu de faire :

      import math

      tu fais :

      from math import *

      Tu pourra taper directement floor(), ceil(), etc…

    • [^] # Re: Question naïve

      Posté par (page perso) . Évalué à 4.

      (je parle uniquement de Python ici et non de Pythran)

      Moi qui fais de temps en temps du calcul "scientifique" je trouve gonflant de devoir déclarer le type de mes valeurs lorsque je le fais avec un "langage de programmation".

      L'intérêt du Python c'est justement de pouvoir s'en passer…

      Quels sont donc les avantages du travail scientifique en python ?

      Je ne fais pas souvent du calcul scientifique mais Matlab &co ont une syntaxe que je trouve plus complexe à comprendre que le Python (subjectif mais ça satisfait les gens de mon avis). Autre point, si tu développes un logiciel scientifique, tu peux développer tout le logiciel en Python et faire des appels à Numpy lorsque c'est nécessaire. C'est quand même sympa de pouvoir développer l'interface graphique avec Qt par exemple.

      Et puis vu que tes 2 critiques ne sont pas justifiées (pas besoin de remettre le nom du module à chaque appel de fonction et typage dynamique), on pourrait inverser la question : Quels sont les avantages du travail scientifique avec Scilab ?

  • # Fonctions de haut niveau

    Posté par . Évalué à 1.

    Je suis ton projet avec un grand intérêt, j'aime beaucoup l'approche (et les résultats !).

    Je me demandais si il y a un espoir, à l'avenir, de pouvoir utiliser des fonctions de plus "haut niveau" comme des fonctions scipy (par exemple scipy.optimize.minimize). Quelles sont les limites ?

    • [^] # Re: Fonctions de haut niveau

      Posté par (page perso) . Évalué à 2.

      merci :-)

      il y a quelques limites liées au typage : impossible dans le modèle Pythran actuel de supporter les fonctions qui ne sont pas implicitement statiquement typées. Par exemple un truc du genre :

      def foo(n):
        if n == 1: return "hello"
        else: return 5.

      ensuite pour les fonctions scipy, on pourrait espérer qu'une fois le support de numpy suffisamment abouti, ça se fasse automagiquement pour celles qui ne sont pas implémentées en natif. Mais la route est encore longue, bien que certaines genre

      https://github.com/scipy/scipy/blob/v0.15.1/scipy/optimize/optimize.py#L154

      passeraient sans soucis.

  • # cython

    Posté par (page perso) . Évalué à 1.

    Salut et bravo pour ce travail. Sinon comme je commence à m'intéresser de près à cython, est ce que tu pourrais expliciter un peu plus cette phrase

    Donc arriver à compiler ça sans demander à l'utilisateur d'expliciter les calculs comme il faudrait le faire en Cython, c'est chouette !

    • [^] # Re: cython

      Posté par (page perso) . Évalué à 2.

      Cython n'optimise pas les appels à des fonctions numpy. Tu peux lire le fil stackoverflow dont est extrait l'exemple donné dans le journal pour voir la « meilleur solution » cython proposée et te convaincre que c'est un peu une régression :-)

  • # Python 3 ?

    Posté par . Évalué à 4.

    Merci, je ne connaissais pas Pythran.

    Le support Python 3 est il envisagé ?

    Je vois que qqn sur GitHub a déjà demandé que le code compilé depuis Python 2.7 puisse être importé en Python 3, mais je pense aussi à la compilation de code Python 3.

    • [^] # Re: Python 3 ?

      Posté par (page perso) . Évalué à 3.

      Salut,

      il y a plusieurs niveaux de support possible

      1. que pythran soit exécutable avec un interpréteur python3
      2. que le module généré par pythran soit importable en python3
      3. que le code donné en entrée de pythran soit du python3

      et toute combinaison de ces trois fonctionnalités. Pour le moment, aucune n'est supportée, on a eu assez peu de demande autour de ça, je l'avoue. Et ce n'est pas si évident car l'AST et la sémantique du langages changent entre les deux… il ne suffit pas de mettre des parenthèses dans les print ;-)

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.