Edytuj: Mój rozmiar próbki był zbyt mały. Kiedy uruchomiłem go na podstawie rzeczywistych danych na 8 procesorach, widziałem wzrost prędkości 7,2x. Nie jest zbyt obskurny, aby dodać 4 znaki do mojego kodu;)Scala kolekcja równoległa runtime zagadkowe
Obecnie jestem w trakcie próby "sprzedaży" zarządzania na korzyściach płynących z używania Scali, szczególnie jeśli chodzi o skalowanie z procesorami. W tym celu stworzyłem prostą aplikację testową, która wykonuje garść matematyki wektorowej i był nieco zaskoczony tym, że środowisko wykonawcze nie było wyraźnie lepsze na mojej czterordzeniowej maszynie. Co ciekawe, stwierdziłem, że środowisko wykonawcze jest najgorsze za pierwszym razem, gdy przechodzisz przez kolekcję i poprawia się z kolejnymi połączeniami. Czy są jakieś leniwe rzeczy w kolekcji równoległej, które to powodują, czy też robię to źle? Należy zauważyć, że pochodzę ze świata C++/C#, więc jest całkiem możliwe, że jakoś zawiodłem swoją konfigurację. Niezależnie od tego, oto moja konfiguracja:
InteliJ Scala Plugin
Scala 2.9.1.final
Windows 7 64 bit, procesor Quad-Core (brak hyperthreading)
import util.Random
// simple Vector3D class that has final x,y,z components a length, and a '-' function
class Vector3D(val x:Double, val y:Double, val z:Double)
{
def length = math.sqrt(x*x+y*y+z*z)
def -(rhs : Vector3D) = new Vector3D(x - rhs.x, y - rhs.y, z - rhs.z)
}
object MainClass {
def main(args : Array[String]) =
{
println("Available CPU's: " + Runtime.getRuntime.availableProcessors())
println("Parallelism Degree set to: " + collection.parallel.ForkJoinTasks.defaultForkJoinPool.getParallelism);
// my position
val myPos = new Vector3D(0,0,0);
val r = new Random(0);
// define a function nextRand that gets us a random between 0 and 100
def nextRand = r.nextDouble() * 100;
// make 10 million random targets
val targets = (0 until 10000000).map(_ => new Vector3D(nextRand, nextRand, nextRand)).toArray
// take the .par hit before we start profiling
val parTargets = targets.par
println("Created " + targets.length + " vectors")
// define a range function
val rangeFunc : (Vector3D => Double) = (targetPos) => (targetPos - myPos).length
// we'll select ones that are <50
val within50 : (Vector3D => Boolean) = (targetPos) => rangeFunc(targetPos) < 50
// time it sequentially
val startTime_sequential = System.currentTimeMillis()
val numTargetsInRange_sequential = targets.filter(within50)
val endTime_sequential = System.currentTimeMillis()
println("Sequential (ms): " + (endTime_sequential - startTime_sequential))
// do the parallel version 10 times
for(i <- 1 to 10)
{
val startTime_par = System.currentTimeMillis()
val numTargetsInRange_parallel = parTargets.filter(within50)
val endTime_par = System.currentTimeMillis()
val ms = endTime_par - startTime_par;
println("Iteration[" + i + "] Executed in " + ms + " ms")
}
}
}
Wyjście ten program to:
Available CPU's: 4
Parallelism Degree set to: 4
Created 10000000 vectors
Sequential (ms): 216
Iteration[1] Executed in 227 ms
Iteration[2] Executed in 253 ms
Iteration[3] Executed in 76 ms
Iteration[4] Executed in 78 ms
Iteration[5] Executed in 77 ms
Iteration[6] Executed in 80 ms
Iteration[7] Executed in 78 ms
Iteration[8] Executed in 78 ms
Iteration[9] Executed in 79 ms
Iteration[10] Executed in 82 ms
Co tu się dzieje? Pierwsze 2 razy, kiedy robimy filtr, jest wolniejsze, a potem przyspieszenie? Rozumiem, że z natury będzie to koszt uruchomienia równoległego, próbuję po prostu dowiedzieć się, gdzie ma sens wyrażenie paralelizmu w mojej aplikacji, a konkretnie chcę być w stanie pokazać zarządowi program, który działa 3-4 razy szybciej na pudełku z rdzeniem Quad. Czy to nie jest dobry problem?
Pomysły?
Jeśli szukasz pomysłu na zarządzanie sprzedażą, zajrzyj na http://scala-boss.heroku.com/#1 (użyj strzałek, aby zobaczyć kolejne slajdy). – huynhjl
Generalnie preferuj równoległe tablice do równoległych wektorów, przynajmniej do dodania konkatów do wektorów. – axel22
@huynhjl - Wiedziałem, że prezentacja jest coś warta, gdy zobaczyłem moje życie przedstawione w dwóch pierwszych komiksach. Dzięki! – fbl