python - 为什么 contextmanager 很慢

标签 python performance with-statement

一位同事向我指出,with 语句可能很慢。因此,我进行了测量,发现从 contextmanager 函数中获取值的时间确实比从 Python 2.7 中的生成器中获取值的时间长 20 倍,在 PyPy 2.6 中甚至长 200 倍。

为什么会这样?是否可以重写 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

最佳答案

使用分析器和 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() 更快; 函数调用很昂贵;异常处理是昂贵的......所以比较是不公平的,这个分析只是为了好玩。

它还告诉我们,每次执行“with” block 时,都会创建一个上下文管理器实例(几乎不可避免)。除此之外,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

关于python - 为什么 contextmanager 很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34872535/

相关文章:

python - 猜谜游戏循环中倒计时的机会

python - 如果一次读取整个文件需要 with 语句?

python - 进行此搜索的最有效方法是什么(mysql 或文本)?

python - 在 Windows 上安装 python mysql 模块时出错?

C++:与每次内联输入验证函数相比,对输入验证函数进行编程对性能有多大影响?

sql-server - 为什么/何时/如何选择整个聚集索引扫描而不是全表扫描?

types - 如何在 "with"子句中使用 Refl

php - 在 php 中使用语句

Python 数据类型与类?

java - 使用正则表达式的性能开销/改进