wpg4665
wpg4665

Reputation: 131

python plus equals slower

I ran some quick benchmarking using timeit.repeat comparing two different ways to use an _accumulator_.

def testAccumPlusEqual():
    x = 0
    for i in range(100):
        x += 1
    return x

def testAccumEqualPlus():
    x = 0
    for i in range(100):
        x = x + 1
    return x

My implementation of timeit.repeat is:

if __name__ == '__main__':
    import timeit
    print(timeit.repeat("testAccumPlusEqual()",
                    setup="from __main__ import testAccumPlusEqual"))
    print(timeit.repeat("testAccumEqualPlus()",
                    setup="from __main__ import testAccumEqualPlus"))

The results are found below:

>>> 
[8.824021608811469, 8.80440620087051, 8.791231916848997]
[8.101681307351758, 8.143080002052649, 8.181129610882778]

Granted, in the grand scheme of things, this time difference may not be noticeable, but if used in large scale it could lead to a slow down. So I guess I'm really asking:

From everywhere I've seen, the de facto standard is to accumulate with +=, but should that be the case still?

Why would += perform worse than x=x+?

NOTE: Using CPython 3.3.2 on Windows 7 64bit(using 32bit version of python)

Upvotes: 0

Views: 488

Answers (1)

roman
roman

Reputation: 117380

It's not actually an answer, but it could help you to understand what happens in you Python code. You can call dis on both of functions and get:

>>> import dis
>>> dis.dis(testAccumEqualPlus)
  2           0 LOAD_CONST               1 (0)
              3 STORE_FAST               0 (x)

  3           6 SETUP_LOOP              30 (to 39)
              9 LOAD_GLOBAL              0 (range)
             12 LOAD_CONST               2 (100)
             15 CALL_FUNCTION            1
             18 GET_ITER            
        >>   19 FOR_ITER                16 (to 38)
             22 STORE_FAST               1 (i)

  4          25 LOAD_FAST                0 (x)
             28 LOAD_CONST               3 (1)
             31 BINARY_ADD          
             32 STORE_FAST               0 (x)
             35 JUMP_ABSOLUTE           19
        >>   38 POP_BLOCK           

  5     >>   39 LOAD_FAST                0 (x)
             42 RETURN_VALUE        
>>> dis.dis(testAccumPlusEqual)
  2           0 LOAD_CONST               1 (0)
              3 STORE_FAST               0 (x)

  3           6 SETUP_LOOP              30 (to 39)
              9 LOAD_GLOBAL              0 (range)
             12 LOAD_CONST               2 (100)
             15 CALL_FUNCTION            1
             18 GET_ITER            
        >>   19 FOR_ITER                16 (to 38)
             22 STORE_FAST               1 (i)

  4          25 LOAD_FAST                0 (x)
             28 LOAD_CONST               3 (1)
             31 INPLACE_ADD         
             32 STORE_FAST               0 (x)
             35 JUMP_ABSOLUTE           19
        >>   38 POP_BLOCK           

  5     >>   39 LOAD_FAST                0 (x)
             42 RETURN_VALUE

As you see, the only difference is INPLACE_ADD for += and BINARY_ADD for = .. +

Upvotes: 2

Related Questions