3 ms·
What you're saying could be taken two ways. 1. That the time (including start-up time) for something on the JVM would be longer because the JVM needs to start
by mdasen 11y ago
What you're saying could be taken two ways.
1. That the time (including start-up time) for something on the JVM would be longer because the JVM needs to start up.
2. That Go producing a native binary is automatically faster at things.
In reference to the first, the benchmark timings are started when the software actually starts running.
In terms of the second, being a native binary doesn't mean faster. Go still has a runtime. It's just included in the binary.
Go's performance advantage is probably a few things. Go was made to spawn millions of goroutines and Go uses primitive types. I don't know all that much about Scala, but I believe it doesn't do primitive types which means that every one of those Int objects requires following a pointer to get to the value. That's overhead. EDIT: this looks like it's wrong as Scala has a bunch of pre-defined classes that extend AnyVal and correspond to primitive types in Java.
It also looks like the Scala code is passing back a Long even when the value is still small enough to fit in an Int. That's doubling the amount of memory when that memory only needs 64-bits near the end stages. By contrast, the Go code does `sum := 0` which I'm not actually sure how that's working. If I initialize a 32 bit int without specifying it, I see that I wrap around (http://play.golang.org/p/PiiMZmEzKp http://play.golang.org/p/PiiMZmEzKp). So, I'm not sure how initialising it to 0 and then adding to something over an int32 works. Maybe Go uses a different default int depending on platform?
Go's implementation is also simpler. In the Scala version, there's a receive method and the sub-actors just pass back when they're done potentially causing a bunch of switching. Go, on the other hand, creates all the goroutines and only when that's done starts receiving from them. In the Scala version, the program might think that there's value in switching from creating the sub-actors to receiving from them - and they should pass back very quickly. The implementation here in Go doesn't allow for that switching.
When dealing with something as synthetic as this, all of these little things matter.