gpt4 book ai didi

python - 如何使 Python 生成器尽可能快?

转载 作者:太空狗 更新时间:2023-10-30 01:43:26 26 4
gpt4 key购买 nike

为了编写一个事件驱动的模拟器,我依赖于 simpy ,它大量使用 Python 生成器。我试图了解如何尽可能快地制作生成器,即最小化状态保存/恢复开销。我尝试了三种选择

  1. 存储在类实例中的所有状态
  2. 全局存储的所有状态
  3. 所有状态存储在本地

并使用 Python 3.4.3 得到以下结果:

class_generator 20.851247710175812
global_generator 12.802394330501556
local_generator 9.067587919533253

可以查到代码here .

这对我来说感觉违反直觉:在类实例中存储所有状态意味着只有 self 需要保存/恢复,而全局存储所有状态应确保零保存/恢复开销。

有人知道为什么类生成器和全局生成器比本地生成器慢吗?

最佳答案

yield 发生时,生成器实际上保留了实际的调用帧。无论您有 1 个还是 100 个局部变量,它都不会真正影响性能。

性能差异实际上来自 Python(这里我使用的是 CPython,也就是你从 http://www.python.com/ 下载的那个,或者在你的操作系统上作为 /usr/bin/python,但由于大多数相同的原因,大多数实现会具有相似的性能特征)在不同类型的变量查找上的行为:

  • 局部变量在 Python 中实际上没有命名;相反,它们由一个数字 引用,并由LOAD_FAST 操作码访问。

  • 使用LOAD_GLOBAL 操作码访问全局变量。它们总是按名称引用,因此每次访问都需要实际的字典查找。

  • 实例属性访问是最慢的,因为self.foobar首先需要使用LOAD_FAST加载对self的引用,然后 LOAD_ATTR 用于在引用对象上查找 foobar,这是一个字典查找。此外,如果该属性在实例 本身上,这没有问题,但如果它在 上设置,则属性查找会变慢。您还在实例上设置 值,它会更慢,因为现在它需要在加载的实例上执行 STORE_ATTR。更复杂的是,实例的 class 也需要被查询 - 如果 class 碰巧有一个 property descriptor同名,那么它可以改变读取和设置属性的行为。

因此,最快的生成器是仅引用局部变量的生成器。将全局只读变量的值存储到局部变量以加快速度是 Python 代码中的一个常见习惯用法。

为了演示差异,考虑为这 3 个变量访问生成的代码 abself.c:

a = 42

class Foo(object):
def __init__(self):
self.c = 42

def foo(self):
b = 42
yield a
yield b
yield self.c

print(list(Foo().foo())) # prints [42, 42, 42]

foo 方法反汇编的相关部分是:

  8           6 LOAD_GLOBAL              0 (a)
9 YIELD_VALUE
10 POP_TOP

9 11 LOAD_FAST 1 (b)
14 YIELD_VALUE
15 POP_TOP

10 16 LOAD_FAST 0 (self)
19 LOAD_ATTR 1 (c)
22 YIELD_VALUE
23 POP_TOP

LOAD_GLOBALLOAD_ATTR 的操作数分别是对名称 ac 的引用;这些数字是表格上的索引。 LOAD_FAST的操作数是局部变量在局部变量表的表中的编号

关于python - 如何使 Python 生成器尽可能快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37675026/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com