L
L
lambakean2020-01-18 15:26:07
Python
lambakean, 2020-01-18 15:26:07

How can memory usage be optimized when dealing with class instances in Python?

I'm writing a game that has units. There is a Unit class, which is a repository of information about each individual unit. The Unit class now has the following structure:

class Unit:
    def __init__(self, color, health):
        self.color = color
        self.health = health

When creating a unit, I create an instance of the Unit class each time, so that later I can easily get the necessary information about any unit in the place I need. So, if I have 100 identical units on the field, then the same information is stored in memory in 100 copies?
Since I usually have a lot of units in a game, and each unit has 23 properties, I thought it would be better to optimize like this:
class Unit:

    color = (13, 155, 250)
    health = 100

    def __init__(self):
        #Do something
        pass

In the second code example, the same information is not stored in memory many times, as I think, since color and health are no longer properties of units, of which there are hundreds, but class properties, and we have only one class.
I decided to see if this way would help optimize memory usage, so I used the guppy3
library. First, I checked how much memory 10,000 units take up using the first (non-optimized) way:
from guppy import hpy

'''
Массив со списком всех юнитов, чтобы объекты не уничтожались
из-за того, что на них ничего не ссылается
'''
l = list()

class Unit:
  
  def __init__(self, number, radius, width, color):

    self.number = number
    self.radius = radius
    self.width = width
    self.color = color

    l.append(self)


for i in range(10000):
  ''' Создание 10000 юнитов '''
  Unit(i, 4, 1, (13, 150, 255))


h = hpy()
''' Вывод информации о том, сколько места в памяти занимает программа '''
print(h.heap())

As a result, according to the data that the guppy3 library gives, my program took up 5.8 MB of memory
. Now let's check how much memory the program will take if we use the second (optimized) method:
from guppy import hpy

'''
Массив со списком всех юнитов, чтобы объекты не уничтожались
из-за того, что на них ничего не ссылается
'''
l = list()

class Unit:

  radius = 4
  width = 1
  color = (13, 150, 255)
  
  def __init__(self, number):

    self.number = number

    l.append(self)


for i in range(10000):
  ''' Создание 10000 юнитов '''
  Unit(i)


h = hpy()
''' Вывод информации о том, сколько места в памяти занимает программа '''
print(h.heap())

As a result, it turns out that the second program takes up almost exactly the same amount of space in memory - 5.8 MB.
Why is there no difference? Maybe "under the hood" python itself optimizes this, so the result is the same? Or, on the contrary, both there and there the same information is repeated 10,000 times and you need to look for another way to fix it?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
Vladimir Kuts, 2020-01-18
@lambakean

Try to define the class like this:

class Unit:
  __slots__ = ('number', 'radius', 'width', 'color')

  def __init__(self, number, radius, width, color):

    self.number = number
    self.radius = radius
    self.width = width
    self.color = color

    l.append(self)

Without using __slots__
Partition of a set of 71837 objects. Total size = 6887839 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  10000  14  1120000  16   1120000  16 dict of __main__.Unit
     1  12606  18  1099993  16   2219993  32 str
     2  10056  14   729024  11   2949017  43 tuple
     3  10000  14   560000   8   3509017  51 __main__.Unit
     4    561   1   460680   7   3969697  58 type
     5   2512   3   361909   5   4331606  63 types.CodeType
     6   4892   7   340577   5   4672183  68 bytes
     7   2413   3   328168   5   5000351  73 function
     8  11220  16   317416   5   5317767  77 int
     9    561   1   269392   4   5587159  81 dict of type
<125 more rows. Type e.g. '_.more' to view.>

Using __slots__:
Partition of a set of 61841 objects. Total size = 5928031 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  12606  20  1099995  19   1099995  19 str
     1  10058  16   729232  12   1829227  31 tuple
     2  10000  16   720000  12   2549227  43 __main__.Unit
     3    561   1   460512   8   3009739  51 type
     4   2512   4   361909   6   3371648  57 types.CodeType
     5   4892   8   340583   6   3712231  63 bytes
     6   2413   4   328168   6   4040399  68 function
     7  11220  18   317416   5   4357815  74 int
     8    561   1   269392   5   4627207  78 dict of type
     9    400   1   181760   3   4808967  81 set
<124 more rows. Type e.g. '_.more' to view.>

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question