Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 874
yadutaf Avatar asked Sep 13 '12 00:09

yadutaf


People also ask

Does python compute local variables quicker?

Locals should be faster When a line of code asks for the value of a variable x, Python will search for that variable in all the available namespaces, in order: local namespace - specific to the current function or class method.

Which variable is fast in access?

Local (temporary) variables and method-argument variables are the fastest variables to access and update.


1 Answers

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        
like image 181
John La Rooy Avatar answered Oct 23 '22 11:10

John La Rooy