2012-02-07 8 views
7

Piszę wielowątkowy dekompresor w Pythonie. Każdy wątek musi uzyskać dostęp do innego fragmentu pliku wejściowego.Czytanie pojedynczego pliku z wielu wątków w pythonie

Uwaga 1: nie można załadować całego pliku, ponieważ mieści się w zakresie od 15 Gb do 200 Gb; Nie używam wielowątkowości do przyspieszenia odczytu danych, ale dekompresji danych, chcę tylko upewnić się, że odczytane dane nie spowalniają dekompresji.

Uwaga 2: GIL nie stanowi problemu, ponieważ główna funkcja dekompresora jest rozszerzeniem C i wywołuje Py_ALLOW_THREADS, dzięki czemu GIL jest zwalniany podczas dekompresji. Druga faza dekompresji używa numpy, która jest również wolna od GIL.

1) Sądziłem, że to nie działa po prostu udostępnić obiekt dekompresor (które zasadniczo otacza obiekt pliku), ponieważ jeśli nawlec wywołuje następujące:

decompressor.seek(x) 
decompressor.read(1024) 

i nici B robi to samo, gwint A może skończyć odczyt z przesunięcia B wątku. Czy to jest poprawne?

2) Teraz po prostu sprawię, że każda nitka stworzy swoją własną wersję dekompresora i wygląda na to, że działa, ale nie jestem pewien, czy jest to najlepsze podejście. uznałem te możliwości:

  • dodać coś takiego

    seekandread(from_where, length) 
    

    do klasy dekompresor, który uzyskuje blokadę, szuka, czyta i zwalnia blokadę;

  • Utwórz wątek, który czeka na żądania odczytu i wykonuje je we właściwej kolejności.

Czy brakuje mi oczywistego rozwiązania? Czy istnieje znacząca różnica w wydajności między tymi metodami?

Dzięki

+5

Odczytywanie pliku w trybie wielowątkowym w rzeczywistości spowalnia proces, gdy masz dysk twardy. Igła musi przeskakiwać z jednego miejsca do drugiego zamiast pracować w sposób powtarzalny. Powinieneś załadować plik przed jego przetworzeniem. –

+0

Nie można załadować całego pliku, ponieważ mieści się w zakresie od 15 Gb do 200 Gb; Nie używam wielowątkowości do przyspieszenia odczytu danych, ale dekompresji danych, chciałem tylko upewnić się, że odczytane dane nie spowalniają dekompresji. – Alberto

+0

Oczywiście może to dotyczyć dysków SSD. Nie mam pojęcia o tym temacie. Nie powinieneś przekazywać tego za pomocą sprzętu. Gdy dyski SSD są dość powszechne, wykonywanie we/wy w trybie wielowątkowym może być wydajne. –

Odpowiedz

2

Możesz użyć mmap. Zobacz mmap() vs. reading blocks

Jak zauważa Tim Cooper, mmap jest to dobry pomysł, gdy masz dostęp losowy (wiele wątków będzie się wydawać, masz to), i będą mogli dzielić te same fizyczne strony.

+0

To wydaje się doskonałe! Spojrzałem na dokumentację Pythona dla mmap, ale nie mogłem znaleźć odniesienia do bezpieczeństwa wątków. Jeśli 2 wątki robią coś takiego jak a = zmapowany plik [x: y] w tym samym czasie, czy działa zgodnie z oczekiwaniami? – Alberto

+0

Aby odpowiedzieć sobie, wydaje się, że notacja Pythona mmap slice jest rzeczywiście wątki bezpieczne. Stworzyłem program testowy, który uzyskuje dostęp do różnych części pliku mmapped z różnych wątków i sprawdza wynik. Przekazuje test, jeśli używam notacji plastra, to się nie powiedzie, jeśli użyję funkcji wyszukiwania/odczytu. Nadal jednak muszę sprawdzić wydajność. – Alberto

+1

@Alberto: Wydaje mi się, że każdy segment, który jest już przetwarzany, powinien być chroniony przez przynajmniej muteks, jeśli nie jest to semafor warunkowy do rzucania. Przez rzucający semafor warunkowy mam na myśli semafor, który nie czeka, jeśli warunek wstępny nie jest spełniony, dopóki nie jest, ale zamiast tego rzuca wyjątek. To hybryda między semaforem a gaurdem. Możesz rzucić tylko wtedy, gdy warunek B nie jest spełniony i czekać, czy warunek A jest spełniony. –

2

może chcesz użyć wzoru Leader/Follower jeśli nie robi to już.
Wątek Przywódca będzie wiedział, które segmenty są już obsługiwane, a które nie są i przypisuje się do następnego nieprzetworzonego segmentu, a następnie staje się obserwatorem, pozostawiając przywództwo do następnego dostępnego wątku w puli.

+0

Dzięki, zagłębię się w to. – Alberto

1

CPython ma GIL tak wiele wątków nie zwiększa wydajności dla zadań związanych z procesorem.

Jeśli problem nie jest związany z IO (dysk zapewnia/przechowuje dane szybciej niż dekompresuje je CPU), można użyć multiprocessing module: każdy proces otwiera plik i dekompresuje dany zakres bajtów.

+1

Główna funkcja dekompresora jest rozszerzeniem C i wywołuje Py_ALLOW_THREADS, dzięki czemu GIL jest zwolniony podczas dekompresji. Druga faza dekompresji używa numpy, która jest również pozbawiona gil. Zmierzyłem już dobre przyspieszenie. – Alberto

+0

(może to wyjaśnienie - o tym, że "podjąłeś opiekę" nad GIL - może również przejść do głównej treści pytania) – jsbueno

+0

Masz rację, zrobione – Alberto