Forum Programmation.python bench : pourquoi python serait lent ?

Posté par  .
Étiquettes : aucune
0
31
août
2004
Bonsoir
Je viens de faire un bench à la mano vite fait afin de tenter de casser certaines idées reçues.
J'ai pris pour cela une fonction nulle qui vérifie si un nombre est premier, un bon nombre premier (4012009) et j'ai comparé python 2.3.3, perl 5.8.3, c++ (gcc 3.3.3) et python2.3.3+psyco1.2 (avec la commande time)
Les résultats parlent d'eux même :
Perl :
real 0m2.224s
user 0m1.644s
sys 0m0.015s

Python :
real 0m3.491s
user 0m2.436s
sys 0m0.186s

cpp :
real 0m0.207s
user 0m0.154s
sys 0m0.002s

python+psyco :
real 0m0.391s
user 0m0.235s
sys 0m0.010s


Face à ces résultats, plusieurs questions me taraudent :
- python est très lent sans psyco : c'est pas nouveau, mais c'est du à quoi ???
- python+psyco est très très rapide :
= y a-t-il des défauts provoqués par psyco (conso mémoire, CPU...) ?
= du code python risque-t-il de ne pas s'exécuter à cause de python ?
= pourquoi psyco n'est pas dans la lib python standard ?
= pourquoi continue-t-on à dire "python est lent" tout court alors qu'il peut être presque aussi rapide que le C++ à l'aide de psyco?

Merci de répondre à ces questions sans trop troller

