2010-09-30 9 views
7

Pracuję na systemie analitycznym opartym na Scala (http://www.hiringthing.com) i stwierdzam, że często zadaję sobie następujące pytanie. Biorąc pod uwagę "czystą" funkcję bez efektów ubocznych, jeśli dwukrotnie trafię tę funkcję tymi samymi wejściami, czy mogę oczekiwać, że kompilator ponownie użyje wartości wygenerowanej od pierwszego uruchomienia, czy też przejdzie przez cały kod ponownie. Innymi słowy, czy pierwszy przykład poniżej jest bardziej wydajny niż drugi?Jak efektywny jest kompilator Scali przy ponownym wykorzystaniu znanych wyników funkcji?

def add(x: Int, y: Int) = x + y * 10000000000 

val a = add(1,2) 
do_something(a) 
do_another_thing(a) 

vs.

def add(x: Int, y: Int) = x + y * 10000000000 

do_something(add(1,2)) 
do_another_thing(add(1,2)) 

Jeśli kompilator może rzeczywiście zoptymalizować drugi przypadek, to nie ogranicza się do złożoności funkcji?

Co chcę zrobić, to unikać używania pewne funkcje matematyczne ciężkich wielokrotnie właśnie przez wzgląd na wygodę programowania ...

Dzięki

+0

javap może pomóc zaglądnąć do kodu bajtowego wygenerowanego przez kompilator. Wątpię jednak, aby obecny kompilator dokonał optymalizacji, po prostu dlatego, że bez monad trudno jest określić, czy kod ma jakieś skutki uboczne, ale mogę się mylić. –

Odpowiedz

9

Z tego, co widziałem, kompilator Scala nie optymalizuje go w ogóle. JVM może, jeśli może stwierdzić, że wyniki się nie zmienią, ale często nie ma dobrego sposobu na poznanie.

Generalnie, jeśli jest to trywialne obliczenie, nie ma to znaczenia, zarówno dlatego, że jest trywialne, jak i dlatego, że JVM może dowiedzieć się, że wystarczy zrobić to tylko raz. Jeśli jest to skomplikowane, a szybkość jest niezbędna, należy użyć metody val a =, chyba że masz testy porównawcze, które wykazują, że JVM jest w tym przypadku wystarczająco inteligentna.

Należy pamiętać, że czasami trudno jest umieścić vals. Są dwa sposoby na obejście tego. Po pierwsze zauważ, że prawie wszystko można zastąpić odpowiednikiem w nawiasach klamrowych, więc wyjątki mogą być rzadsze niż myślisz. Ponadto, ten sposób może być użyteczne w pewnych okolicznościach:

def reuse[A,B](a: A)(f: A => B) = f(a) 
reuse(add(1,2))(a => { do_something(a); do_another_thing(a)}) 
+1

Przez długą tradycję Lisp i Haskell, funkcja "ponownego użycia" powinna być prawdopodobnie nazwana "niech" –

+0

Tak, prawdopodobnie tak. –

5

Kompilator scala nie próbuje zoptymalizować każdy poprzez zaproszenia do funkcje lub metody, chyba że specjalnie używasz adnotacji @inline (a nawet to nie jest gauranteed). Powiedział, że JVM, a zwłaszcza kompilator HotSpot JIT, prawie na pewno będą w stanie wstawić wywołanie "dodaj" w swoich przykładach, a następnie będą w stanie usunąć wynikowe wspólne podwyrażenia.

Jak zawsze, gdy zadajesz pytania dotyczące wydajności i optymalizacji kompilatora, odpowiedzi nie powinny być traktowane jako ewangelia bez rozległego, profesjonalnego testu porównawczego. Wyniki osiągane w przeszłości nie stanowią zachęty do przyszłych zysków. Zawartość może się rozliczyć podczas wysyłki. Jeśli obrzęk utrzymuje się po czterech godzinach, skontaktuj się z lekarzem. Wszystkie modele powyżej 18.

+2

Zapomniałeś "lub 21 w razie potrzeby". –

5

bez systemu efekt kompilator po prostu nie może zdecydować, aby ponownie użyć metody (lub Function) wartości powrotne.

Podobnie jak w przypadku większości języków w całej historii przetwarzania danych, do Ciebie należy usunięcie nadmiaru z algorytmów.

Powiązane problemy