Python divmod()是否比使用%和//运算符快?

Python divmod()是否比使用%和//运算符快?,python,performance,division,modulo,divmod,Python,Performance,Division,Modulo,Divmod,我记得在汇编中,整数除法指令同时产生商和余数。因此,在python中,内置的divmod()函数在性能方面是否比使用%和/运算符更好(当然,假设一个人同时需要商和余数) 测量就是知道(Macbook Pro 2.8Ghz i7上的所有计时): divmod()函数在这里处于不利地位,因为每次都需要查找全局变量。将其绑定到本地(时间试验中的所有变量都是本地的)稍微提高了性能: >>> timeit.timeit('dm(n, d)', 'n, d = 42, 7; dm = di

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

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

divmod()
函数在这里处于不利地位,因为每次都需要查找全局变量。将其绑定到本地(时间试验中的所有变量都是本地的)稍微提高了性能:

>>> timeit.timeit('dm(n, d)', 'n, d = 42, 7; dm = divmod')
0.13460898399353027
但是操作符仍然获胜,因为在执行对
divmod()
的函数调用时,它们不必保留当前帧:

>>> 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        
/
%
变体使用了更多的操作码,但是
调用函数
字节码是一种熊型的、性能方面的代码


在PyPy中,对于小整数,实际上没有太大区别;在C整数运算的绝对速度下,操作码的小速度优势逐渐消失:

>>>> 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的速度非常快)

但是,当数字变大时,
divmod()
将以国家英里的优势获胜:

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

这是因为PyPy不再能够将这些整数解装箱为C整数;您可以看到使用
sys.maxint
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

如果您使用的是“小”本机整数,那么Martijn的答案是正确的,因为与函数调用相比,算术运算非常快。然而,对于bigints,情况完全不同:

>>> import timeit
>>> timeit.timeit('divmod(n, d)', 'n, d = 2**74207281 - 1, 26', number=1000)
24.22666597366333
>>> timeit.timeit('n // d, n % d', 'n, d = 2**74207281 - 1, 26', number=1000)
49.517399072647095
当除法一个2200万位数时,divmod的速度几乎是单独除法和模的两倍,正如您所期望的那样


在我的机器上,交叉发生在2^63左右,但不要相信我的话。正如Martijn所说,测量!当性能真的很重要时,不要假设在一个地方正确的东西在另一个地方仍然正确。

为什么不使用
timeit
来发现呢?函数调用开销(字节码中的
call\u Function
)可能会导致第一个版本的执行速度变慢,但是
timeit
是一种确定的方法。哪种实现?你知道为什么
divmod
完全破坏了
/
%
,但是使用了大量数据吗?@JimFasarakis-Hilliard:大量支持。您不能再对C整数值使用基本的C
掩蔽运算,因此通常的优化就不存在了。你必须使用一种与int的大小成正比的算法,在使用divmod时只执行一次,而在使用单个操作符时执行两次会造成严重的伤害。使用
divmod(a,b)
是否总是比使用
a//b,a%b
更好,或者,当数字足够小时,我应该使用后者吗?@AnnZen您使用的是PyPy还是关键路径上的代码,需要尽可能快地执行?如果没有,选择你认为能更好地传达意图的内容。@MartijnPieters代码不需要尽可能快。。。但我需要把坐标颠倒过来。
divmod(a,b)[::-1]
a//b,a%b
更能传达意图吗?非常感谢。
>>>> 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
>>>> 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
>>> import timeit
>>> timeit.timeit('divmod(n, d)', 'n, d = 2**74207281 - 1, 26', number=1000)
24.22666597366333
>>> timeit.timeit('n // d, n % d', 'n, d = 2**74207281 - 1, 26', number=1000)
49.517399072647095