<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David A. Kra</title>
    <description>The latest articles on DEV Community by David A. Kra (@dakra137).</description>
    <link>https://dev.to/dakra137</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1018998%2Fd5f95399-431f-410f-a69b-1de9c4a01a2d.png</url>
      <title>DEV Community: David A. Kra</title>
      <link>https://dev.to/dakra137</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dakra137"/>
    <language>en</language>
    <item>
      <title>How much better are python local variables over globals, attributes, or slots?</title>
      <dc:creator>David A. Kra</dc:creator>
      <pubDate>Fri, 27 Dec 2024 09:57:34 +0000</pubDate>
      <link>https://dev.to/dakra137/how-much-better-are-python-local-variables-over-globals-attributes-or-slots-a2e</link>
      <guid>https://dev.to/dakra137/how-much-better-are-python-local-variables-over-globals-attributes-or-slots-a2e</guid>
      <description>&lt;p&gt;&lt;em&gt;Alternatively, how much worse are the others compared to local variables?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What's the big deal?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Some articles advise python programmers to use local variables if they will be used a lot, even if only referenced but not changed.&lt;/p&gt;

&lt;p&gt;For example, here are two quotes from &lt;a href="https://dev.to/martinheinz/making-python-programs-blazing-fast-4knl"&gt;"Making Python Programs Blazing Fast"&lt;/a&gt;  :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"There's actually difference in speed of lookup even between - let's say - local variable in function (fastest), class-level attribute (e.g. self.name - slower) and global for example imported function like time.time (slowest)." &lt;/p&gt;

&lt;p&gt;"Don't Access Attributes&lt;/p&gt;

&lt;p&gt;Another thing that might slow down your programs is dot operator (.) which is used when accessing object attributes. This operator triggers dictionary lookup using &lt;strong&gt;getattribute&lt;/strong&gt;, which creates extra overhead in your code. "&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also, these are  not the only possibilities. What about differences among:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Class attributes, &lt;/li&gt;
&lt;li&gt;Instance attributes, &lt;/li&gt;
&lt;li&gt;Slot Instance attributes,&lt;/li&gt;
&lt;li&gt;Slot Instance attributes of a different instance &lt;/li&gt;
&lt;li&gt;Method attributes, &lt;/li&gt;
&lt;li&gt;Function (non-method) attributes,&lt;/li&gt;
&lt;li&gt;And as compared to using a constant?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;I tested by using all these in loops with 1,000,000 iterations, but minimal work per iteration inside them. The functions were all methods of a class, except for the function used to test function attributes for functions outside a class.&lt;/p&gt;

&lt;p&gt;A typical function was of the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      def testINSA(self):
          s=0
          for i in range(10000000):
            s+=self.INSA # what is after the = differs in each function.
          return s    

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All the code is at the bottom of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;colgroup&gt;
&lt;col&gt;
&lt;col&gt;
&lt;col&gt;
&lt;col&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
  &lt;tr&gt;
    &lt;th&gt;Addend location&lt;/th&gt;
    &lt;th&gt;Avg time of 10 calls&lt;/th&gt;
    &lt;th&gt;Rate per Second&lt;/th&gt;
    &lt;th&gt;Relative Performance&lt;/th&gt;
  &lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
  &lt;tr&gt;
    &lt;td&gt;LV Local Variable &lt;/td&gt;
    &lt;td&gt;1.0253&lt;/td&gt;
    &lt;td&gt;0.975&lt;/td&gt;
    &lt;td&gt;100%&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;C Constant&lt;/td&gt;
    &lt;td&gt;1.0896&lt;/td&gt;
    &lt;td&gt;0.918&lt;/td&gt;
    &lt;td&gt;94%&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;ISA Instance Slotted Attribute         &lt;/td&gt;
    &lt;td&gt;1.1156&lt;/td&gt;
    &lt;td&gt;0.896&lt;/td&gt;
    &lt;td&gt;92%&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;GV Global Variable&lt;/td&gt;
    &lt;td&gt;1.1293&lt;/td&gt;
    &lt;td&gt;0.886&lt;/td&gt;
    &lt;td&gt;91%&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;OISA Other Instance Slotted Attribute         &lt;/td&gt;
    &lt;td&gt;1.2055&lt;/td&gt;
    &lt;td&gt;0.830&lt;/td&gt;
    &lt;td&gt;85%&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;CA Class Attribute&lt;/td&gt;
    &lt;td&gt;1.6385&lt;/td&gt;
    &lt;td&gt;0.610&lt;/td&gt;
    &lt;td&gt;63%&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;INSA Instance Non-Slotted Attribute    &lt;/td&gt;
    &lt;td&gt;1.739&lt;/td&gt;
    &lt;td&gt;0.575&lt;/td&gt;
    &lt;td&gt;59%&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;FA Function Attribute&lt;/td&gt;
    &lt;td&gt;1.8076&lt;/td&gt;
    &lt;td&gt;0.553&lt;/td&gt;
    &lt;td&gt;57%&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;MA Method Attribute                    &lt;/td&gt;
    &lt;td&gt;3.3259&lt;/td&gt;
    &lt;td&gt;0.301&lt;/td&gt;
    &lt;td&gt;31%&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Explanation: &lt;br&gt;
