Topic: [RISOLTO] Mistero su sys.getsizeof applicato alle liste  (Letto 36 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline marcus72

  • python unicellularis
  • *
  • Post: 24
  • Punti reputazione: 0
    • Mostra profilo
[RISOLTO] Mistero su sys.getsizeof applicato alle liste
« il: Novembre 24, 2018, 16:59 »
Scusatemi: ma sys.getsizeof(lista) come può dare una dimensione molto minore di sum([sys.getsizeof(x) for x in lista])  (cioè la somma di tutti i suoi elementi)? Per avere quindi lo spazio totale occupato in ram di una lista come si può fare?

Mi spiego meglio con un esempio nell'interprete di Python 3.5.2:

>>> s0 = str(10**49)
>>> sys.getsizeof(s0 + '0')
100

Quindi una stringa di 51 caratteri occupa in memoria 100B. Mi sarei aspettato 51B (visto che le stringhe sono composte solo da cifre numeriche, che sono quindi caratteri Ascii). Ma va benissimo: non è questo il problema.

>>> l = [s0 + str(i) for i in range(10)]
>>> l
['100000000000000000000000000000000000000000000000000', '100000000000000000000000000000000000000000000000001', '100000000000000000000000000000000000000000000000002', '100000000000000000000000000000000000000000000000003', '100000000000000000000000000000000000000000000000004', '100000000000000000000000000000000000000000000000005', '100000000000000000000000000000000000000000000000006', '100000000000000000000000000000000000000000000000007', '100000000000000000000000000000000000000000000000008', '100000000000000000000000000000000000000000000000009']
>>> sys.getsizeof(l)
192

Quindi una lista di 10 stringhe da 51 caratteri Ascii l'una (e tutte diverse, così da non permettere a Python di mantenere in memoria una sola copia), di cui ognuna occupa in memoria 100B, al posto di occupare 1000B (o anche qualcosa in più o qualcosa in meno), occupa 192B?? Come può essere? Praticamente sono 510 caratteri, come minimo dovrebbe occupare 510B (in realtà dovrebbe essere qualcosa di più, visto che c'è bisogno di spazio per organizzare la lista, e magari anche qualche altra cosetta in più per accelerare eventuali nuove aggiunte di elementi...

Altro esempio (sempre con Python 3.5.2):

>>> f = 1.5
>>> sys.getsizeof(f)
24

Quindi un float occupa in memoria 24B.

>>> l2 = [f**i for i in range(1, 101)]
>>> sys.getsizeof(l2[-1])
24

Mi conferma che ogni float appartenente alla lista l2 occupa sempre 24B.

>>> sys.getsizeof(l2)
912

Ed ecco di nuovo la grossa sorpresa: 100 float dovrebbero occupare 2400B, mentre infilati in una lista sembrano occupare appena 912B, due volte e mezzo di meno!! Come può essere?
« Ultima modifica: Novembre 24, 2018, 20:03 da marcus72 »

Offline marcus72

  • python unicellularis
  • *
  • Post: 24
  • Punti reputazione: 0
    • Mostra profilo
Re:Mistero su sys.getsizeof applicato alle liste
« Risposta #1 il: Novembre 24, 2018, 17:31 »
Per il secondo esempio, con i float, un'anima pia mi ha illuminato.
Una lista vuota ha 64B di overhead:

>>> lista = []
>>> sys.getsizeof(lista)
64

Un python float ha 16B di overhead e quindi invece di consumare 8B ne consuma 24B (perché è un oggetto):

>>> x = 9.7         
>>> sys.getsizeof(x)
24

Invece, l'occupazione di memoria di una lista di float è data dalla somma dell'overhead della lista + l'occupazione del float32 (senza overhead):

>>> lista = [6.7, 8.9]
>>> sys.getsizeof(lista)
80

Che è corretto!
64B (overhead lista) + 2*8B (float32 senza overhead) = 80B

Quindi nel caso del mio secondo esempio, lo spazio teorico occupato dovrebbe essere
64B (overhead lista) + 100*8B (float32 senza overhead) = 864B
che poco si discosta dai 912B (i 48B in più sono usati dalla lista per gestire 100 posizioni, e probabilmente ci sono anche un po' di posti extra per accogliere eventuali nuovi elementi in modo più veloce).

Certo, rimane per me ancora il mistero della gestione delle stringhe in una lista...

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 2.821
  • Punti reputazione: 9
    • Mostra profilo
Re:Mistero su sys.getsizeof applicato alle liste
« Risposta #2 il: Novembre 24, 2018, 18:35 »
Uhm, non so quanto l'anima pia ti abbia davvero illuminato. Una lista in cpython è implementata essenzialmente come una lista di puntatori agli oggetti che contiene. Quindi non mi meraviglia affatto che sia più "piccola" della somma del suo contenuto. Un elemento della lista può essere un oggetto gigantesco, ma nella lista viene conservato solo il puntatore ad esso. Il fatto che i puntatori siano a loro volta dei numeri può spiegare la coincidenza per cui se la lista contiene numeri, allora sembra che "il conto torni". Ma è una coincidenza, appunto.

Offline marcus72

  • python unicellularis
  • *
  • Post: 24
  • Punti reputazione: 0
    • Mostra profilo
Re:Mistero su sys.getsizeof applicato alle liste
« Risposta #3 il: Novembre 24, 2018, 20:02 »
Uhm, non so quanto l'anima pia ti abbia davvero illuminato. Una lista in cpython è implementata essenzialmente come una lista di puntatori agli oggetti che contiene. Quindi non mi meraviglia affatto che sia più "piccola" della somma del suo contenuto. Un elemento della lista può essere un oggetto gigantesco, ma nella lista viene conservato solo il puntatore ad esso. Il fatto che i puntatori siano a loro volta dei numeri può spiegare la coincidenza per cui se la lista contiene numeri, allora sembra che "il conto torni". Ma è una coincidenza, appunto.

Mi sa che hai ragione, perché una lista di 10 float e un'altra di 10 stringhe da 51 caratteri l'una occupano esattamente entrambe 192B.
Non è che voglio criticare questa cosa: basta saperlo e sommare, allo spazio occupato dalla lista anche quello occupato dai suoi elementi.
Comunque grazie!

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 2.821
  • Punti reputazione: 9
    • Mostra profilo
Re:[RISOLTO] Mistero su sys.getsizeof applicato alle liste
« Risposta #4 il: Novembre 24, 2018, 20:09 »
Non ci siamo. Se una lista contiene altre liste, o se contiene oggetti che contengono riferimenti ad altri oggetti... etc, etc, (i casi possibili sono zillioni), non riuscirai comunque a calcolare "quanta memoria occupa la lista". E poi: hai la lista, ma hai anche lo spazio in memoria che il reference counting dedica a quella lista. Conti anche quello, o no? Eccetera eccetera. In generale, in un linguaggio di alto livello come Python, ha poco senso mettersi a fare conti del genere. Poi certo, ci sono scenari in cui potrebbe servirti, per esempio quando stai dando la caccia a un baco.