2016-01-19 2 views
3

Сотрудник указал мне, что with заявление может быть медленным. Таким образом, я измерил и, действительно, требуется 20 раз дольше, чтобы получить значение от функции contextmanager, чем от генератора в Python 2.7 и даже в 200 раз дольше в PyPy 2.6.Почему contextmanager медленный

Почему это так? Можно ли переписать contextlib.contextmanager(), чтобы работать быстрее?

Для справки:

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 

И тайминги:

$ 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 
+0

Тест, который обновляет контекстный менеджер с нуля каждый раз, не является особенно хорошим тестовым примером для стоимости _using_ менеджера контекста. Как правило, украшенная функция '@ contextmanager' определяется один раз, но используется много раз; это как отказ от использования 'class'es и функций, потому что использование' dict 'и помещение всего inline происходит быстрее. – ShadowRanger

+0

@ShadowRanger вы правы. Похоже, что «contextmanager» вносит большой вклад в замедление. И после этого замена декоратора на класс контекстного менеджера, поскольку @krrr предлагает ускорить работу даже больше. –

ответ

3

Использование профилировщика и источник contextlib, я нашел:

value_from_with: 
ncalls tottime cumtime filename:lineno(function) 
1000000 1.415 4.802 value_from_with # 1sec more than value_from_generator, likely caused by with statement 
1000000 1.115 1.258 contextlib.py:37(__init__) # better doc string of context manager instance 
1000000 0.656 0.976 contextlib.py:63(__exit__) # optional exception handling 
1000000 0.575 1.833 contextlib.py:124(helper) # "wrapped" in decorator 
2000000 0.402 0.604 {built-in method next} # why it's so expensive? 
1000000 0.293 0.578 contextlib.py:57(__enter__) # a next() call to the generator in try&except block (just for error msg) 
2000000 0.203 0.203 inner1 
1000000 0.143 0.143 {built-in method getattr} # better doc string, called by __init__ 

value_from_generator: 
ncalls tottime cumtime filename:lineno(function) 
1000000 0.416 0.546 value_from_generator 
2000000 0.130 0.130 inner2 

Он сказал нам: распаковка из генератора быстрее, чем использование next(); вызов функции стоит дорого; обработка исключений дорогая ... поэтому сравнение несправедливо, и это профилирование просто для удовольствия.

Он также сказал нам, что каждый раз, когда выполняется «с» блоком, создается экземпляр менеджера контекста (почти неизбежный). Кроме того, contextmanager проделал определенную работу, чтобы помочь нам. Если вы действительно хотите, чтобы оптимизировать его, вы можете написать класс контекста менеджера вместо того, чтобы использовать декоратор

профилированный код:

def inner1(): yield 1 

def value_from_generator(): 
    value, = inner1() 
    return value 

# inner should not be created again and again 
@contextmanager 
def inner2(): yield 1 

def value_from_with(): 
    with inner2() as value: 
     return value 
1

Эти два инструменты «немного» разные цели, поэтому сравнивать их производительность выиграл» я действительно ничего не показываю.

Контекстные менеджеры позволяют выполнять некоторые операции до и после выполнения кода в блоке with. Общее использование заключается в том, чтобы занять ресурсы при запуске, выполнить работу, выполнить очистку, т. Е. Соединения с БД, доступ к файлам и т. Д.

Генераторы позволяют вам выполнять функции, которые сохраняют состояние между вызовами. Общее использование для экономии ресурсов на ненужных (в определенный момент) вычислениях и экономии памяти при одновременном сохранении всех результатов операции. Поэтому первичное использование предназначено для вычислений.

+0

Действительно, генератор и менеджер контекста имеют совершенно разные цели. Я использовал генератор для сравнения, поскольку он предназначен для определения как часть менеджера контекста. –