If we want to compare apple to apples, we should use thread pool shared across Scala actors: http://stackoverflow.com/a/1597942/341181
Just add 7 lines of code and Scala should get at least 10x faster.
If we want to compare apple to apples, we should use thread pool shared across Scala actors: http://stackoverflow.com/a/1597942/341181
Just add 7 lines of code and Scala should get at least 10x faster.
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
object FutureGen {
def caller(num: Int, size: Int, div: Int): Future[Long] = {
if (size == 1) {
Future.successful(num.toLong)
} else {
val futures = for (i <- 0 until div) yield caller(num + i * (size / div), size / div, div)
Future.sequence(futures).map(_.sum)
}
}
}
object Root extends App {
// Warmup
Await.result(FutureGen.caller(0, 1000000, 10), Duration.Inf)
val startTime = System.currentTimeMillis()
val x: Long = Await.result(FutureGen.caller(0, 1000000, 10), Duration.Inf)
val diffMs = System.currentTimeMillis() - startTime
println(s"Result: $x in $diffMs ms.")
}
This would give Scala: 499ms and Go: 436ms which is pretty neat, since go actually has no type system. (And I'm using the global execution context, which could also be changed) Currently your akka version is slow, since it's creating a new class on every call, so you would have like a dozen more methods to dispatch than my version.This is a great example. I've done a lot of Scala and o found the original version very obvious to read, but only experience tells me why the compiler can't make it efficient. Your version is not really worse in any way, but I think it's fair to say that it shouldn't be obvious to everyone why one is suboptimal at first glance.
sum := 0
for i := 0; i < div; i++ {
sub_num := num + i * (size / div)
go skynet(rc, sub_num, size / div, div)
}
This is the go version and only the inside will run on a thread so I made a fair comparsion P.S: The go version will actually run single threaded when you are having GOMAXPROCS=1. if size == 1 {
c <- num
return
}"c <- num" is a blocking statement. Since c is an unbuffered channel, execution cannot proceed past the statement until a reader consumes a value. The reader in this case creates 10 goroutines executing "c <- num" and only then starts consuming values. If the statements you wrote were inlined execution would immediately halt in a deadlock.
That betrays the whole benchmark. The basic point of the benchmark is to answer the question, "How expensive is it to create and run 1,111,111 actors in various languages?" You can't make a manual optimization that causes only 111,111 actors to be created and then say that it's a better comparison.
And when you are clever you would be comparing monads, which actually a goroutine could be. and a future/promise, too.
Btw. Even a Future does more than a goroutine do. it won't crash your whole program when it fails, which a goroutine does.
Futures are not a concurrency mechanism precisely. They are a monadic data structure around possibly-concurrent execution. You can wrap a Future around concurrent execution by an Actor, or you can do what you have done and wrap a Future around a sequential recursive computation. As with the synchronous .NET core implementation of the benchmark, the performance is better due to not having the overhead of Actors or other concurrency. Like the synchronous .NET core implementation writing the computation this way is not a fair comparison to the other languages in the benchmark and the high performance does not indicate anything about the efficiency of the concurrency mechanisms available in Scala.
The point is exactly to compare different implementations of the same concept. That's always the point in performance comparisons!