web-dev-qa-db-fra.com

Python équivalence aux fonctions en ligne ou aux macros

Je viens de réaliser que faire

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

est trois fois plus rapide que de le faire

abs(x)**2

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

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

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

46
Charles Brunet

Est-il possible d'insérer une telle fonction, comme je le ferais en C en utilisant une macro ou un mot-clé en ligne?

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

Comme indiqué dans les commentaires, PyPy s'alignera automatiquement (ce qui précède tient toujours - il génère "simplement" une version optimisée à l'exécution, en profite, mais en éclate lorsqu'elle est invalidée), bien que dans ce cas spécifique qui n'aide pas car l'implémentation de NumPy sur PyPy a commencé il y a peu de temps et n'est même pas au niveau bêta à ce jour. Mais l'essentiel est: ne vous inquiétez pas des optimisations à ce niveau en Python. Soit les implémentations l'optimisent elles-mêmes, soit elles ne le sont pas, ce n'est pas votre responsabilité.

33
user395760

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

Inliner inlines Python. Preuve de concept pour cet article de blog

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)

Toute personne intéressée par cette question et les complications liées à la mise en œuvre d'un tel optimiseur dans CPython, pourrait également vouloir jeter un coup d'œil à:

13
AXO

Je conviens avec tout le monde que de telles optimisations ne feront que vous faire mal sur CPython, que si vous vous souciez des performances, vous devriez considérer PyPy (bien que notre NumPy puisse être trop incomplet pour être utile). Cependant, je ne suis pas d'accord et je vous dis que vous pouvez vous soucier de telles optimisations sur PyPy, pas celle-ci 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'assembly que vous voulez, pas que vous ayez besoin de presque jamais.

7
Alex Gaynor

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

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

Ainsi, le coût supplémentaire de l'appel de fonction diminuera probablement. 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 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

Quoi qu'il en soit (comme d'autres l'ont souligné), ce type de micro-optimisation (afin d'éviter un appel de fonction) n'est pas un moyen vraiment productif d'écrire python code).

6
eat

Non.

Le plus proche que vous pouvez obtenir des macros C est un script (awk ou autre) que vous pouvez inclure dans un makefile, et qui remplace un certain modèle comme abs (x) ** 2 dans votre python scripts avec la forme longue.

6
Thaddee Tyl

Vous pouvez essayer d'utiliser lambda:

abs2 = lambda x : x.real*x.real+x.imag*x.imag

puis appelez-le par:

y = abs2(x)
1
Linfeng Mu