Il y avait un détail qui me chiffonnait dans ma toute première réponse, j’avais en mémoire qu’un int en Python prend 28 octets lorsque la valeur rentre dans un entier de 32 bits, la valeur de 32 octets me paraissait donc un peu étrange même si pas complètement à côté de la plaque.
Je viens de prendre un moment pour vérifier ça, ainsi que le fait que la list
grandit.
Avec ce code
import sys
from pympler.asizeof import asizeof
for i in range(10):
l = list(range(i))
gso = sys.getsizeof(l)
aso = asizeof(l)
diff = aso - gso
print(i, gso, aso, diff, diff / i if i else None)
print(sys.getsizeof(0), asizeof(0))
print(sys.getsizeof(int(1e10)), asizeof(int(1e10)))
print(sys.getsizeof(int(1e100)), asizeof(int(1e100)))
print(sys.getsizeof([int(1e100)]), asizeof([int(1e100)]))
J’obtiens la sortie suivante :
0 56 56 0 None
1 72 104 32 32.0
2 72 136 64 32.0
3 88 184 96 32.0
4 88 216 128 32.0
5 104 264 160 32.0
6 104 296 192 32.0
7 120 344 224 32.0
8 120 376 256 32.0
9 136 424 288 32.0
28 32
32 32
72 72
64 136
Avec la boucle, on voit deux choses :
- comme je le suspectai, la taille d’une liste augmente linéairement (enfin presque, par pas de 16 sur des listes de cette taille) avec le nombre d’éléments pour pouvoir stocker les pointeurs (8 octets par pointeur sur mon système x64) ;
- la différence entre les deux mesures de tailles est bien directement proportionnelle à la taille avec 32 bits par éléments (aussi confirmé par la toute dernière ligne qui compare deux listes qui contiennent un entier très large).
Avec les autres lignes, on peut mettre en évidence un "bug" de pympler
: il considère (peut être parce que je suis sur un système 64 bits) que les entiers sont au moins 64 bits en back end même lorsqu’ils rentrent dans 32 bits, surestimant la taille des petits entiers de 4 octets. Les tailles sont bonnes pour des entiers plus larges. Cela dit, si on est à 4 octets prêt, c’est probablement que Python n’est pas le bon outil pour ce qu’on fait. La doc de pympler
précise d’ailleurs que les tailles peuvent êtres approximatives :
Function asizeof calculates the combined (approximate) size in bytes of one or several Python objects.
C’est assez logique, on ne peut pas accéder aux tailles d’objets arbitraires sans passer par un outil beaucoup plus lourd comme valgrind.