In [2]: %timeit Class1().runCached(10000)
1000 loops, best of 3: 1.74 ms per loop
In [3]: %timeit Class1().runNormal(10000)
100 loops, best of 3: 2.92 ms per loop
In [4]: %timeit Class10().runCached(10000)
1000 loops, best of 3: 1.7 ms per loop
In [5]: %timeit Class10().runNormal(10000)
100 loops, best of 3: 6.01 ms per loop
In [6]: %timeit Class100().runCached(10000)
1000 loops, best of 3: 1.7 ms per loop
In [7]: %timeit Class100().runNormal(10000)
10 loops, best of 3: 42.9 ms per loop
节省的不是时间复杂性,而是实际时间。在命名空间中查找函数名就是在字典中查找键,它已经是O(1)。又是一个查找对象(dict也是一个查找对象)。有一个优化的操作码,用于按名称查找局部变量,但它仍然不能比O(1)快。在
在您的示例中,查找
self.printMethod
将查找本地(self
),然后查找属性(printMethod
)。这是两次查找。如果将其存储在一个local中,那么对局部变量printMethod
的每次后续访问都只是一次查找,而不是两次。仍然是O(1),但速度更快,因为它是一个较小的常数。在This question进一步讨论了Python中名称查找的工作方式。在
下面是一些代码,您可以用来计算时间差:
http://pastebin.com/svBN5NZ9
以及一些计时结果:
因此,在一般情况下,缓存方法的速度更快,方法查找时间取决于类继承层次结构的深度。在
但是请注意,如果使用像pypy这样的跟踪JIT,可能会得到不同的结果,因为跟踪可能会有效地为您缓存方法指针。在
两个O(1)操作所需的时间可能非常不同。实例属性查找(
self.printMethod
)和局部变量都是O(1),但局部变量访问经过优化,不需要字典查找,因此速度更快。查看访问CPython中的局部变量vs实例变量的字节码:您可以看到,访问}需要额外的
pm
需要一个简单的LOAD_FAST
操作,该操作从本地堆栈框架中的固定数值offest加载一个值,而访问{LOAD_ATTR
操作。在当然,建立局部变量的值确实需要时间,因此必须多次使用它(就像在代码示例中一样)才能看到任何性能优势。在
正如@user5402指出的,由于编译器的优化,您的里程数可能会因您使用的实现而有所不同。在
相关问题 更多 >
编程相关推荐