Un collaborateur m'a fait remarquer que l'instruction with
peut être lente. J'ai donc mesuré et en effet il faut 20 fois plus de temps pour obtenir une valeur à partir d'une fonction contextmanager
que d'un générateur en Python 2.7 et même 200 fois plus en PyPy 2.6.Pourquoi contextmanager est lent
Pourquoi est-ce vrai? Est-il possible de réécrire contextlib.contextmanager()
pour courir plus vite?
Pour la référence:
def value_from_generator():
def inner(): yield 1
value, = inner()
return value
def value_from_with():
@contextmanager
def inner(): yield 1
with inner() as value:
return value
Et timings:
$ python -m timeit 'value_from_generator()'
10000000 loops, best of 3: 0.169 usec per loop
$ python -m timeit 'value_from_with()'
100000 loops, best of 3: 3.04 usec per loop
Un test qui recrée à chaque fois le gestionnaire de contexte n'est pas un cas de test particulièrement bon pour le coût d'utilisation d'un gestionnaire de contexte. En général, une fonction décorée '@ contextmanager' est définie une fois, mais utilisée plusieurs fois; c'est comme rejeter l'utilisation de 'class'es et de fonctions car utiliser' dict's et mettre tout en ligne est plus rapide. – ShadowRanger
@ShadowRanger vous avez raison. Il semble que le 'contextmanager' contribue beaucoup au ralentissement. Et après avoir remplacé le décorateur par une classe de gestionnaire de contexte, @krrr suggère d'accélérer les choses encore plus. –