Reputation: 681
I read this that PyObject has Type, Value and Reference count for garbage collection. But the following shows each integer object takes 32 bytes which for a 64-bit OS, there seems to be one more field. What would that be?
>>> hex(id(3))
'0x1595ae90130'
>>> hex(id(4))
'0x1595ae90150'
>>> hex(id(5))
'0x1595ae90170'
You'll observe that the IDs are 32 bytes apart.
Upvotes: 1
Views: 323
Reputation: 681
#define PyLong_SHIFT 30 for PYLONG_BITS_IN_DIGIT = 30
The value of an integer is equal to SUM(for i=0 through abs(ob_size)-1) ob_digit[i] * 2**(PyLong_SHIFT*i) where
struct _longobject {
PyObject_VAR_HEAD
uint32_t ob_digit[1];
};
#define PyObject_VAR_HEAD \ // 28 bytes
int ob_refcnt; \
struct _typeobject *ob_type; \
int ob_size;
Below is a Python code to demonstrate the integer is of variable size and allocated 30 bits for each 4 bytes of memory.
from decimal import Decimal
print("Check PyObject <int> fields and sizes 2**30, 60, 90 ==>>")
shift = 30
ob_size = 50
refCntObTypeSz = 8 * 3 # PyObject_VAR_HEAD 3 8-byte fields
def getSize(value):
size = value.__sizeof__()
maxValue = Decimal(2**((size - refCntObTypeSz)*8)) # 32-bit based
ratio = value / maxValue
print(f'i = {i:2d} : allocated size {size:3d} bit_length {value.bit_length():4d} value {hex(value)} =>\tactual/allocated = {ratio:0.3e}')
return size
zero = 0
prev = zero.__sizeof__() # 24
for i in range(0, ob_size):
value = (1 << (shift * i)) - 1 # 30-bit based
nxt = getSize(value)
if (nxt != prev):
print(f"A gap of prev size {prev} != next size {nxt} bytes jump")
value += 1
nxt = getSize(value)
if (nxt != prev + 4):
print(f"An increment of != 4 bytes: prev size {prev} and next size {nxt}")
prev = nxt
output:
Check PyObject <int> fields and sizes 2**30, 60, 90 ==>>
i = 0 : allocated size 24 bit_length 0 value 0x0 => actual/allocated = 0.000e+3
i = 0 : allocated size 28 bit_length 1 value 0x1 => actual/allocated = 2.328e-10
i = 1 : allocated size 28 bit_length 30 value 0x3fffffff => actual/allocated = 2.500e-1
i = 1 : allocated size 32 bit_length 31 value 0x40000000 => actual/allocated = 5.821e-11
i = 2 : allocated size 32 bit_length 60 value 0xfffffffffffffff => actual/allocated = 6.250e-2
i = 2 : allocated size 36 bit_length 61 value 0x1000000000000000 => actual/allocated = 1.455e-11
i = 3 : allocated size 36 bit_length 90 value 0x3ffffffffffffffffffffff => actual/allocated = 1.562e-2
i = 3 : allocated size 40 bit_length 91 value 0x40000000000000000000000 => actual/allocated = 3.638e-12
Upvotes: 0
Reputation: 1859
The function id
returns an address it doesn't inform you on the size of the object. The 32 bit difference you're seeing here 0x1595ae90150 - 0x1595ae90130
is not the size of the object. To get the size of an object you can use getsizeof
in the sys module:
import sys
x = 5
print(sys.getsizeof(x))
# 28
Upvotes: 5