IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Différence de performances entre 2.7 et 3.3


Sujet :

Python

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut Différence de performances entre 2.7 et 3.3
    Bonjour,

    J'ai un script que j'ai décliné en version 2.7 et 3.3. Le script d'origine était celui en 3.3 où j'utilise timeit pour voir comparer la vitesse d'exécution de 2 fonctions. Surpris que la fonction que je pensais être la plus rapide était en fait beaucoup plus lente, j'ai alors testé en 2.7 pour lequel j'ai adapté le script. La fonction la plus rapide est toujours la plus rapide mais les temps des 2 fonctions sont beaucoup plus faibles en2.7. C'est cette différence que je souhaite expliquer.

    Code 2.7 : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import timeit
     
    def f(max):
        a = 0
        for i in range(0, max):
            a = a + i
        return a
     
    def g(max):
        a = 0
        i = 0
        while i < max:
            a = a + i
            i = i + 1
        return a
     
    # Check results are the same
    a = f(1000)
    print a
     
    a = g(1000)
    print a
     
    # See execution times
    print timeit.timeit("f(1000)", setup="from __main__ import f")
    print timeit.timeit("g(1000)", setup="from __main__ import g")
    Python 2.7 (r27:82525, Jul  4 2010, 07:43:08) [MSC v.1500 64 bit (AMD64)] on win32
    Type "copyright", "credits" or "license()" for more information.
    >>> ================================ RESTART ================================
    >>> 
    499500
    499500
    41.6989751267
    74.8646763445
    >>> 
    Code 3.3 : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import timeit
     
    def f(max):
        a = 0
        for i in range(0, max):
            a = a + i
        return a
     
    def g(max):
        a = 0
        i = 0
        while i < max:
            a = a + i
            i = i + 1
        return a
     
    # Check results are the same
    a = f(1000)
    print(a)
     
    a = g(1000)
    print(a)
     
    # See execution times
    print(timeit.timeit("f(1000)", setup="from __main__ import f"))
    print(timeit.timeit("g(1000)", setup="from __main__ import g"))

    Python 3.3.4 (v3.3.4:7ff62415e426, Feb 10 2014, 18:13:51) [MSC v.1600 64 bit (AMD64)] on win32
    Type "copyright", "credits" or "license()" for more information.
    >>> ================================ RESTART ================================
    >>> 
    499500
    499500
    82.55615388232152
    142.40860910955465
    >>> 
    Pourquoi Python 3.3 est-il beaucoup plus lent ?

    Bonne journée.

  2. #2
    Expert éminent

    Avatar de deusyss
    Homme Profil pro
    Expert Python
    Inscrit en
    Mars 2010
    Messages
    1 659
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Expert Python
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 659
    Points : 8 442
    Points
    8 442
    Par défaut
    Bonjour,

    Je m'avance peut etre, car je n'ai pas sous la main de façon de tester, mais il me semble qu'en branche 3.X, un certains nombres de fonctions ont été remplacée/fusionnée.

    Par exemple, input() correspond à raw_input() en branche 2.X. De même il me semble que le range() branche 3 équivaut au xrange() branche 2. Cela a été fait, je pense, dans un soucis de simplification, entre autre. Mais cela peux avoir des effets de bords.

    Donc sauf erreur de ma part ta différence de timing doit se situé à ce niveau. Essaie en modifiant et testant ton code sur cette base.
    "La connaissance appartient à tout le monde" (Film Antitrust)

    Tout le nécessaire pour Python:
    *News/Accueil *Cours/tutoriels *FAQ
    *Forums *Outils dédiés *Mon espace personnel avec mes Articles, Cours et Tutoriels

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    J'avais pensé à ça. En fait, j'avais dans l'idée ensuite de tester xrange.

    D'après ce que j'ai lu
    • xrange travaille comme un générateur avec yield, alors que range génère une liste (branche 2.7). xrange serait donc plus rapide si on itère une seule fois ce qui est le cas ici.
    • En branche 3.3, range tel qu'il est en branche 2.7 n'existe plus et xrange est devenu range. La performance devrait donc être à l'avantage de la branche 3.3.


    Histoire de voir, j'ai rajouté une fonction utilisant xrange et non range :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    def fx(max):
        a = 0
        for i in xrange(0, max):
            a = a + i
        return a
    Je regarde les temps :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    print timeit.timeit("f(1000)", setup="from __main__ import f")
    print timeit.timeit("fx(1000)", setup="from __main__ import fx")
    Ce qui donne :
    40.2306855892
    36.7023118224
    L'avantage va donc bien à fx comme attendue. La différence n'est pas énorme sans doute car la plage est petite.

    Tout ceci n'explique pas pourquoi g() est également beaucoup plus lente. D'ailleurs, je m'aperçois que le facteur de 2 pour les 2 fonctions. Hasard ? J'ai regardé timeit et son paramètre number : il a la même valeur par défaut en 2.7 et 3.3 sauf erreur de ma part... L'explication ne viendrait donc pas de là.

    EDIT : pour m'assurer de ce dernier point, j'ai rajouté une valeur explicite à number : 100000. Les résutats conservent ce facteur 2.
    Citation Envoyé par 2.7
    4.03874518115
    3.66527114159 # pour fx
    7.48002309017
    Citation Envoyé par 3.3
    8.47351248366718
    14.499518559653758

  4. #4
    Expert éminent

    Avatar de deusyss
    Homme Profil pro
    Expert Python
    Inscrit en
    Mars 2010
    Messages
    1 659
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Expert Python
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 659
    Points : 8 442
    Points
    8 442
    Par défaut
    Salut,

    vraiment etrange. Je pensais que ce que j'avais évoqué expliquait le constat que tu fais. Mais c'est clairement pas l'explication.

    Je n'ai pas python 3 d'installer sur ma machine, mais cela ne pourrait-il pas provenir de timeit directement? Pourrais tu essayer en comparant simplement 2 timestamp (via time.time()) afin de calculer le temps mis par ton code à s'exécuter. Cela nous indiquerait dejà si timeit reste fiable/coherent d'une branche à l'autre.
    "La connaissance appartient à tout le monde" (Film Antitrust)

    Tout le nécessaire pour Python:
    *News/Accueil *Cours/tutoriels *FAQ
    *Forums *Outils dédiés *Mon espace personnel avec mes Articles, Cours et Tutoriels

  5. #5
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    La fonction f est plus rapide que g parce que l'interpréteur python a moins de travail à exécuter dans la boucle.

    g contient l'instruction "i=i+1". On peut dire que f effectue aussi implicitement cette opération, dans la fonction range, mais range est implémenté en C et c'est nettement plus rapide que de le faire en Python.
    Aussi, une boucle for avec un itérable implémenté en C est généralement plus rapide qu'une boucle while, car la condition de sortie de la boucle est testé dans l'implémentation de l'itérable (en C), et pas en python.

    En guise d'illustration, voici le bytecode désassemblé de la boucle de la fonction f:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
            >>   22 FOR_ITER                16 (to 41) 
                 25 STORE_FAST               2 (i) 
     
      6          28 LOAD_FAST                1 (a) 
                 31 LOAD_FAST                2 (i) 
                 34 BINARY_ADD           
                 35 STORE_FAST               1 (a) 
                 38 JUMP_ABSOLUTE           22
    Et pour la fonction g:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
            >>   15 LOAD_FAST                2 (i) 
                 18 LOAD_FAST                0 (max) 
                 21 COMPARE_OP               0 (<) 
                 24 POP_JUMP_IF_FALSE       50 
     
     13          27 LOAD_FAST                1 (a) 
                 30 LOAD_FAST                2 (i) 
                 33 BINARY_ADD           
                 34 STORE_FAST               1 (a) 
     
     14          37 LOAD_FAST                2 (i) 
                 40 LOAD_CONST               2 (1) 
                 43 BINARY_ADD           
                 44 STORE_FAST               2 (i) 
                 47 JUMP_ABSOLUTE           15
    Bien sûr, toutes les instructions de bytecode codent ne se valent pas, mais ça donne une idée quand-même.

    [EDIT] J'ai conscience que ça n'explique en rien la différence de vitesse entre Python 2.7 et 3.3, mais la question de la différence de vitesse entre f et g a aussi été évoquée dans un post précédent

  6. #6
    Expert éminent

    Avatar de deusyss
    Homme Profil pro
    Expert Python
    Inscrit en
    Mars 2010
    Messages
    1 659
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Expert Python
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 1 659
    Points : 8 442
    Points
    8 442
    Par défaut
    Salut dividee,

    je ne sait pas effectivement si ça explique la difference de perf à elle toute seule, mais cette explication fort interessante, peut servir à optimiser un peu plus certains de nos codes. Merci beaucoup
    "La connaissance appartient à tout le monde" (Film Antitrust)

    Tout le nécessaire pour Python:
    *News/Accueil *Cours/tutoriels *FAQ
    *Forums *Outils dédiés *Mon espace personnel avec mes Articles, Cours et Tutoriels

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    J'avoue que cette explication tient la route. Il m'arrive de temps en temps de faire des objdump de mes codes C mais pour Python, je n'avais encore jamais pensé à faire une telle manipulation... La différence de temps me semble quand même énorme : Python est-il à ce point lent sur des calculs aussi simples que i = i + 1 ?!

Discussions similaires

  1. Réponses: 4
    Dernier message: 07/01/2010, 12h27
  2. [PC portable] Différence de performance entre processeur 2 Ghz et 2,5 GHz
    Par debdev dans le forum Ordinateurs
    Réponses: 5
    Dernier message: 02/11/2009, 12h41
  3. Différence de performance entre localhost et serveur
    Par Borowsky dans le forum Décisions SGBD
    Réponses: 5
    Dernier message: 10/09/2009, 00h56
  4. Réponses: 14
    Dernier message: 12/04/2009, 20h47
  5. Différence de performance entre JOIN et Subselect ?
    Par guidav dans le forum Requêtes
    Réponses: 1
    Dernier message: 20/07/2007, 10h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo