73 votes

Équivalence en Python des fonctions ou macros en ligne

Je viens de réaliser que faire

x.real*x.real+x.imag*x.imag

est trois fois plus rapide que de faire

abs(x)**2

où x est un tableau numpy de nombres complexes. Pour la lisibilité du code, je pourrais définir une fonction telle que

def abs2(x):
    return x.real*x.real+x.imag*x.imag

ce qui est toujours beaucoup plus rapide que abs(x)**2, mais au prix d'un appel de fonction. Est-il possible de mettre en ligne une telle fonction, comme je le ferais en C en utilisant une macro ou le mot-clé inline ?

14 votes

Si vous avez besoin de ce genre d'optimisations, vous devez probablement utiliser quelque chose comme Cython.

8 votes

PyPy à la rescousse !

12 votes

Si vous vous souciez de ces petites optimisations, vous devriez utiliser le C, pas Python. Python n'a rien à voir avec la vitesse, vraiment.

46voto

delnan Points 52260

Est-il possible de mettre en ligne une telle fonction, comme je le ferais en C à l'aide d'une macro ou du mot-clé inline ?

Non. Avant d'atteindre cette instruction spécifique, les interprètes Python ne savent même pas si une telle fonction existe, et encore moins ce qu'elle fait.

Comme indiqué dans les commentaires, PyPy sera automatiquement mis en ligne (ce qui précède est toujours valable - il génère "simplement" une version optimisée au moment de l'exécution, en tire profit, mais s'en détache lorsqu'elle est invalidée), bien que dans ce cas précis, cela n'aide pas car l'implémentation de NumPy sur PyPy a commencé il y a peu et n'est même pas encore au niveau bêta à ce jour. Mais l'essentiel est que : Ne vous inquiétez pas des optimisations à ce niveau en Python. Soit les implémentations l'optimisent elles-mêmes, soit elles ne le font pas, ce n'est pas votre responsabilité.

37voto

irdb Points 11

Pas exactement ce que le PO a demandé, mais presque :

Inliner inline les appels aux fonctions Python. Preuve de concept pour ce blog article

from inliner import inline

@inline
def add_stuff(x, y):
    return x + y

def add_lots_of_numbers():
    results = []
    for i in xrange(10):
         results.append(add_stuff(i, i+1))

Dans le code ci-dessus, la fonction add_lots_of_numbers est convertie en ceci :

def add_lots_of_numbers():
    results = []
    for i in xrange(10):
         results.append(i + i + 1)

Les personnes intéressées par cette question et par les complications liées à la mise en œuvre d'un tel optimiseur dans CPython peuvent également consulter le site suivant :

10voto

Alex Gaynor Points 6217

Je suis d'accord avec tous les autres pour dire que de telles optimisations ne font que vous faire souffrir. sur CPython que si vous vous souciez des performances, vous devriez envisager PyPy (bien que notre NumPy soit trop incomplet pour être utile). Cependant, je ne suis pas d'accord et je dis que vous pouvez vous préoccuper de telles optimisations sur PyPy, pas celle-là en particulier, comme cela a été dit, PyPy le fait automatiquement, mais si vous connaissez bien PyPy, vous pouvez vraiment régler votre code pour que PyPy émette l'assemblage que vous voulez, même si vous n'en avez presque jamais besoin.

9voto

Thaddee Tyl Points 661

Non.

Ce qui se rapproche le plus des macros C est un script (awk ou autre) que vous pouvez inclure dans un makefile, et qui substitue un certain motif comme abs(x)**2 dans vos script python avec la forme longue.

22 votes

... ce qui est une idée horrible, beaucoup de travail supplémentaire et une chance décente de casse obscure pour un gain pratique presque nul.

1 votes

De toute façon, Python n'est pas le langage le plus rapide qui soit, ce qui est normal en raison de ses cycles de développement rapides. L'ajout d'une étape de "prétraitement" pour un nouveau projet Python est en effet fortement déconseillé.

20 votes

Il n'a pas prétendu que c'était une bonne idée. Techniquement, il a raison.

7voto

eat Points 4573

En fait, ça pourrait être encore plus rapide à calculer, comme :

x.real** 2+ x.imag** 2

Ainsi, le surcoût lié à l'appel de fonction est susceptible de diminuer. Voyons voir :

In []: n= 1e4
In []: x= randn(n, 1)+ 1j* rand(n, 1)
In []: %timeit x.real* x.real+ x.imag* x.imag
10000 loops, best of 3: 100 us per loop
In []: %timeit x.real** 2+ x.imag** 2
10000 loops, best of 3: 77.9 us per loop

Et en encapsulant le calcul dans une fonction :

In []: def abs2(x):
   ..:     return x.real** 2+ x.imag** 2
   ..: 
In []: %timeit abs2(x)
10000 loops, best of 3: 80.1 us per loop

De toute façon (comme d'autres l'ont souligné) ce genre de micro-optimisation (afin d'éviter un appel de fonction) n'est pas vraiment une façon productive d'écrire du code python.

1 votes

~3us peut ne pas être beaucoup si vous faites quelque chose 100 fois, ou 10000 fois. Si vous le faites un million de fois, vous voudrez réduire ce temps de réponse.

1 votes

@MrMesees il y a un C pour cela

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X