Journal Pythran, en plein délire

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
50
23
mai
2018

Cher journal, je dois te confesser que j'ai déclenché quelque chose dont j'ai un peu honte.

Tu te souviens bien sûr de Pythran, ce compilateur pour noyau scientifiques écrit en Python, et de ce petit détail d'implémentation qui veut que le compilateur commence par transformer les fonctions Python en fonctions C++ template (a.k.a. transformer du code Python en meta-programme C++ pour faire stylé voire pédant).

Un choix de design a été de faire en sorte que ces fonctions ne font plus référence à aucune partie la l'API C de Python. C'est du code C++ « normal ». Et voilà ce que ça a donné :-/

  1. Un zozo a décidé qu'au lieu de générer un code appelable depuis Python, on peut utiliser le C++ intermédiaire depuis du code C++ classique. Comme ça on prototype en Python, puis hop, on lance la CLI qui va bien et on a le code de prod avec des perfs décentes et très peu de dépendances.

  2. Un autre zozo a décidé que ce même code C++ pouvait être associé à swig pour générer… du code appelable depuis Java. Hop on prototype en Python puis on appelle du code natif depuis Java via JNI, tranquille.

Et le plus drôle, c'est que ce genre de monstre a vocation à aller en prod, le délire ne s'arrête jamais :-)

  • # bidouille

    Posté par  . Évalué à 0.

    Fondamentalement il n'y a pas de mal à ça. C'est un detournemt de la fonction initiale peut-être que le résultat est imparfait. Mais pourquoi pas. Parfois le bidouillage est très bien pour de petits besoin.

    • [^] # Re: bidouille

      Posté par  . Évalué à 9.

      Je pense que c'était ironique et que serge_sans_paille est très heureux de voir ces usages non-orthodoxes de pythran (il y a des indices: "perfs décentes et très peu de dépendances")

      • [^] # Re: bidouille

        Posté par  (site web personnel) . Évalué à 7.

        C'est effectivement totalement (shellement?) ironique. J'avais laissé la porte ouverte pour faire ça, mais une petite porte. Et voilà que des gens s'engouffrent dedans avec enthousiasme.

        Je jubile.

  • # Au passage : Pythran: Crossing the Python Frontier

    Posté par  . Évalué à 4.

    Sur l'implémentation naïve de la fonction de Rosen publier dans le numéro de mars/avril de cise dans cet example de code :

    def rosen_explicit_loop(x): 
        s = 0. 
        n = s.shape 
        for i in range(0, n - 1): 
          s += 100. * x[i + 1] - x[i] ** 2.) ** 2. + (1 - x[i]) ** 2 
        return s

    ne serait-ce pas n=x.shape ?

    Cela ne gêne pas vraiment la compréhension de l'article mais, j'ai buté dessus un petit moment avant d'envisager que cela soit une coquille …

  • # Dans le même genre, il y a Cannoli

    Posté par  (site web personnel) . Évalué à 1.

  • # Liste des fonctions de numpy supportées

    Posté par  . Évalué à 4.

    S'il faut choisir entre numba et pythran, je pense que le choix pour moi se fera sur le support du sous ensemble de numpy.

    Celui de numba est extrêmement faible, ce qui rend le port de code que l'on a voulu par trop lent (donc on utilise plein d'outils numpy) extrêmement chiant.

    Je ne trouve pas une page explicite qui liste les fonction numpy supportées, c'est dommage !

    Par exemple serait il possible de compiler ce bout de code  ?

    import numpy as np
    def get(img, y0, y1, x0, x1, mode="reflect"):
        xs, ys = np.mgrid[y0:y1, x0:x1]
        height, width = img.shape
    
        if mode == "nearest":
            xs = np.clip(xs, 0, height-1)
            ys = np.clip(ys, 0, width-1)
    
        elif mode == "wrap":
            xs = xs % height
            ys = ys % width
    
        elif mode == "reflect":
            maxh = height-1
            maxw = width-1
    
            # An unobvious way of performing reflecting modulo
            # You should comment this
            xs = np.absolute((xs + maxh) % (2 * maxh) - maxh)
            ys = np.absolute((ys + maxw) % (2 * maxw) - maxw)
    
        elif mode == "constant":
            output = np.empty((y1-y0, x1-x0))
            output.fill(0) # WHAT THE CONSTANT IS
    
            # LOADS of bounds checks and restrictions
            # You should comment this
            target_section = output[max(0, -y0):min(y1-y0, -y0+height), max(0, -x0):min(x1-x0, -x0+width)]
            new_fill = img[max(0, y0):min(height, y1), max(0, x0):min(width, x1)]
    
            # Crop both the sections, so that they're both the size of the smallest
            # Use more lines; I'm too lazy right now
            target_section[:new_fill.shape[0], :new_fill.shape[1]] = new_fill[:target_section.shape[0], :target_section.shape[1]]
    
            return output
    
        else:
            raise NotImplementedError("Unknown mode")
    
        return img[xs, ys]

    En soit, je ne suis pas sur de gagner en perf, mais comme j'appelle ce code dans plein de filtres, je ne peux pas utiliser numba par exemple si je fais appel à cette fonction.

    Personnellement mon intérêt réside dans ne pas avoir à vectoriser des boucles avec plein de conditions sur les indices d'un tableau …

    La c'est la version 2D pour jouer mais ne 3D, ce n'est même pas la peine.

    Et si je le fais en cython, alors j'ai perdu tout le monde … et je dois tout gérer !

    Donc une solution ou je serais directement capable en lisant une page de choisir de réduire mes appels au sous ensemble supporté serait cool. C'est juste que sans la page du support, c'est assez difficile.

    Après, c'est peut être mois que n'ai juste pas cherché où il faut, cela ne serait pas la première fois …

Suivre le flux des commentaires

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