Je pense que vous voyez des modèles de sur-allocation, c'est un échantillon de la source :
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
*/
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
En imprimant les tailles des compréhensions de listes de longueurs 0-88, vous pouvez voir les correspondances de motifs :
# create comprehensions for sizes 0-88
comprehensions = [sys.getsizeof([1 for _ in range(l)]) for l in range(90)]
# only take those that resulted in growth compared to previous length
steps = zip(comprehensions, comprehensions[1:])
growths = [x for x in list(enumerate(steps)) if x[1][0] != x[1][1]]
# print the results:
for growth in growths:
print(growth)
Résultats (le format est (list length, (old total size, new total size))
) :
(0, (64, 96))
(4, (96, 128))
(8, (128, 192))
(16, (192, 264))
(25, (264, 344))
(35, (344, 432))
(46, (432, 528))
(58, (528, 640))
(72, (640, 768))
(88, (768, 912))
La surallocation est faite pour des raisons de performance permettant aux listes de croître sans allouer plus de mémoire à chaque croissance (meilleure amorti performance).
Une raison probable de la différence avec l'utilisation de la compréhension de liste est que cette dernière ne peut pas calculer de manière déterministe la taille de la liste générée, mais list()
peut. Cela signifie que les comprehensions vont continuellement faire croître la liste au fur et à mesure qu'elles la remplissent en utilisant la sur-allocation jusqu'à ce qu'elle soit finalement remplie.
Il est possible que le tampon de surallocation ne soit pas rempli de nœuds alloués inutilisés une fois qu'il est terminé (en fait, dans la plupart des cas, il ne le sera pas, cela irait à l'encontre de l'objectif de surallocation).
list()
Cependant, il peut ajouter un tampon, quelle que soit la taille de la liste, puisqu'il connaît à l'avance la taille finale de la liste.
Une autre preuve de soutien, provenant également de la source, est que nous voyons liste des compréhensions invoquant LIST_APPEND
qui indique l'utilisation de list.resize
ce qui indique que l'on consomme la mémoire tampon de pré-affectation sans savoir quelle proportion de celle-ci sera remplie. Ceci est cohérent avec le comportement que vous observez.
Pour conclure, list()
pré-allouera plus de nœuds en fonction de la taille de la liste
>>> sys.getsizeof(list([1,2,3]))
60
>>> sys.getsizeof(list([1,2,3,4]))
64
La compréhension de liste ne connaît pas la taille de la liste, elle utilise donc des opérations d'ajout au fur et à mesure de sa croissance, épuisant le tampon de pré-affectation :
# one item before filling pre-allocation buffer completely
>>> sys.getsizeof([i for i in [1,2,3]])
52
# fills pre-allocation buffer completely
# note that size did not change, we still have buffered unused nodes
>>> sys.getsizeof([i for i in [1,2,3,4]])
52
# grows pre-allocation buffer
>>> sys.getsizeof([i for i in [1,2,3,4,5]])
68
3 votes
C'est une question très intéressante. Je peux reproduire le phénomène dans Python 3.4.3. Encore plus intéressant : sur Python 2.7.5
sys.getsizeof(list(range(100)))
est de 1016,getsizeof(range(100))
est de 872 etgetsizeof([i for i in range(100)])
est de 920. Tous ont le typelist
.0 votes
Il est intéressant de noter que cette différence est également présente dans Python 2.7.10 (bien que les chiffres réels soient différents de ceux de Python 3). Elle existe également dans les versions 3.5 et 3.6b.
0 votes
J'obtiens les mêmes chiffres pour Python 2.7.6 que @SvenFestersen, également en utilisant
xrange
.2 votes
Il y a une explication possible ici : stackoverflow.com/questions/7247298/taille-de-la-liste-en-mémoire . Si l'une des méthodes crée la liste en utilisant
append()
il pourrait y avoir une sur-affectation de la mémoire. Je pense que la seule façon de vraiment clarifier ce point est de jeter un coup d'œil aux sources Python.0 votes
Seulement 10 % de plus (ce n'est pas vraiment dit nulle part). Je reformulerais le titre en "légèrement plus".