2010-12-26 11 views
6

Przetwarzam bardzo duże ilości danych, przechowywane w słowniku, przy użyciu wieloprocesowości. Zasadniczo wszystko, co robię, to ładowanie niektórych podpisów, przechowywanie w słowniku, budowanie z niego wspólnego obiektu dict (pobieranie obiektu 'proxy' zwróconego przez program Manager.dict()) i przekazywanie tego proxy jako argumentu funkcji, która ma do wykonania w trybie wieloprocesowym.python: udostępnianie ogromnych słowników przy użyciu wieloprocesowości

prostu do wyjaśnienia:

signatures = dict() 
load_signatures(signatures) 
[...] 
manager = Manager() 
signaturesProxy = manager.dict(signatures) 
[...] 
result = pool.map (myfunction , [ signaturesProxy ]*NUM_CORES) 

Teraz wszystko działa perfekcyjnie jeśli podpisy jest mniejsza niż 2 miliony wpisów lub więcej. W każdym razie, muszę przetworzyć słownik z kluczami 5.8M (zbieranie podpisów w formacie binarnym generuje plik 4,8 GB). W tym przypadku proces umiera podczas tworzenia obiektu proxy:

Traceback (most recent call last): 
    File "matrix.py", line 617, in <module> 
signaturesProxy = manager.dict(signatures) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 634, in temp 
token, exp = self._create(typeid, *args, **kwds) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 534, in _create 
id, exposed = dispatch(conn, None, 'create', (typeid,)+args, kwds) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 79, in dispatch 
raise convert_to_error(kind, result) 
multiprocessing.managers.RemoteError: 
--------------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 173, in handle_request 
    request = c.recv() 
EOFError 
--------------------------------------------------------------------------- 

Znam struktura danych jest ogromny, ale pracuję na maszynie wyposażone w/32GB pamięci RAM oraz systemem top widzę, że proces, po załadowaniu sygnatur, zajmuje 7 GB pamięci RAM. Następnie zaczyna budować obiekt proxy, a użycie pamięci RAM wzrasta do ~ 17 GB pamięci RAM, ale nigdy nie zbliża się do 32. W tym momencie użycie pamięci RAM zaczyna maleć szybko i proces kończy się z powyższym błędem. Sądzę więc, że nie jest to spowodowane błędem braku pamięci ...

Czy masz pomysł lub sugestię?

Dziękuję

Davide

Odpowiedz

-3

Jeśli słowniki są tylko do odczytu, nie potrzebujesz obiektów proxy w większości systemów operacyjnych.

Po prostu wczytaj słowniki przed rozpoczęciem pracy robotników i umieść je gdzieś, gdzie będą dostępne; Najprostszym miejscem jest globalnie moduł. Będą czytelne dla pracowników.

from multiprocessing import Pool 

buf = "" 

def f(x): 
    buf.find("x") 
    return 0 

if __name__ == '__main__': 
    buf = "a" * 1024 * 1024 * 1024 
    pool = Pool(processes=1) 
    result = pool.apply_async(f, [10]) 
    print result.get(timeout=5) 

tylko ta wykorzystuje 1GB pamięci w połączeniu, nie 1GB dla każdego procesu, ponieważ każdy współczesny OS uczyni cień kopiowanie przy zapisie danych utworzonych przed widelcem. Pamiętaj tylko, że zmiany danych nie będą widoczne dla innych pracowników, a pamięć zostanie oczywiście przydzielona na wszelkie dane, które zmienisz.

Będzie używać pewnej pamięci: strona każdego obiektu zawierającego licznik odwołań zostanie zmodyfikowana, więc zostanie przydzielona. Czy to ma znaczenie zależy od danych.

Będzie to działać na każdym systemie operacyjnym, który implementuje zwykłe rozwidlania. To nie zadziała w systemie Windows; jego (okaleczony) model procesu wymaga ponownego uruchomienia całego procesu dla każdego pracownika, więc nie jest zbyt dobry w udostępnianiu danych.

+1

Czy to działa z Windows 7 (który jest zdecydowanie nowoczesnym systemem operacyjnym?) –

+0

@Seun: Nie wiem; spróbuj go przetestować. Wątpię, aby jego model procesu był bardziej nowoczesny niż poprzednie wersje; Windows zawsze był w ciemnych wiekach. –

+1

(Nic takiego jak SO dla losowych, niepoprawnych downvotes.) –

2

W trosce o oszczędność czasu i brak konieczności debugowania problemów na poziomie systemu, może można podzielić słownika rekord 5,8 miliona do trzech zestawów ~ 2 mln każdy i wykonuj zadanie 3 razy.

+0

mogłem, ale to nie jest optymalnym rozwiązaniem, ponieważ, tak czy inaczej, w końcu będę musiał zrekonstruować cały słownik i użyć go do innych operacji –

+0

Wtedy to brzmi jak zadanie byłoby właściwe dla Hadoop/MapReduce ... Może powinieneś to sprawdzić. – Fragsworth

6

Dlaczego nie spróbujesz tego z bazą danych? Bazy danych nie ograniczają się do adressable/physical ram i są bezpieczne dla użycia multithread/process.

0

Myślę, że problemem, z którym się zetknąłeś, był stół dyktujący lub mieszający, zmieniający się w miarę wzrostu. Początkowo dyktuje określoną liczbę dostępnych wiader. Nie jestem pewien co do Pythona, ale wiem, że Perl zaczyna się od 8, a kiedy kubełki są pełne, skrót zostaje odtworzony o 8 kolejnych (tj.8, 16, 32, ...).

Wiadro to miejsce lądowania dla algorytmu wyznaczania wartości skrótu. 8 miejsc nie oznacza 8 wpisów, co oznacza 8 miejsc w pamięci. Po dodaniu nowego elementu generowany jest skrót dla tego klucza, a następnie zapisywany w tym zasobniku.

Tu dochodzi do kolizji. Im więcej elementów znajduje się w zasobniku, tym wolniejsza funkcja, ponieważ elementy są dodawane sekwencyjnie ze względu na dynamiczny rozmiar gniazda.

Jednym z problemów, które mogą wystąpić, jest to, że klucze są bardzo podobne i dają ten sam wynik skrótu - co oznacza, że ​​większość kluczy znajduje się w jednym gnieździe. Wstępne przydzielenie segmentów mieszających pomoże wyeliminować to i faktycznie poprawić czas przetwarzania i zarządzanie kluczami, a także nie musi już wykonywać całej tej zamiany.

Jednak myślę, że nadal jesteś ograniczony do ilości wolnej pamięci ciągłej i ostatecznie będziesz musiał przejść do rozwiązania bazy danych.

uwaga boczna: Wciąż jestem nowy w Pythonie, wiem, że w Perlu można zobaczyć statystyki hash, wykonując print% HASHNAME, pokaże on rozkład zużycia kubełka. Pomaga zidentyfikować liczbę kolizji - jeśli potrzebujesz wstępnie alokować wiadra. Czy można to zrobić również w Pythonie?

Rich

Powiązane problemy