PS : c'est qu'un benchmark, je sais
  • # sources

    Posté par  . Évalué à 4.

    Est-ce que tu pourrais rendre dispo les sources de tes programmes de test ?

    Merci d'avance.
    • [^] # Re: sources

      Posté par  . Évalué à 2.

      Mais bien sûr !
      J'ai essayé d'avoir une version python et une version perl très proches, alors que par méconnaissance la version C++ est plus éloignée.
      Bon, je viens de m'apercevoir que ma fonction is_premier déconnait : 4012009 n'est pas premier ! J'avais fait boulette sur la fonction modulo, enfin c'est pas grave ça change pas les résultats :)
      Version C++
      #include
      bool is_premier (int nombre) {
      int i;
      for (i=1 ; i < nombre ; i++) {
      if (nombre % i == 0)
      return false;
      }
      return true;
      }
      int main (int argc, char **argv) {
      if (is_premier(4012009)) std::cout << "4012009 est premier\n";
      else std::cout << "4012009 n'est pas premier\n";
      }
      Version Perl
      #!/usr/bin/env perl

      sub is_premier {
      my ($nombre) = @_;
      my $i;
      foreach $i (1..($nombre-1)) {
      if ($nombre%$i == 0) {
      return 0;
      }
      }
      return 1;
      }

      if (is_premier(4012009)) {
      print "4012009 est premier\n"
      } else {
      print "4012009 n'est pas premier\n"
      }
      Version Python
      #!/usr/bin/env python
      import psyco #à commenter pour
      psyco.full() # désactiver psyco

      def is_premier(nombre):
      for i in range(1, nombre):
      if (nombre%i == 0):
      return 0
      return 1

      if (is_premier (4012009)):
      print "4012009 est premier"
      else:
      print "4012009 n'est pas premier"

      Ce sera tout ?
      • [^] # Re: sources

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

        Je viens d'essayer avec une version ruby en plus, et a ma grande surprise, ruby étant connu pour être très lent, ca va quand même beaucoup plus vite que python sans psyco

        pascal@jc:~/b$ time ruby test.rb
        4012009 n'est pas premier
        0.01user 0.00system 0:00.00elapsed 111%CPU (0avgtext+0avgdata 0maxresident)k
        0inputs+0outputs (262major+146minor)pagefaults 0swaps

        pascal@jc:~/b$ time perl test.pl
        4012009 n'est pas premier
        0.01user 0.00system 0:00.00elapsed 200%CPU (0avgtext+0avgdata 0maxresident)k
        0inputs+0outputs (352major+61minor)pagefaults 0swaps

        pascal@jc:~/b$ time python test.py
        4012009 n'est pas premier
        0.40user 0.21system 0:00.69elapsed 87%CPU (0avgtext+0avgdata 0maxresident)k
        0inputs+0outputs (487major+16039minor)pagefaults 0swaps

        Le code ruby :
        def is_premier(nombre)
        (2..nombre-1).each { |i|
        return false if (nombre%i == 0)
        }
        return true;
        end

        if (is_premier(4012009))
        print "4012009 est premier\n"
        else
        print "4012009 n'est pas premier\n"
        end
        • [^] # Re: sources

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

          Ça depend aussi de la fonction utilisé sur range.
          En prenant xrange, j'ai ça :

          /tmp $ time python p.py
          4012009 n'est pas premier
          0.99user 0.40system 0:02.88elapsed 48%CPU (0avgtext+0avgdata 0maxresident)k
          0inputs+0outputs (0major+16442minor)pagefaults 0swaps


          /tmp $ perl -pi 's/range/xrange/' p.py
          /tmp $ time python p.py
          4012009 n'est pas premier
          0.05user 0.01system 0:00.07elapsed 89%CPU (0avgtext+0avgdata 0maxresident)k
          0inputs+0outputs (0major+680minor)pagefaults 0swaps

          /tmp $ grep psyco p.py
          /tmp $ ( echo 'import psyco' ; echo "psyco.full()" ; cat p.py ) > p2.py
          /tmp $ time python p2.py
          4012009 n'est pas premier
          0.06user 0.02system 0:00.29elapsed 30%CPU (0avgtext+0avgdata 0maxresident)k
          0inputs+0outputs (2major+838minor)pagefaults 0swaps

          /tmp $ time ruby p.rb
          4012009 n'est pas premier
          0.02user 0.00system 0:00.86elapsed 3%CPU (0avgtext+0avgdata 0maxresident)k
          0inputs+0outputs (14major+380minor)pagefaults 0swaps

          /tmp $ time perl p.pl
          4012009 n'est pas premier
          0.00user 0.00system 0:00.01elapsed 63%CPU (0avgtext+0avgdata 0maxresident)k
          0inputs+0outputs (0major+411minor)pagefaults 0swaps

          Ou on voit que le fait de compiler avec psyco a un impact non négligeable sur le bench, quand on utilise xrange. Il faudrait donc le faire tourner sur un nombre bien plus grand ( et surtout premier ).
        • [^] # Re: sources

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

          je suis désolé mais ce bench mesure beaucoup plus le startup time de l'interpréteur que quoi que ce soit d'autre...

          pour avoir un bench correct, se renseigner auprès de nombreux sites qui l'ont déjà fait de manière sérieuse.
          • [^] # Re: sources

            Posté par  . Évalué à 1.

            Pourrais tu fournir un tel lien ?
            Pourrais tu aussi expliquer en quoi ce bench mesure le startup time ? Et pourquoi python + psyco est mille fois plus rapide que python seul ? Si y'a que le startup time, alors psyco est magique !
            Merci d'avance
            • [^] # Re: sources

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

              Pour la différence python/psyco, je suis d'accord avec toi. Mais ça n'invalide pas le fait que ton bench mesure le startup time en outre de la vitesse d'exécution. Ce startup time varie grandement entre les différents interpréteurs, et à moins de vouloir le comparer explicitement, pour mesurer la "vraie" vitesse d'un langage on l'exclue car elle devient négligeable sur des programmes non triviaux.

              Un bench souvent considéré comme une bonne référence (mais les données sont anciennes) :

              http://www.bagley.org/~doug/shootout/index3.shtml(...)

              Je n'ai jamais regardé les détails de la mesure des valeurs sur ce test, cependant.
          • [^] # Re: sources

            Posté par  . Évalué à 2.

            Tout à fait d'accord. Il serait mieux de faire la comparaison de temps utilisé directement dans le programme.

            - Regarder l'heure qu'il est (debut)
            - Lancer is_premier() sur n nombres, avec n un nombre suffisamment grand.
            - Re-regarder l'heure qu'il est (fin)
            - Faire la différence entre debut et fin et diviser le tout par n

            Hop, on a le temps de calcul moyen pour la recherche d'un nombre premier (avec les temps d'appel de fct°, de passage de paramètres, etc). Et ce sans passer par la phase de précompilation, de compilation JIT, de lancement de l'application, etc. Là, le bench serait plus intéressant et réaliste.

            D'ailleurs j'aimerai bien savoir ce que ça donne...
      • [^] # Re: sources optimise

        Posté par  . Évalué à 1.

        rien ne vaut un code optimiser
        Version Python

        #!/usr/bin/env python
        import psyco #à commenter pour
        psyco.full() # désactiver psyco

        def is_premier(nombre):
        for i in range(1, nombre/2): # pas besoin de chercher plus loin
        if (nombre%i == 0):
        return 0
        return 1

        if (is_premier (4012009)):
        print "4012009 est premier"
        else:
        print "4012009 n'est pas premier"

        ok je ---> []
        • [^] # Re: sources optimise

          Posté par  . Évalué à 0.

          Ho, tu veux optimiser ?
          def is_premier (nombre):
            if nombre in [2,3,5,7]:
              return 0
            for i in range(9,nombre/2,2): # pourquoi vérifier les nombres pairs ?
              if (nombre%i == 0):
                return 0
            return 1
          • [^] # Re: sources optimise

            Posté par  . Évalué à 1.

            oui et j ai meme mieu
            def is_premier (nombre):
            if nombre in [2,3,5,7]:
            return 0
            for i in range(9,nombre,2): # pourquoi vérifier les nombres pairs ?
            if i > nombre/i : # pourquoi aller plus loin (= range max racine_carree(nombre) )
            return 0
            if (nombre%i == 0):
            return 0
            return 1
            • [^] # Re: sources optimise

              Posté par  . Évalué à 1.

              from math import sqrt

              def is_premier (nombre):
                if nombre in [2,3,5,7]:
                return 0
                for i in range(9,int(sqrt(nombre)+1),2):
                  if (nombre%i == 0):
                    return 0
                return 1
              • [^] # Re: sources optimise

                Posté par  . Évalué à 1.

                avec ce code je trouve que 12 est un nombre premier mais j ai un doute ;)
  • # Psyco

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

    Je vais parfois me baser sur ce bench pour répondre, ne me tenez pas rigueur si je ne donne pas de généralités. Ensuite, si je dis des conneries, merci de corriger, ou de compléter. Je préviens que je n'ai plus été voir les nouveautés de psyco depuis longtemps.

    python est très lent sans psyco : c'est pas nouveau, mais c'est du à quoi ???
    100% interprété.
    Tout objet. faire « x = 0.2 * 0.7 » revient à instancier 3 floats, etc...
    Le code python est compilé en bytecode avant interprétation, mais c'est tout. Psyco compile ce qu'il peut en natif.

    y a-t-il des défauts provoqués par psyco (conso mémoire, CPU...) ?
    Pas à ma connaissance. Peut être la lecture du site de psyco pourrait-elle t'en dire plus à ce sujet ?

    du code python risque-t-il de ne pas s'exécuter à cause de python ?
    Je présume que tu voulais dire "à cause de psyco". Normalement, non. Si psyco ne peut pas compiler un truc, il ne le compile pas.

    pourquoi psyco n'est pas dans la lib python standard ?
    Très facile : c'est un gros hack pas portable. Ca ne fonctionne que sur x86, à ma connaissance.

    pourquoi continue-t-on à dire "python est lent" [...] ?
    Peut-être parce que sur un bench comme celui-ci, sans psyco, il est lent ? :)

    Personnellement, je ne trouve pas que la "lenteur" (toute relative) de python soit rédhibitoire. Une interface en pyqt/pykde/wxpython/pygtk est bigrement plus réactive qu'une interface en java-gtk-machin, même sans psyco.

    J'utilise maintenant le Python pour l'énorme majorité de mes développements, et j'en suis pleinement satisfait (ne me faites pas dire ce que je n'ai pas dit, il n'est pas parfait, ni meilleur que les autres langages).

    Note aussi que ton bench semble particulièrement adapté à psyco...
  • # A propos de Psyco

    Posté par  . Évalué à 3.

    cf http://psyco.sourceforge.net/introduction.html(...)

    En gros c'est de la compilation JIT mais la ou c'est très fort, ce que pour un même code, plusieurs versions compilées sont générées en fonction de paramètres d'entrés. Donc si les paramèters le permettent, la fonction est bien optimisée.

    cf les drawback :
    - ca mange de la RAM (toutes les versions, les différents état etc...)
    - ca marche uniquement sur x86
    - "here are some subtle semantic differences (i.e. bugs)"
    • [^] # Re: A propos de Psyco

      Posté par  . Évalué à 2.

      C'est assez rare les warnings ou les bugs à cause de psyco. Il y en a cependant (même si ça marche toujours très bien) dans la conversion de charsets.

      Sinon, j'ajoute encore un drawback : le programme devient relativement long à démarrer (ben oui, faut compiler). Surtout avec full() qui compile tout et pas en fonction des besoins. Dans mes applications, j'utilise surtout profile() (comme c'est recommandé), et qui ne compile que ce qu'il est vraiment nécessaire de compiler.

      J'ajoute un avantage : la possibilité de définir les fonctions à compiler, ou à partir de quel niveau compiler une fct°, etc.
  • # Correction du code python

    Posté par  . Évalué à 2.

    Voici une correction du code python qui permet de vraiment faire le calcul pour savoir si un nombre est, ou non, premier. La différence entre du code optimisé avec psyco et sans cette optimisation est flagrante dans ce genre d'exemple. Mais bon, c'est aussi que c'est juste dans ce genre de fonctions appellées un nombre indéfinissables de fois que psyco excelle.
    #! /usr/bin/env python
    from time import time
    #import psyco
    #psyco.full()
    
    MAX=10000
    
    def is_premier( nombre ):
    	for i in range( 2, nombre/2 ):
    		if nombre % i == 0: return 0
    #	print "%d est un nombre premier" % nombre
    	return 1
    
    start = time()
    for n in range( 0, MAX ): is_premier( n )
    end = time()
    
    print "tps unitaire: %fs" % ( ( end - start ) / MAX )
    print "total: %.03fs" % ( end - start )
    
    Nota: la correction consiste à ne pas tester la division par 1, car sinon le modulo est toujours égal à zéro ... donc le chiffre quel qu'il soit est détecté comme n'étant pas premier. Normal tout nombre est divisible par 1 et par lui même... Même les nombres premiers ^^ À noter aussi que ce genre de fonction n'est pas tip top pour effectuer des benchs, car plus le nombre MAX sera grand plus la fonction sera longue à appeller (à cause du range)... Résultat il faut obligatoirement utiliser la mm valeur MAX quelque soit le langage utilisé pour faire des benchs. D'ailleurs, avec un MAX de 10000, j'obtient 0.016ms par appel à is_premier() en utilisant psyco et 0.296ms par appel sans l'utiliser. No comment.
    • [^] # Re: Correction du code python

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

      On commence en effet à voir une nette différence :
      • Ruby :
        $ ruby premier.rb
        total: 5.35121s
      • Python sans psyco :
        $ python premier.py
        total: 3.977s
      • Python avec psyco :
        $ python premier.py 
        total: 0.266s

Suivre le flux des commentaires

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