2014-07-03 7 views
7

Próbuję dodać równoległość do programu, który konwertuje .bmp na skalę szarości .bmp. Widzę zazwyczaj 2-4x gorszą wydajność dla kodu równoległego. Zmieniam rozmiary parabufferów/chunkingów i nadal nie mogę o tym myśleć. Szukasz wskazówek.Lazy IO + Parallelism: konwertowanie obrazu na skalę szarości

Cały plik źródłowy użyty tutaj: http://lpaste.net/106832

Używamy Codec.BMP czytać w strumieniu pikseli reprezentowana przez type RGBA = (Word8, Word8, Word8, Word8). Aby przekonwertować na skalę szarości, po prostu zamapuj transformację "luma" na wszystkie piksele.

Realizacja seryjny jest dosłownie:

toGray :: [RGBA] -> [RGBA] 
toGray x = map luma x 

Test .bmp wejście jest 5184 x 3456 (71,7 MB).

Implementacja szeregowa działa w ~ 10 s, ~ 550ns/piksel. Threadscope wygląda czysty:

serial

Dlaczego jest to tak szybko? Przypuszczam, że ma coś z leniwym ByteStringiem (nawet jeśli Codec.BMP używa ścisłego ByteStringa - czy jest tu niejawna konwersja?) I fusion.

Dodawanie Równo- ległości

pierwsza próba była poprzez dodanie równoległość parList. O chłopie. Program wykorzystał pamięć ~ 4-5 GB i system zaczął się wymieniać.

Potem przeczytałem rozdział "Paralizing Lazy Streams with parBuffer" w książce Simona Marlowa O'Reilly i wypróbowałem parBuffer z dużym rozmiarem. To nadal nie dawało pożądanej wydajności. Rozmiary iskier były niewiarygodnie małe.

Potem próbowali zwiększyć rozmiar zapłonową przez wyrwy listę leniwy, a następnie trzyma się parBuffer dla równoległości:

toGrayPar :: [RGBA] -> [RGBA] 
toGrayPar x = concat $ (withStrategy (parBuffer 500 rpar) . map (map luma)) 
         (chunk 8000 x) 

chunk :: Int -> [a] -> [[a]] 
chunk n [] = [] 
chunk n xs = as : chunk n bs where 
    (as,bs) = splitAt (fromIntegral n) xs 

Ale to wciąż nie daje pożądanych wyników:

18,934,235,760 bytes allocated in the heap 
    15,274,565,976 bytes copied during GC 
    639,588,840 bytes maximum residency (27 sample(s)) 
    238,163,792 bytes maximum slop 
      1910 MB total memory in use (0 MB lost due to fragmentation) 

            Tot time (elapsed) Avg pause Max pause 
    Gen 0  35277 colls, 35277 par 19.62s 14.75s  0.0004s 0.0234s 
    Gen 1  27 colls, 26 par 13.47s 7.40s  0.2741s 0.5764s 

    Parallel GC work balance: 30.76% (serial 0%, perfect 100%) 

    TASKS: 6 (1 bound, 5 peak workers (5 total), using -N2) 

    SPARKS: 4480 (2240 converted, 0 overflowed, 0 dud, 2 GC'd, 2238 fizzled) 

    INIT time 0.00s ( 0.01s elapsed) 
    MUT  time 14.31s (14.75s elapsed) 
    GC  time 33.09s (22.15s elapsed) 
    EXIT time 0.01s ( 0.12s elapsed) 
    Total time 47.41s (37.02s elapsed) 

    Alloc rate 1,323,504,434 bytes per MUT second 

    Productivity 30.2% of total user, 38.7% of total elapsed 

gc_alloc_block_sync: 7433188 
whitehole_spin: 0 
gen[0].sync: 0 
gen[1].sync: 1017408 

par1

Jak mogę lepiej zrozumieć, co się tutaj dzieje?

+0

Ustaliłeś rozsądny czas bazowy? Ile czasu zajmuje obliczenie długości '[RGBA]'? Ponieważ inne twoje komentarze wskazują, że ta wartość jest przesyłana strumieniowo z leniwym IO, jest całkiem możliwe, że czas IO zawsze będzie dominował przy przetwarzaniu twoich działań, równolegle lub nie. Więc ile czasu pracy to po prostu IO i parsowanie? – Carl

+0

Mogę spróbować zobaczyć, jak długo trwa przetwarzanie IO i Codec.BMP. Linia bazowa, której używam, to seryjna implementacja, która trwa ~ 10 sekund. Sądzę, że jest to wystarczająco użyteczne, by porównać z 30-40, które zajmuje równoległa implementacja. – brooksbp

Odpowiedz

0

Masz dużą listę pikseli RGBA. Dlaczego nie używasz parListChunk z rozsądnym rozmiarem kawałka?

+0

To wydaje się być bardziej komentarzem niż odpowiedzią, nie rozwiązuje problemu PO, ale po prostu sugeruje coś, co można wypróbować. – bheklilr

+0

parListChunk wymusza grzbiet obrazu [5184 x 3456], który zajmuje wiele GB pamięci.Próbuję tego uniknąć i nadal używam leniwego IO. – brooksbp

Powiązane problemy