Why is 2 * x * x faster than 2 * ( x * x ) in Python 3.x, for integers?

前端 未结 4 698
我在风中等你
我在风中等你 2021-01-30 13:37

The following Python 3.x integer multiplication takes on average between 1.66s and 1.77s:

import time
start_time = time.time()
num = 0
for x in range(0, 10000000         


        
4条回答
  •  时光说笑
    2021-01-30 13:43

    First of all, note that we don't see the same thing in Python 2.x:

    >>> timeit("for i in range(1000): 2*i*i")
    51.00784397125244
    >>> timeit("for i in range(1000): 2*(i*i)")
    50.48330092430115
    

    So this leads us to believe that this is due to how integers changed in Python 3: specifically, Python 3 uses long (arbitrarily large integers) everywhere.

    For small enough integers (including the ones we're considering here), CPython actually just uses the O(MN) grade-school digit by digit multiplication algorithm (for larger integers it switches to the Karatsuba algorithm). You can see this yourself in the source.

    The number of digits in x*x is roughly twice that of 2*x or x (since log(x2) = 2 log(x)). Note that a "digit" in this context is not a base-10 digit, but a 30-bit value (which are treated as single digits in CPython's implementation). Hence, 2 is a single-digit value, and x and 2*x are single-digit values for all iterations of the loop, but x*x is two-digit for x >= 2**15. Hence, for x >= 2**15, 2*x*x only requires single-by-single-digit multiplications whereas 2*(x*x) requires a single-by-single and a single-by-double-digit multiplication (since x*x has 2 30-bit digits).

    Here's a direct way to see this (Python 3):

    >>> timeit("a*b", "a,b = 2, 123456**2", number=100000000)
    5.796971936999967
    >>> timeit("a*b", "a,b = 2*123456, 123456", number=100000000)
    4.3559221399999615
    

    Again, compare this to Python 2, which doesn't use arbitrary-length integers everywhere:

    >>> timeit("a*b", "a,b = 2, 123456**2", number=100000000)
    3.0912468433380127
    >>> timeit("a*b", "a,b = 2*123456, 123456", number=100000000)
    3.1120400428771973
    

    (One interesting note: If you look at the source, you'll see that the algorithm actually has a special case for squaring numbers (which we're doing here), but even still this is not enough to overcome the fact that 2*(x*x) just requires processing more digits.)

提交回复
热议问题