Mówi się, że Scala Dla zrozumienia są w rzeczywistości dość powolne. Podano mi, że ze względu na ograniczenie Java, dla zrozumienia (takiego jak "zmniejszenie", użytego poniżej), należy wygenerować tymczasowy obiekt z każdą iteracją, aby wywołać funkcję przekazaną.Dlaczego Scala "dla pętli" jest tak bardzo powolna w porównaniu do pętli FOR?
IS .. .TA PRAWDA? Poniższe testy zdają się potwierdzać, ale nie do końca rozumiem, dlaczego tak się dzieje.
To może mieć sens dla „lambda” lub funkcji anonimowych, ale nie dla funkcji nieanonimowych.
W moim teście, uruchomiłem pętle przed list.reduce (zobacz poniższy kod) i okazało się, że są ponad dwa razy szybsze, nawet jeśli każda iteracja nazywa dokładnie tę samą funkcję, która została przekazana do zredukowania!
Uważam to niezwykle intuicyjne (raz pomyśli, że biblioteka Scala byłby już starannie stworzony, aby być tak optymalne jak to możliwe).
W teście I umieszczone razem, uruchomiony w tej samej pętli (Podsumowując numery 1 do milion, bez przepełnienia) pięć różnych sposobów
- do pętli ponad tablicy wartości
- pętli, lecz wywoływania funkcji zamiast inline arytmetyki
- pętli, tworząc obiekt, który zawiera funkcję Dodawanie
- list.reduce, przechodząc I anonimową funkcję
- list.reduce, przekazując człon Przedmiotem funkcyjne
Wyniki były następujące: testy: min/max/średnie (milisekund)
1. 27/157/64.78
2. 27/192/65.77 <--- note the similarity between tests 1,2 and 4,5
3. 139/313/202.58
4. 63/342/150.18
5. 63/341/149.99
Jak można zauważyć, że „dla zrozumienia” wersje rzędu " dla nowego dla każdej instancji ", co oznacza, że" nowy "może być w rzeczywistości przeprowadzany zarówno w anonimowych, jak i nieanonimowych wersjach funkcji.
Metodologia: kod poniżej (call Test usunięty) została opracowana w jednym pliku .jar, aby zapewnić wszystkie wersje prowadził ten sam kod biblioteki. Każdy test w każdej iteracji był wywoływany w nowej maszynie JVM (tj. Scala -cp ... dla każdego testu) w celu usunięcia problemów związanych z wielkością sterty.
class t(val i: Int) {
def summit(j: Int) = j + i
}
object bar {
val biglist:List[Int] = (1 to 1000000).toList
def summit(i: Int, j:Int) = i+j
// Simple for loop
def forloop: Int = {
var result: Int = 0
for(i <- biglist) {
result += i
}
result
}
// For loop with a function instead of inline math
def forloop2: Int = {
var result: Int = 0
for(i <- biglist) {
result = summit(result,i)
}
result
}
// for loop with a generated object PER iteration
def forloop3: Int = {
var result: Int = 0
for(i <- biglist) {
val t = new t(result)
result = t.summit(i)
}
result
}
// list.reduce with an anonymous function passed in
def anonymousfunc: Int = {
biglist.reduce((i,j) => {i+j})
}
// list.reduce with a named function
def realfunc: Int = {
biglist.reduce(summit)
}
// test calling code excised for brevity. One example given:
args(0) match {
case "1" => {
val start = System.currentTimeMillis()
forloop
val end = System.currentTimeMillis()
println("for="+(end - start))
}
...
}
'.reduce' nie ma nic wspólnego z" dla zrozumienia " –
Na marginesie jest wtyczka do kompilatora scala, która ma na celu usunięcie tego narzutów z typowych przypadków: https://code.google.com/p/scalacl/wiki/ScalaCLPlugin. Sam jednak tego nie próbowałem. –
W rzeczywistości, twoje pierwsze 3 testy używają do zrozumienia, a Ty porównujesz terminy tych z 'redu' .. –