When comparing performance, always compare the (Achievements / Resource), such as Miles per Gallon, or Calculations per Second, rather than Liters per Km, or Seconds per Calculation. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time&lt;/strong&gt; is as reported by timeit, limited to 3 significant digits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate&lt;/strong&gt; is the reciprocal of time. How many of these per second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relative performance&lt;/strong&gt; is the rate as compared to the best. By using relative performance, bigger is better, and twice as big is twice as good.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Findings
&lt;/h2&gt;

&lt;p&gt;Yes, the local variables are the fastest. They are even faster than constants.&lt;/p&gt;

&lt;p&gt;The performance of the different types of variables is clustered into groupings.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Locals&lt;/strong&gt; do perform the best.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constants, class instance slotted attributes, and global variables:&lt;/strong&gt;  Constants were 94% as fast, Class Instance slotted attributes were 92% as fast, while Global variables were 91% as fast as locals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other Instance slotted attributes:&lt;/strong&gt; These are slotted instance attributes of some &lt;em&gt;other&lt;/em&gt; object. These performed 85% of a local variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Class Attribute, Class Instance Non-Slotted Attribute, and Function Attribute&lt;/strong&gt;: These performed, respectively, at 63%, 59%, and 57% of a local variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method attribute&lt;/strong&gt; A method attribute performs in a class of its own, at 31% of the performance of a local variable.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;If used millions of times, it is worthwhile to use local variables and slot attributes for performance reasons, not merely for code quality reasons.&lt;/p&gt;

&lt;p&gt;The surprise is that compared to common wisdom, &lt;strong&gt;globals&lt;/strong&gt; are almost as performant as slotted instance attributes in a class. Another surprise is that method attributes are the worst.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/python3.12
# published as https://dev.to/dakra137/how-much-better-are-python-local-variables-over-globals-attributes-or-slots-a2e 

# slots information
# see https://docs.python.org/3/reference/datamodel.html#slots 
# see also https://www.turing.com/kb/introduction-to-python-class-attributes 

# comments at https://dev.to/martinheinz/making-python-programs-blazing-fast-4knl 

# test performance differences among accessing a global variable, class, instance-slot attribute, other-instance slot attribute,  instance-nonslot atribute, function attribute, or local variable.

from timeit import timeit
iterations=10000000

GV=42 # Global Variable

class TestClass:
      __slots__=['ISA',  '__dict__'] # Instance slots

      CA=GV+1 # Class Attribute

      def __init__(self):
          self.ISA=self.CA+1 # instance slot attribute 
          self.INSA=self.ISA+1  # instance non-slot attribute 

      def classfun(self): # Only called once. set a method attribute
          TestClass.classfun.MA=self.INSA+1
          LV=TestClass.classfun.MA+1
          print(GV, self.CA, self.ISA, self.INSA, self.classfun.MA, LV)

      def testC(self):
          s=0
          for i in range(iterations):
            s+=1
          return s    


      def testGV(self):
          s=0
          for i in range(iterations):
            s+=GV
          return s    

      def testCA(self):
          s=0
          for i in range(iterations):
            s+=self.CA
          return s    

      def testISA(self):
          s=0
          for i in range(iterations):
            s+=self.ISA
          return s    

      def testOISA(self): # Other instance's ISA
          s=0
          for i in range(iterations):
            s+=instance2.ISA
          return s    

      def testINSA(self):
          s=0
          for i in range(iterations):
            s+=self.INSA
          return s    


      def testMA(self):  #self.classfun.MA, LV
          s=0
          for i in range(iterations):
            s+=self.classfun.MA
          return s    


      def testLV(self):
          s=0
          LV=TestClass.classfun.MA+1
          for i in range(iterations):
            s+=LV
          return s    

# # # # # # #  end of Class  # # # # #

def testFA(anobj):  # test a function attribute, not a method or class attribute.
          testFA.FA=anobj.classfun.MA+2
          s=0
          for i in range(iterations):
            s+=testFA.FA
          return s    

# test performance differences among accessing a global variable, class attribute, instance-slot attribute, instance-nonslot atribute, function attribute, or local variable.

if __name__ == "__main__": 
     instance = TestClass() 
     instance2 = TestClass() 
     print(instance.__slots__) 
     instance.foo="foo"
     instance.classfun()
     print(instance.__dict__)

     tin=10
     print("LV  ",instance.testLV(), f"{timeit('instance.testLV()',number=tin,globals=globals()):.3f}" )
     print("C   ",instance.testC(), f"{timeit('instance.testC()',number=tin,globals=globals()):.3f}" )
     print("ISA ",instance.testISA(), f"{timeit('instance.testISA()',number=tin,globals=globals()):.3f}" )     
     print("GV  ",instance.testGV(), f"{timeit('instance.testGV()',number=tin,globals=globals()):.3f}" )
     print("OISA",instance.testOISA(), f"{timeit('instance.testOISA()',number=tin,globals=globals()):.3f}" )
     print("CA  ",instance.testCA(), f"{timeit('instance.testCA()',number=tin,globals=globals()):.3f}")
     print("INSA",instance.testINSA(), f"{timeit('instance.testINSA()',number=tin,globals=globals()):.3f}" )
     print("FA  ",testFA(instance), f"{timeit('testFA(instance)',number=tin,globals=globals()):.3f}" )
     print("MA  ",instance.testMA(), f"{timeit('instance.testMA()',number=tin,globals=globals()):.3f}" )

quit()


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>performance</category>
      <category>slots</category>
    </item>
  </channel>
</rss>
