在Python中,为什么局部变量访问比类成员访问快?
在试图解决一个更复杂的问题时,我比较了局部变量和成员变量的访问速度 这里有一个测试程序:在Python中,为什么局部变量访问比类成员访问快?,python,performance,benchmarking,Python,Performance,Benchmarking,在试图解决一个更复杂的问题时,我比较了局部变量和成员变量的访问速度 这里有一个测试程序: #!/usr/bin/env python MAX=40000000 class StressTestMember(object): def __init__(self): self.m = 0 def do_work(self): self.m += 1 self.m *= 2 class StressTestLocal(object
#!/usr/bin/env python
MAX=40000000
class StressTestMember(object):
def __init__(self):
self.m = 0
def do_work(self):
self.m += 1
self.m *= 2
class StressTestLocal(object):
def __init__(self):
pass
def do_work(self):
m = 0
m += 1
m *= 2
# LOCAL access test
for i in range(MAX):
StressTestLocal().do_work()
# MEMBER access test
for i in range(MAX):
StressTestMember().do_work()
我知道在每次迭代中实例化StressTestMember
和StressTestLocal
看起来可能不是个好主意,但在建模程序中,这些基本上是活动记录,这是有意义的
在一个简单的基准之后
- 本地访问测试:0m22.836
- 成员访问测试:0m32.648s
本地版本在仍然是类的一部分的情况下,速度提高了约33%。为什么?本地名称更快,因为Python进行了一些优化,使本地名称不需要dict访问,另一方面,实例属性需要访问对象的
\uu dict\ucode>
这也是为什么本地名称比全局名称快的原因。本地名称更快,因为Python进行了一些优化,使本地名称不需要dict访问,另一方面,实例属性需要访问对象的\uuu dict\uu
这也是为什么局部名称比全局名称快的原因。self.m+=1
意味着您必须查找名为self
的局部变量,然后找到名为m
当然,如果你只需要查找一个局部变量,不需要额外的步骤,它会更快
查看发动机罩下发生的情况可能很有用:
>>> import dis
>>> dis.dis(StressTestLocal.do_work)
18 0 LOAD_CONST 1 (0)
3 STORE_FAST 1 (m)
19 6 LOAD_FAST 1 (m)
9 LOAD_CONST 2 (1)
12 INPLACE_ADD
13 STORE_FAST 1 (m)
20 16 LOAD_FAST 1 (m)
19 LOAD_CONST 3 (2)
22 INPLACE_MULTIPLY
23 STORE_FAST 1 (m)
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
>>> dis.dis(StressTestMember.do_work)
10 0 LOAD_FAST 0 (self)
3 DUP_TOP
4 LOAD_ATTR 0 (m)
7 LOAD_CONST 1 (1)
10 INPLACE_ADD
11 ROT_TWO
12 STORE_ATTR 0 (m)
11 15 LOAD_FAST 0 (self)
18 DUP_TOP
19 LOAD_ATTR 0 (m)
22 LOAD_CONST 2 (2)
25 INPLACE_MULTIPLY
26 ROT_TWO
27 STORE_ATTR 0 (m)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE
self.m+=1
意味着您必须查找名为self
的局部变量,然后找到名为m
当然,如果你只需要查找一个局部变量,不需要额外的步骤,它会更快
查看发动机罩下发生的情况可能很有用:
>>> import dis
>>> dis.dis(StressTestLocal.do_work)
18 0 LOAD_CONST 1 (0)
3 STORE_FAST 1 (m)
19 6 LOAD_FAST 1 (m)
9 LOAD_CONST 2 (1)
12 INPLACE_ADD
13 STORE_FAST 1 (m)
20 16 LOAD_FAST 1 (m)
19 LOAD_CONST 3 (2)
22 INPLACE_MULTIPLY
23 STORE_FAST 1 (m)
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
>>> dis.dis(StressTestMember.do_work)
10 0 LOAD_FAST 0 (self)
3 DUP_TOP
4 LOAD_ATTR 0 (m)
7 LOAD_CONST 1 (1)
10 INPLACE_ADD
11 ROT_TWO
12 STORE_ATTR 0 (m)
11 15 LOAD_FAST 0 (self)
18 DUP_TOP
19 LOAD_ATTR 0 (m)
22 LOAD_CONST 2 (2)
25 INPLACE_MULTIPLY
26 ROT_TWO
27 STORE_ATTR 0 (m)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE
因此,在局部范围内创建对类变量的新引用是否明智?e、 例如,m=self.m
?在这个测试中不会有任何区别,但我的do_work()
版本是一个运行数百万次的循环。@JamesS,如果它足够重要,那么最好测量您的确切用例来调整它。如果您多次引用self.m
,我希望能够更快地引入局部变量。记住,如果您对其进行了变异,请将局部变量保存回属性。因此,在局部范围中创建对类变量的新引用是否明智?e、 例如,m=self.m
?在这个测试中不会有任何区别,但我的do_work()
版本是一个运行数百万次的循环。@JamesS,如果它足够重要,那么最好测量您的确切用例来调整它。如果您引用了几次self.m
,我希望将其拉入局部变量会更快。记住,如果您对其进行了变异,请将局部变量保存回属性。