2015-10-23 13 views
6

Jestem ciekawy, jak zarządzanie pamięcią różni się między Bytearray a listą w Pythonie.Różnica między Bytearray i List w Pythonie

Znalazłem kilka pytań, takich jak Difference between bytearray and list, ale nie odpowiada dokładnie na moje pytanie.

Moje pytanie precyzyjnie ...

from array import array 
>>> x = array("B", (1,2,3,4)) 
>>> x.__sizeof__() 
36 
>>> y = bytearray((1,2,3,4)) 
>>> y.__sizeof__() 
32 
>>> z = [1,2,3,4] 
>>> z.__sizeof__() 
36 

Jak widać istnieje różnica w wielkości między listy/array.array (36 bajtów dla 4 elementów) oraz tablica bajtów (32 bajtów dla 4 elementów). Czy ktoś może mi wyjaśnić, dlaczego tak się dzieje? Ma to sens w przypadku tablicy bajtów, która zajmuje 32 bajtów pamięci dla elementów 4(4 * 8 == 32), ale jak można to zinterpretować dla list i array.array?

# Lets take the case of bytearray (which makes more sense to me at least :p) 
for i in y: 
     print(i, ": ", id(i)) 

1 : 499962320 
2 : 499962336 #diff is 16 units 
3 : 499962352 #diff is 16 units 
4 : 499962368 #diff is 16 units 

Dlaczego różnica między dwoma sąsiadującymi elementami różnią 16 jednostek tutaj, gdy każdy element zajmuje tylko 8 bajtów. Czy to oznacza, że ​​każdy wskaźnik adresu pamięci wskazuje na skubanie?

Również jakie są kryteria alokacji pamięci dla liczby całkowitej? Czytałem, że Python przydzieli więcej pamięci na podstawie wartości liczby całkowitej (popraw mnie, jeśli się mylę), jak im większa liczba, tym więcej pamięci.

Np

>>> y = 10 
>>> y.__sizeof__() 
14 
>>> y = 1000000 
>>> y.__sizeof__() 
16 
>>> y = 10000000000000 
>>> y.__sizeof__() 
18 

jakie są kryteria, które Python przydziela pamięć?

I dlaczego Python zajmuje o wiele więcej pamięci, podczas gdy C zajmuje tylko 8 bajtów (moja to maszyna 64-bitowa)? kiedy są idealnie poniżej zakresu liczb całkowitych (2 ** 64)?

Metadane:

Python version:'3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:43:06) [MSC v.1600 32 bit (Intel)]'

arch Maszyna: 64-bitowy

