python – `in`对`__contains__`有多少优化?
所以在itertools recipe部分,他们有一段代码如下:
seen = set() seen_add = seen.add 我想知道一个类似的想法是否可能弥补in和__contains__之间的一些性能差距.例如,使用以下代码: seen = set() seen_add = seen.add in_seen = seen.__contains__ for item in iterable: in_seen(item) VS seen = set() seen_add = seen.add in_seen = seen.__contains__ # make identical in beginning for item in iterable: item in seen 因此,如果我正确地读取dis的输出,问题归结为“x是y比func(x)快吗?” 编辑:对那些说无关紧要的人,我不是用它作为优化.我试图通过分开这个元素来更好地理解语言. 解决方法
我们最多谈论几十纳秒,所以通常没关系.而且,即使它确实如此,事情也比最初出现时更复杂.
预绑定看到.__包含_,因为看到_contains会比调用看到.__ contains__更快,但不会像看到的那样只使用(更明显和惯用). 那么,为什么这与seen_adds不同? 在seen.add()的情况下,您明确地创建并调用绑定方法,并且没有办法解决这个问题.因此,创建绑定方法一次,而不是每次…仍然通常不值得,但在极少数情况下,当您需要保存纳秒时,这是一个胜利. 在看到的情况下,您没有显式创建绑定方法,您只是在评估运算符.在CPython中,如果看到的是Python类的一个实例,它将隐式创建一个绑定方法 – 但如果它是内置类的一个实例,它将直接在C槽中查找该方法并调用它.因此,虽然通过一次创建绑定方法而不是一遍又一遍来节省时间,但它仍然没有浪费时间通过绑定方法而不是直接调用它来浪费时间. 当然,在不同的Python实现中 – 或者只是使用不是内置的不同类型 – 事情可能会有所不同. 如果这实际上很重要(通常不会这样),您当然应该使用平台,Python实现和您关心的类型对其进行测试. 但是,纯粹作为一个例子,我将在我的MacBook Pro上使用64位python.org CPython 3.7进行测试: In [778]: s = {1,2,3} In [779]: sc = s.__contains__ In [780]: %timeit 4 in s 33.9 ns ± 0.444 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each) In [781]: %timeit s.__contains__(4) 69.3 ns ± 0.936 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each) In [782]: %timeit sc(4) 47.6 ns ± 0.866 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each) 正如预期的那样,sc会让我们浪费一些时间,但不是全部. 但是使用纯Python类型: In [787]: class Set: ...: def __contains__(self,n): ...: return 1 <= n < 4 In [788]: s = Set() In [789]: sc = s.__contains__ In [790]: %timeit 4 in s 129 ns ± 5.69 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each) In [791]: %timeit s.__contains__(4) 124 ns ± 1.14 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each) In [792]: %timeit sc(4) 108 ns ± 1.19 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each) … s中的4比s慢一点___包含__(4)(因为它基本上只是一个完全调用的包装器),并且创建绑定方法使它更快. 因此,我们得到完全相反的结果,两种不同的类型代表相同的值. 而且,这些案例中最大的差异仍然只有35ns. 作为旁注,预绑定该方法对局部人而言比全局变量更有帮助. (局部变量查找明显快于属性查找;全局变量查找仅比属性查找快一点.)在单行中更难以证明,但如果这是您的实际预期用途,您应该自己测试. 请记住,所有这一切都与CPython有关. 当我在PyPy 3.5.3 / 5.10.1中运行完全相同的代码时,设置为6.39 / 6.29 / 6.31ns,Set为1.52 / 1.51 / 1.50ns. 请注意,几乎所有的细节都完全相反:__contains__比set更快,预绑定它实际上减慢了速度而不是加速它们,非内置集快4倍而不是3倍慢.为什么?我可以做一些猜测,但每当我试图深入PyPy的JIT获得可靠的答案时,我在三天后出来,只学到了Armin Rigo是一个18级巫师. (还要注意,只是切换Python解释器比我们在语言中可以做的任何微优化产生的数量级更大.) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |