Why is local variable access faster than class member access in Python?
Asked Answered
T

2

20

While trying to tackle a more complex problem, I came to compare access speed to local variable vs member variables.

Here a test program:

#!/usr/bin/env python

MAX=40000000

class StressTestMember(object):
    def __init__(self):
        self.m = 0

    def do_work(self):
        self.m += 1
        self.m *= 2

class StressTestLocal(object):
    def __init__(self):
        pass

    def do_work(self):
        m = 0
        m += 1
        m *= 2

# LOCAL access test
for i in range(MAX):
    StressTestLocal().do_work()

# MEMBER access test
for i in range(MAX):
    StressTestMember().do_work()

I know it might look like a bad idea to instantiate StressTestMember and StressTestLocal on each iterations but it makes sense in the modeled program where these are basically Active Records.

After a simple benchmark,

  • LOCAL access test: 0m22.836
  • MEMBER access test: 0m32.648s

The local version is ~33% faster while still part of a class. Why?

Tezel answered 13/9, 2012 at 0:48 Comment(0)
R
33

self.m += 1 means you have to look up a local variable called self and then find the attribute called m

Of course if you just have to look up a local variable, it will be faster without the extra step.

It can be useful to look at what is happening under the hood:

>>> import dis
>>> dis.dis(StressTestLocal.do_work)
 18           0 LOAD_CONST               1 (0)
              3 STORE_FAST               1 (m)

 19           6 LOAD_FAST                1 (m)
              9 LOAD_CONST               2 (1)
             12 INPLACE_ADD         
             13 STORE_FAST               1 (m)

 20          16 LOAD_FAST                1 (m)
             19 LOAD_CONST               3 (2)
             22 INPLACE_MULTIPLY    
             23 STORE_FAST               1 (m)
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE        
>>> dis.dis(StressTestMember.do_work)
 10           0 LOAD_FAST                0 (self)
              3 DUP_TOP             
              4 LOAD_ATTR                0 (m)
              7 LOAD_CONST               1 (1)
             10 INPLACE_ADD         
             11 ROT_TWO             
             12 STORE_ATTR               0 (m)

 11          15 LOAD_FAST                0 (self)
             18 DUP_TOP             
             19 LOAD_ATTR                0 (m)
             22 LOAD_CONST               2 (2)
             25 INPLACE_MULTIPLY    
             26 ROT_TWO             
             27 STORE_ATTR               0 (m)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE        
Roughage answered 13/9, 2012 at 1:0 Comment(2)
So does it follow that it'd be wise to create a new reference to a class variable in local scope? e.g., m = self.m? It wouldn't make any difference in this test, but my version of do_work() is a loop that runs millions of times.Lythraceous
@JamesS, It's always best to measure your exact use case if it's important enough to tune it. If you were referencing self.m a few times I'd expect it to be faster to pull into a local var. Remember to save the local back to the attribute if you are mutating it.Roughage
L
7

Local names is faster because Python does some optimization that local names don't need dict access, on the other hand, instance attributes need to access the __dict__ of the object.

This is also the reason why local names are faster than global names.

Loreenlorelei answered 13/9, 2012 at 0:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.