- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
为了编写一个事件驱动的模拟器,我依赖于 simpy ,它大量使用 Python 生成器。我试图了解如何尽可能快地制作生成器,即最小化状态保存/恢复开销。我尝试了三种选择
并使用 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 个变量访问生成的代码 a
、b
和 self.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_GLOBAL
和 LOAD_ATTR
的操作数分别是对名称 a
和 c
的引用;这些数字是表格上的索引。 LOAD_FAST
的操作数是局部变量在局部变量表的表中的编号。
关于python - 如何使 Python 生成器尽可能快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37675026/
我知道在 KDB 中,如果您有一个列表,例如... l:`apples`oranges`pears` 您可以像下面这样进行 N 次随机选择: 9?l 但是如何尽可能均匀地选择列表中的每个项目? 最佳答
我真的厌倦了它。我有一个高级 Web 应用程序依赖于大量 Javascript 库(jQuery、jQueryUI、OpenLayers、highcharts、EJSChart 等等)。不用说,Int
我是一名优秀的程序员,十分优秀!