I have two almost identical pieces of code. One is running Scala on JVM, second is running Javascript. Both perform lots of atan
and asin
calls (this is extracted from the real application performing quaternion to Euler angles conversion). The Javascript implementations runs an order of magnitude faster.
The JS version takes about 1 000 ms on my machine. The Scala code takes about 10 000 ms when running on JVM, but when compiled using Scala.js it is again running for about 1000 ms (see ScalaFiddle).
What is the reason for such huge performance difference? What changes would I have to implement for the JVM code run this fast?
var size = 100000;
var input = new Array(size);
function fromQuaternion(a, b, c) {
return Math.asin(a) + Math.atan2(a, b) + Math.atan2(b, c);
}
function convert() {
var sum = 0;
for (var i = 0; i < size * 3; i += 3) {
sum += fromQuaternion(input[i], input[i+1], input[i+2]);
}
return sum;
}
for (var i = 0; i < size * 3; i += 3) {
input[i + 0] = Math.random();
input[i + 1] = Math.random();
input[i + 2] = Math.random();
}
var total = 0;
for (var i = 0; i < 10; i++) total += convert();
var start = Date.now();
for (var i = 0; i < 100; i++) total += convert();
var end = Date.now();
console.log("Duration " + (end - start));
console.log("Result " + total);
document.write("Duration " + (end - start));
val input = Array.fill(100000) {
val x = util.Random.nextDouble()
val y = util.Random.nextDouble()
val z = util.Random.nextDouble()
(x, y, z)
}
def fromQuaternion(a: Double, b: Double, c: Double): Double = {
Math.asin(a) + Math.atan2(a, b) + Math.atan2(b, c)
}
def convert = {
input.foldLeft(0.0) { (sum, q) =>
sum + fromQuaternion(q._1, q._2, q._3)
}
}
// warmup
var sum = 0.0
for (_ <- 0 until 10) sum += convert
val start = System.currentTimeMillis()
for (_ <- 0 until 100) sum += convert
val end = System.currentTimeMillis()
println(s"Duration ${end - start} ms")
println(f"Sum $sum%f")
When I measure asin
and atan2
separately (with fromQuaternion
containing only a single asin
or a single atan2
), I get following results:
- JS
atan2
: 453 ms JS
asin
230 msJava Math
atan2
1000 msJava Math
asin
3800 msApache FastMath
atan2
1020 ms- Apache FastMath
asin
1400 ms
I have tested Apache FastMath as well. While its asin
is a bit faster, its performance is still way behind the one seen in the browser.
My measurements are done with Oracle Java 8 SDK 1.8.0.161 (JVM) and Chrome 78.0.3904.108 (Browser), both running on x64 Windows 10 running Intel Core i7 @ 3.2 GHz with 12 GB RAM.