divmod()是否比使用%和//运算符快? [英] Is divmod() faster than using the % and // operators?

查看:227
本文介绍了divmod()是否比使用%和//运算符快?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从汇编中记得,整数除法指令会同时产生商和余数.因此,在python中,内置的divmod()函数是否会比使用%和//运算符在性能方面更好(当然,一个需要商和余数)?

I remember from assembly that integer division instructions yield both the quotient and remainder. So, in python will the built-in divmod() function be better performance-wise than using the % and // operators (suppose of course one needs both the quotient and the remainder)?

q, r = divmod(n, d)
q, r = (n // d, n % d)

推荐答案

要知道要测量(Macbook Pro 2.8Ghz i7上的所有计时):

To measure is to know (all timings on a Macbook Pro 2.8Ghz i7):

>>> import sys, timeit
>>> sys.version_info
sys.version_info(major=2, minor=7, micro=12, releaselevel='final', serial=0)
>>> timeit.timeit('divmod(n, d)', 'n, d = 42, 7')
0.1473848819732666
>>> timeit.timeit('n // d, n % d', 'n, d = 42, 7')
0.10324406623840332

divmod()函数在这里处于劣势,因为您每次都需要查找全局变量.将其绑定到本地(timeit时间试用中的所有变量都是本地的)可以稍微提高性能:

The divmod() function is at a disadvantage here because you need to look up the global each time. Binding it to a local (all variables in a timeit time trial are local) improves performance a little:

>>> timeit.timeit('dm(n, d)', 'n, d = 42, 7; dm = divmod')
0.13460898399353027

但是运算符仍然会赢,因为执行divmod()的函数调用时不必保留当前帧:

but the operators still win because they don't have to preserve the current frame while a function call to divmod() is executed:

>>> import dis
>>> dis.dis(compile('divmod(n, d)', '', 'exec'))
  1           0 LOAD_NAME                0 (divmod)
              3 LOAD_NAME                1 (n)
              6 LOAD_NAME                2 (d)
              9 CALL_FUNCTION            2
             12 POP_TOP             
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE        
>>> dis.dis(compile('(n // d, n % d)', '', 'exec'))
  1           0 LOAD_NAME                0 (n)
              3 LOAD_NAME                1 (d)
              6 BINARY_FLOOR_DIVIDE 
              7 LOAD_NAME                0 (n)
             10 LOAD_NAME                1 (d)
             13 BINARY_MODULO       
             14 BUILD_TUPLE              2
             17 POP_TOP             
             18 LOAD_CONST               0 (None)
             21 RETURN_VALUE        

//%变体使用更多的操作码,但是CALL_FUNCTION字节码是熊,从性能角度考虑.

The // and % variant uses more opcodes, but the CALL_FUNCTION bytecode is a bear, performance wise.

在PyPy中,对于小的整数,实际上并没有太大的区别.在C整数运算的绝对速度下,操作码所具有的较小的速度优势就消失了:

In PyPy, for small integers there isn't really much of a difference; the small speed advantage the opcodes have melts away under the sheer speed of C integer arithmetic:

>>>> import platform, sys, timeit
>>>> platform.python_implementation(), sys.version_info
('PyPy', (major=2, minor=7, micro=10, releaselevel='final', serial=42))
>>>> timeit.timeit('divmod(n, d)', 'n, d = 42, 7', number=10**9)
0.5659301280975342
>>>> timeit.timeit('n // d, n % d', 'n, d = 42, 7', number=10**9)
0.5471200942993164

(我不得不将重复次数提高到10亿,以表明差异的确有多小,这里PyPy的速度非常快).

(I had to crank the number of repetitions up to 1 billion to show how small the difference really is, PyPy is blazingly fast here).

但是,当数字变为时,divmod() 会赢得一个国家英里:

However, when the numbers get large, divmod() wins by a country mile:

>>>> timeit.timeit('divmod(n, d)', 'n, d = 2**74207281 - 1, 26', number=100)
17.620037078857422
>>>> timeit.timeit('n // d, n % d', 'n, d = 2**74207281 - 1, 26', number=100)
34.44323515892029

(我现在不得不将重复次数调到与霍布斯的数字相比减少了10倍,以便在合理的时间内得到结果).

(I now had to tune down the number of repetitions by a factor of 10 compared to hobbs' numbers, just to get a result in a reasonable amount of time).

这是因为PyPy不再可以将这些整数拆箱为C整数;您会看到使用sys.maxintsys.maxint + 1之间的时间上的显着差异:

This is because PyPy no longer can unbox those integers as C integers; you can see the striking difference in timings between using sys.maxint and sys.maxint + 1:

>>>> timeit.timeit('divmod(n, d)', 'import sys; n, d = sys.maxint, 26', number=10**7)
0.008622884750366211
>>>> timeit.timeit('n // d, n % d', 'import sys; n, d = sys.maxint, 26', number=10**7)
0.007693052291870117
>>>> timeit.timeit('divmod(n, d)', 'import sys; n, d = sys.maxint + 1, 26', number=10**7)
0.8396248817443848
>>>> timeit.timeit('n // d, n % d', 'import sys; n, d = sys.maxint + 1, 26', number=10**7)
1.0117690563201904

这篇关于divmod()是否比使用%和//运算符快?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