Module dis de Python et convolution de constantes

Bonjour à tous. Aujourd'hui, nous voulons partager une autre traduction préparée en prévision du lancement du cours "Développeur Web en Python" . C'est parti!



J'ai été très surpris récemment quand j'ai découvert que


>>> pow(3,89) 

fonctionne plus lentement que


 >>> 3**89 

J'ai essayé de trouver une explication acceptable, mais je n'ai pas pu. J'ai suivi le temps d'exécution de ces deux expressions en utilisant le module timeit de Python 3:


 $ python3 -m timeit 'pow(3,89)' 500000 loops, best of 5: 688 nsec per loop $ python3 -m timeit '3**89' 500000 loops, best of 5: 519 nsec per loop 

La différence est petite. Seulement 0,1 μs, mais cela me hantait. Si je ne peux rien expliquer dans la programmation, je commence à souffrir d'insomnie


J'ai trouvé la réponse en utilisant le flux IRC Python sur Freenode. La raison pour laquelle pow fonctionne un peu plus lentement est que CPython a déjà une étape supplémentaire de chargement de pow depuis l'espace de noms. Alors qu'en appelant 3 ** 9, une telle charge n'est pas nécessaire en principe. Cela signifie également que cette différence de temps restera plus ou moins constante si les valeurs d'entrée augmentent.


L'hypothèse a été confirmée:


 $ python3 -m timeit 'pow(3,9999)' 5000 loops, best of 5: 58.5 usec per loop $ python3 -m timeit '3**9999' 5000 loops, best of 5: 57.3 usec per loop 

Dans le processus de recherche d'une solution à ce problème, j'ai également découvert le module dis. Il vous permet de décompiler le bytecode Python et de l'apprendre. Ce fut une découverte extrêmement excitante, car récemment j'ai étudié l'ingénierie inverse des fichiers binaires, et le module découvert a été utile à cet égard.


J'ai décompilé le bytecode des expressions ci-dessus et j'ai obtenu ce qui suit:


 >>> import dis >>> dis.dis('pow(3,89)') # 1 0 LOAD_NAME 0 (pow) # 2 LOAD_CONST 0 (3) # 4 LOAD_CONST 1 (89) # 6 CALL_FUNCTION 2 # 8 RETURN_VALUE >>> dis.dis('3**64') # 1 0 LOAD_CONST 0 (3433683820292512484657849089281) # 2 RETURN_VALUE >>> dis.dis('3**65') # 1 0 LOAD_CONST 0 (3) # 2 LOAD_CONST 1 (65) # 4 BINARY_POWER # 6 RETURN_VALUE 

Vous pouvez lire comment comprendre correctement la sortie de dis.dis en vous référant à cette réponse sur Stackoverflow.


Ok, revenons au code. Décompiler la poudre a du sens. Le bytecode charge pow depuis l'espace de noms, se charge dans les registres 3 et 89, et appelle finalement la fonction pow. Mais pourquoi les sorties des deux décompilations suivantes sont-elles différentes? Après tout, tout ce que nous avons changé, c'est la valeur de l'exposant de 64 à 65!


Cette question m'a présenté un autre nouveau concept appelé «convolution de constantes». Cela signifie que lorsque nous avons une expression constante, Python calcule sa valeur au stade de la compilation, donc lorsque vous exécutez le programme, cela ne prendra pas beaucoup de temps, car Python utilise la valeur déjà calculée. Jetez un œil à ceci:


 def one_plue_one(): return 1+1 # --vs-- def one_plue_one(): return 2 

Python compile la première fonction dans la seconde et l'utilise lors de l'exécution du code. Pas mal, hein?


Alors pourquoi la convolution des constantes fonctionne-t-elle pour 3 ** 64, mais pas pour 3 ** 65? Eh bien, je ne sais pas. Ceci est probablement lié en quelque sorte à la limitation du nombre de degrés précédemment calculé par le système en mémoire. Je peux me tromper. La prochaine étape que je prévois de prendre est de creuser dans le code source Python pendant mon temps libre et d'essayer de comprendre ce qui se passe. Je cherche toujours une réponse à ma question, donc si vous avez des idées, partagez-les dans les commentaires.


Je veux que vous vous inspiriez de cet article pour trouver vous-même une solution à vos problèmes. Vous ne savez jamais où les réponses vous mèneront. En fin de compte, vous pouvez apprendre quelque chose de complètement nouveau, comme cela m'est arrivé. J'espère que la flamme de la curiosité brûle toujours en vous!


Avez-vous remarqué des choses similaires? En attendant vos commentaires!

Source: https://habr.com/ru/post/fr460143/


All Articles