2011-12-31 11 views
9

Ja tylko nauka Haskell i napisał dwa programy z witryny samouczka, tak żeW Haskell, wydajności i gdzie wiązanie

maximumnowhere :: (Ord a) => [a] -> a 
maximumnowhere [] = error "empty" 
maximumnowhere [x] = x 
maximumnowhere (x:xs) = if x > maximumnowhere xs then x else maximumnowhere xs 

i

maximumwhere :: (Ord a) => [a] -> a 
maximumwhere [] = error "empty" 
maximumwhere [x] = x 
maximumwhere (x:xs) = if x > maximum' then x else maximum' where maximum' = maximumwhere xs 

Myślałem, te dwa programy są dość równoważne, ponieważ myślałem, że miejsce wiązania tylko zastępuje zmienną zawartością. ale kiedy uruchomiłem go w ghci, pierwszy był znacznie wolniejszy od drugiego, szczególnie w przypadku macierzy o długości powyżej 25 lat. Prawdopodobnie, gdzie powiązanie powoduje ogromną różnicę wydajności, ale nie wiem dlaczego. Czy ktoś może mi to wyjaśnić?

+1

Pierwsza nie podzieli się ocenami "maximumnowhere xs" (używanymi zarówno w przypadku warunkowym, jak i innym) - jeśli chcesz się dzielić, powinieneś zrobić to samemu, jak w drugiej wersji. –

+3

Dodając dalsze informacje, GHC generalnie nie robi wspólnej eliminacji podwyrażeń (co sprawiłoby, że obie wersje wykonują to samo). Dzieje się tak dlatego, że CSE może wprowadzać wycieki przestrzeni w leniwym języku - zobacz FAQ GHC - http://www.haskell.org/haskellwiki/GHC:FAQ#Does_GHC_do_common_subexpression_elimination.3F –

+5

Dlaczego ludzie używają GHCI do pomiarów wydajności? Istnieje kompilator optymalizujący, który możesz przetestować z ... –

Odpowiedz

14

Nie, nie są one równoważne. let i where wprowadzenie sharing, co oznacza, że ​​wartość jest oceniana tylko raz. Kompilator na ogół nie podzieli wyniku dwóch identycznych wyrażeń, chyba że je przekażesz, ponieważ nie może ogólnie stwierdzić, czy wymiana czasu w przestrzeni czy czasie jest korzystna czy nie.

Tak więc, pierwszym program do połączeń na dwa rekurencyjne iteracji, co O (2^n), a drugi ma tylko jeden za iteracji tj O (n). Różnica między nimi jest ogromna. Na n = 25, pierwsze efekty programu w ponad 33 mln wywołań rekurencyjnych podczas gdy drugi ma tylko 25.

Więc morał tej historii jest to, że jeśli chcesz dzielić, trzeba poprosić o to przy użyciu let lub where.

+5

+1 Dobra odpowiedź. Ze względu na czystość Haskella często podkreślamy rozumowanie równania, ale dla wydajnego Haskella ważne jest, aby wiedzieć, jakie założenia robi kompilator. (W tym przypadku GHC na ogół oczekuje od programisty wyraźnego wskazania udostępniania). –

Powiązane problemy