PS: Prosimy kierować mnie do dobrego artykułu, gdzie zarządzanie pamięcią Python jest wyjaśnione lepiej. Spędziłem prawie godzinę, aby wymyślić te rzeczy i w końcu zadałem to pytanie w SO. :(

+2

Dobre pytanie, przegłosowano. Hej, masz szczęście: na 64-bitowej maszynie Linux Xubuntu CPython 3.4.3 'y .__ sizeof __()' daje '28' dla' y = 10', to samo dla 'y = 1M',' 32' dla 'y = 10000000000000' – Pynchia

+0

Hi @Pynchia, Mine to 32-bitowy python, chociaż mój komputer ma 64-bitową. Nie jestem pewien, ale to może być powód. Pozwala poczekać, aż ktoś wyjaśni. –

Odpowiedz

1

Nie twierdzę to pełna odpowiedź, ale istnieją pewne wskazówki do zrozumienia tego.

bytearray jest ciągiem bajtów i list jest sekwencją odniesień do obiektu. Więc [1,2,3] rzeczywiście posiada wskaźniki pamięci do tych liczb, które są przechowywane w pamięci gdzie indziej. aby obliczyć całkowite zużycie pamięci struktury listy, możemy to zrobić (używam sys.getsizeof wszędzie dalej, to dzwoni __sizeof__ powiększonej GC napowietrznych)

>>> x = [1,2,3] 
>>> sum(map(getsizeof, x)) + getsizeof(x) 
172 

wynik może być re Różne na różnych maszynach.

też spojrzeć na to:

>> getsizeof([]) 
64 

To dlatego, że listy są zmienne. Aby być szybkim, ta struktura przydziela pewną pamięć zakres do przechowywania odniesień do obiektów (plus trochę pamięci dla meta, takie jak długość listy). Gdy dodajesz elementy, kolejne komórki pamięci są wypełniane referencjami do tych elementów. Kiedy nie ma miejsca na przechowywanie nowych przedmiotów, przydzielany jest nowy, większy zakres, istnieją skopiowane tam dane i stary wydany. Nazywane to dynamicznymi tablicami.

Możesz zaobserwować to zachowanie, uruchamiając ten kod.

import sys 
data=[] 
n=15 
for k in range(n): 
    a = len(data) 
    b = sys.getsizeof(data) 
    print('Length: {0:3d}; Size in bytes: {1:4d}'.format(a, b)) 
    data.append(None) 

Moje wyniki:

Length: 0; Size in bytes: 64 
Length: 1; Size in bytes: 96 
Length: 2; Size in bytes: 96 
Length: 3; Size in bytes: 96 
Length: 4; Size in bytes: 96 
Length: 5; Size in bytes: 128 
Length: 6; Size in bytes: 128 
Length: 7; Size in bytes: 128 
Length: 8; Size in bytes: 128 
Length: 9; Size in bytes: 192 
Length: 10; Size in bytes: 192 
Length: 11; Size in bytes: 192 
Length: 12; Size in bytes: 192 
Length: 13; Size in bytes: 192 
Length: 14; Size in bytes: 192 

Widzimy, że istnieje 64 bajtów zostało wykorzystane do przechowywania 8 adresów pamięci (każda 64-bit).

Niemal to samo dotyczy bytearray() (zmiana drugiej linii na data = bytearray() i dodanie 1 w ostatniej).

Length: 0; Size in bytes: 56 
Length: 1; Size in bytes: 58 
Length: 2; Size in bytes: 61 
Length: 3; Size in bytes: 61 
Length: 4; Size in bytes: 63 
Length: 5; Size in bytes: 63 
Length: 6; Size in bytes: 65 
Length: 7; Size in bytes: 65 
Length: 8; Size in bytes: 68 
Length: 9; Size in bytes: 68 
Length: 10; Size in bytes: 68 
Length: 11; Size in bytes: 74 
Length: 12; Size in bytes: 74 
Length: 13; Size in bytes: 74 
Length: 14; Size in bytes: 74 

Różnica polega na tym, że pamięć teraz używane do przechowywania wartości rzeczywiste bajtów, a nie wskaźniki.

Mam nadzieję, że pomoże ci to dokładniej zbadać.

+0

Witam @ anti1869, Dzięki za komentarz. Jest bardzo wyczerpujący i użyteczny. Ale mam następujące pytania w twoim komentarzu. Nie jestem w stanie dodać wszystkich informacji tutaj, a więc dodając kolejny komentarz poniżej. Dzięki –

+0

Jest to zrozumiałe dla listy zgodnie z twoim wyjaśnieniem, ale dlaczego rozmiar tablicy bajtów rozpoczął się od 56. i dlaczego jest stabilny po osiągnięciu 74? A także byłby zadowolony, jeśli możesz podać więcej informacji, dlaczego początkowy rozmiar, jeśli 64 i 56. Dzięki –

+0

Sprawdź kod źródłowy struktur danych. Tam zobaczysz wewnętrzną strukturę kontenera i do czego przydziela pamięć po inicjalizacji. Również jest bardzo wyraźnie widoczny algorytm wzrostu. https://github.com/python/cpython/blob/master/Objects/listobject.c – anti1869

Powiązane problemy