Bob
Bob

Reputation: 4970

python - why do immutable objects not occupy same memory

I'm using micropython but it shouldn't matter

>>> b = [None]*40
>>> gc.collect(); gc.mem_free(); dir(); sys.modules
101
7008
['sys', '__name__', 'a', 'gc', 'b']
{}
>>> for i in range(40):
...     b[i] = (255, 0, 0)
...     gc.collect(); gc.mem_free();
...
...
...
5
6800
0
6768
0
6736
0
6704
0
6672
0
6640
0
6608
0
6576
0
6544
0
6512
0
6480
0
6448
0
6416
0
6384
0
6352
0
6320
0
6288
0
6256
0
6224
0
6192
0
6160
0
6128
0
6096
0
6064
0
6032
0
6000
0
5968
0
5936
0
5904
0
5872
0
5840
0
5808
0
5776
0
5744
0
5712
0
5680
0
5648
0
5616
0
5584
0
5552
>>>

The small number is the number of objects gc.collect() collected and large number is how much free memory exists.

(255, 0, 0) is a tuple which is immutable and it contains immutable objects so why does the amount of free memory decrease after each assignment?

If an object is immutable, what's the point of Python making copies of it?
Why not just assign the same "pointer" to each b[i]?

UPDATE

I used a bigger number in the tuple (55555555555555555555, 55555555555555555555) and the memory usage is the same.

    >>> gc.collect(); gc.mem_free(); dir(); sys.modules
5
6368
['sys', '__name__', 'gc', 'i']
{}
>>> b = [None]*40
>>> for i in range(40):
...     b[i] = (55555555555555555555,55555555555555555555)
...     id(b[i])
...     gc.collect(); gc.mem_free()
...
...
...
5347968
10
5824
5347136
0
5808
5347312
0
5792
5347456
0
5776
5347536
0
5760
5347552
0
5744
5347696
0
5728
5347712
0
5712
5347984
0
5696
5348176
0
5680
5348192
0
5664
5348208
0
5648
5348224
0
5632
5348240
0
5616
5348256
0
5600
5348272
0
5584
5348288
0
5568
5348608
0
5552
5348640
0
5536
5348656
0
5520
5348672
0
5504
5348688
0
5488
5348704
0
5472
5348720
0
5456
5348736
0
5440
5348848
0
5424
5348864
0
5408
5348880
0
5392
5348896
0
5376
5348912
0
5360
5348928
0
5344
5348944
0
5328
5349104
0
5312
5349120
0
5296
5349136
0
5280
5349152
0
5264
5349168
0
5248
5349184
0
5232
5349200
0
5216
5349216
0
5200
>>>

But when I use the integer (55555555555555555555), the memory usage does NOT change as I iterate.

Upvotes: 2

Views: 220

Answers (1)

ShadowRanger
ShadowRanger

Reputation: 155363

Because generalized interning of all immutable objects in the interpreter is complicated, and adds a ton of code that rarely saves anything worthwhile.

That said, your code does use a single copy of the tuple on the CPython reference interpreter. It's an implementation detail, so each interpreter can make its own decisions here, and I guess Micropython didn't choose to do it (possibly to keep the interpreter simple enough to run on weaker hardware).

It looks like Micropython performs caching for int constants, but not for tuples; tuples are harder to handle (at least initially, CPython didn't do this in the main AST phase, it just ran a peephole optimizer over the resulting byte code to convert runs of LOAD_CONST followed by a BUILD_TUPLE using only LOAD_CONST results to a LOAD_CONST of the resulting tuple), and the extra work involved may have been deemed not worthwhile.

Upvotes: 4

Related Questions