11

Aby być bardziej konkretne, mam następujący niewinnie wyglądające trochę Repa 3 program:Jakie są kluczowe różnice między interfejsami API Repa 2 i 3?

{-# LANGUAGE QuasiQuotes #-} 

import Prelude hiding (map, zipWith) 
import System.Environment (getArgs) 
import Data.Word (Word8) 
import Data.Array.Repa 
import Data.Array.Repa.IO.DevIL 
import Data.Array.Repa.Stencil 
import Data.Array.Repa.Stencil.Dim2 

main = do 
    [s] <- getArgs 
    img <- runIL $ readImage s 

    let out = output x where RGB x = img 
    runIL . writeImage "out.bmp" . Grey =<< computeP out 

output img = map cast . blur . blur $ blur grey 
    where 
    grey    = traverse img to2D luminance 
    cast n   = floor n :: Word8 
    to2D (Z:.i:.j:._) = Z:.i:.j 

--------------------------------------------------------------- 

luminance f (Z:.i:.j) = 0.21*r + 0.71*g + 0.07*b :: Float 
    where 
    (r,g,b) = rgb (fromIntegral . f) i j 

blur = map (/ 9) . convolve kernel 
    where 
    kernel = [stencil2| 1 1 1 
         1 1 1 
         1 1 1 |] 

convolve = mapStencil2 BoundClamp 

rgb f i j = (r,g,b) 
    where 
    r = f $ Z:.i:.j:.0 
    g = f $ Z:.i:.j:.1 
    b = f $ Z:.i:.j:.2 

Która zajmuje to dużo czasu na przetworzenie do 640x420 obraz na moim 2Ghz Core 2 Duo laptop:

real 2m32.572s 
user 4m57.324s 
sys  0m1.870s 

Wiem, że coś musi być w błędzie, ponieważ uzyskałem znacznie lepszą wydajność w przypadku bardziej złożonych algorytmów korzystających z Repa 2. Pod tym API, znaczna poprawa, którą odkryłem, pochodziła z dodania wywołania "force" przed każdą transformacją tablicy (którą rozumieć każde wezwanie do mapowania, konwergowania, trawersowania itp.). Nie bardzo rozumiem analogiczną rzecz do zrobienia w Repa 3 - w rzeczywistości myślałem, że parametry nowego typu manifestacji mają zapewnić, że nie ma dwuznaczności, kiedy tablica musi zostać zmuszona? A jak nowy monadyczny interfejs pasuje do tego schematu? Przeczytałem ładny tutorial autorstwa Dona S, ale są pewne kluczowe luki między interfejsami API Repa 2 i 3, które są mało omawiane w Internecie AFAIK.

Po prostu, czy istnieje minimalnie skuteczny sposób, aby poprawić wydajność powyższego programu?

Odpowiedz

10

Nowe parametry typu reprezentacji nie powodują automagicznie w razie potrzeby (prawdopodobnie jest to trudny problem) - nadal trzeba wymuszać ręczne sterowanie. W Repa 3 odbywa się to za pomocą funkcji computeP:

computeP 
    :: (Monad m, Repr r2 e, Fill r1 r2 sh e) 
    => Array r1 sh e -> m (Array r2 sh e) 

Ja osobiście naprawdę nie rozumiem, dlaczego to monadycznego, bo można równie dobrze wykorzystywać monada Tożsamość:

import Control.Monad.Identity (runIdentity) 
force 
    :: (Repr r2 e, Fill r1 r2 sh e) 
    => Array r1 sh e -> Array r2 sh e 
force = runIdentity . computeP 

Więc teraz swoje output funkcja może być przepisany z odpowiednimi zmuszając:

output img = map cast . f . blur . f . blur . f . blur . f $ grey 
    where ... 

skrótem f przy użyciu funkcji pomocnika u do pomocy rodzaj wnioskowania:

u :: Array U sh e -> Array U sh e 
u = id 
f = u . force 

Z tych zmian SpeedUp jest dość dramatyczna - co należy się spodziewać, gdyż bez pośredniego zmuszając kończy się każdy piksel wyjściowy oceny znacznie więcej niż jest to konieczne (wartości pośrednie nie są udostępniane) .

Twój oryginalny kod:

real 0m25.339s 
user 1m35.354s 
sys  0m1.760s 

Z zmuszając:

real 0m0.130s 
user 0m0.320s 
sys  0m0.028s 

Testowane z 600x400 PNG, pliki wyjściowe były identyczne.

+0

To jest świetna odpowiedź! Zrozumiałem, że computeP zastępuje "siłę", ale nie myślał, że użyje jej z monadą tożsamości. Doceniam twoją pomoc. – sacheie

+1

Uważam, że powodem stosowania monadycznych typów powrotu jest to, że idea wymuszania czegoś jest ściśle związana z siłami, które następują kolejno. Lepsze wyjaśnienie znajdziesz w http://www.cse.unsw.edu.au/~chak/papers/LCKP12.html – Axman6

7

computeP to nowy force.

W Repa 3 trzeba użyć computeP wszędzie byś stosowane force w Repa 2.

The Laplace przykład z repa-przykładów jest podobny do tego, co robisz. Powinieneś także użyć cmap zamiast zwykłego map w swojej funkcji blur. Na początku mojej strony pojawi się artykuł wyjaśniający, dlaczego tak się stało na mojej stronie głównej.

+0

Wspaniała cecha społeczności Haskell - opinie od twórców bibliotek :) Czekam z niecierpliwością na Twój referat. – sacheie

Powiązane problemy