2013-05-11 11 views
7

Pracuję nad projektem, który obejmuje dostęp do danych z dużej listy, która jest przechowywana w pamięci. Ponieważ lista jest dość obszerna (miliony linii), zwracam uwagę na to, ile pamięci jest używane. Używam OS X, więc utrzymuję Monitor aktywności otwarty podczas tworzenia tych list.Python list anomalne użycie pamięci

Zauważyłem, że ilość pamięci używanej przez listę może się znacznie różnić w zależności od sposobu jej budowy, ale nie mogę zrozumieć dlaczego.

Teraz przez jakiś przykład kodu:

(używam Python 2.7.4 na OSX 10.8.3)

Pierwsza funkcja poniżej tworzy listę i wypełnia go z wszystkich tych samych trzech liczb losowych.

Druga funkcja poniżej tworzy listę i wypełnia ją wszystkimi różnymi liczbami losowymi.

import random 
import sys 


def make_table1(size): 
    list1 = size *[(float(),float(),float())] # initialize the list 
    line = (random.random(), 
      random.random(), 
      random.random()) 
    for count in xrange(0, size): # Now fill it 
     list1[count] = line 
    return list1 

def make_table2(size): 
    list1 = size *[(float(),float(),float())] # initialize the list 
    for count in xrange(0, size): # Now fill it 
     list1[count] = (random.random(), 
         random.random(), 
         random.random()) 
    return list1 

(pierwsze chciałbym powiedzieć, że zdaję sobie sprawę, powyższy kod mógł być o wiele bardziej efektywnie napisane. Jest napisany w taki sposób, aby zachować dwa przykłady jak najbardziej podobne.)

Teraz tworzę niektóre listy korzystania z tych funkcji:

In [2]: thing1 = make_table1(6000000) 

In [3]: sys.getsizeof(thing1) 
Out[3]: 48000072 

w tym momencie moja pamięć używana skoki przez około 46 MB, co jest, czego można oczekiwać od informacji podanej powyżej.

Teraz do następnej funkcji:

In [4]: thing2 = make_table2(6000000) 

In [5]: sys.getsizeof(thing2) 
Out[5]: 48000072 

Jak widać, pamięć zajęta przez dwóch list jest taki sam. Są dokładnie tej samej długości, więc można się tego spodziewać. Czego się nie spodziewałem, to to, że moja pamięć wykorzystana przez Monitor aktywności przeskoczyła do ponad 1 GB!

Rozumiem, że będzie jakieś obciążenie, ale 20x tyle? 1 GB na listę 46 MB?

Poważnie?

Dobra, do diagnostyki ...

Pierwszą rzeczą próbowałem jest zebranie jakichkolwiek śmieci:

In [5]: import gc 

In [6]: gc.collect() 
Out[6]: 0 

Wykonana różnicę zerowy do ilości pamięci używanej.

Następny użyłem Guppy, aby zobaczyć gdzie pamięć będzie:

In [7]: from guppy import hpy 

In [8]: hpy().heap() 

Out[8]: 
Partition of a set of 24217689 objects. Total size = 1039012560 bytes. 
Index Count %  Size % Cumulative % Kind (class/dict of class) 
    0 6054789 25 484821768 47 484821768 47 tuple 
    1 18008261 74 432198264 42 917020032 88 float 
    2 2267 0 96847576 9 1013867608 98 list 
    3 99032 0 11392880 1 1025260488 99 str 
    4 585 0 1963224 0 1027223712 99 dict of module 
    5 1712 0 1799552 0 1029023264 99 dict (no owner) 
    6 13606 0 1741568 0 1030764832 99 types.CodeType 
    7 13355 0 1602600 0 1032367432 99 function 
    8 1494 0 1348088 0 1033715520 99 type 
    9 1494 0 1300752 0 1035016272 100 dict of type 
<691 more rows. Type e.g. '_.more' to view.> 

porządku, moja pamięć jest zajęta przez:

462 MB z krotki (hę?)

412 MB pływaka (jakie?)

92 MB liście (w porządku, to jeden sens. 2 * 46MB = 92)

Moje listy są przedalokowane, więc nie sądzę, że trwa nadmiar alokacji.

Pytania:

Dlaczego jest ilość pamięci używanej przez tych dwóch bardzo podobnych listach tak różne?

Czy istnieje inny sposób wypełniania listy, która nie ma tak dużego obciążenia?

Czy istnieje sposób na uwolnienie całej tej pamięci?

Uwaga: Proszę nie sugerować przechowywania na dysku lub korzystania z struktur danych array.array lub numpy lub pandas. To są świetne opcje, ale to nie dotyczy ich. To pytanie dotyczy zwykłych starych list.

Próbowałem podobny kod z Python 3.3, a wynik jest taki sam.

Oto ktoś z similar problem. Zawiera pewne wskazówki, ale to nie to samo pytanie.

Dziękuję wszystkim!

+1

wydaje się mieć interes w 2d tablicy rozmiaru 6000000 x 3; Czy spojrzałeś na numpy (np .: 'numpy.random.rand (6000000, 3)') – SingleNegationElimination

Odpowiedz

8

Obie funkcje tworzą listę 6000000 referencji.

sizeof(thelist) ≅ sizeof(reference_to_a_python_object) * 6000000 

Pierwsza lista zawiera 6000000 odniesień do tej samej krotki trzech pływaków.

Druga lista zawiera odniesienia do 6000000 różnych krotek zawierających 18000000 różnych elementów pływających.

enter image description here

Jak widać, pływak zajmuje 24 bajtów i potrójne zajmuje 80 bajtów (używając kompilacji python). Nie, nie ma możliwości, oprócz numpy.

Aby włączyć list do kolekcjonowania śmieci, trzeba pozbyć się wszelkich odniesień do nich:

del thing1 
del thing2