If you are look at the output of javap
(only the parts which differ):
10: invokestatic #17 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
13: istore_3
14: iload_3
- The version using
NumChannels
:
10: checkcast #12 // class app/benchmark/scala/benchmark3b/NumChannels
13: astore_3
14: aload_3
15: invokevirtual #16 // Method app/benchmark/scala/benchmark3b/NumChannels.value:()I
One could assume that the first version should be faster. The 3rd version using Any
results in the same as the first version.
Yet a micro benchmark using JMH shows no real difference:
Benchmark Mode Samples Mean Mean error Units
a.b.s.benchmark3a.Benchmark3a.run thrpt 5 42,352 0,480 ops/ms
a.b.s.benchmark3b.Benchmark3b.run thrpt 5 42,793 1,439 ops/ms
Using Oracle JDK 1.8, Scala 2.10.3, Linux 32-Bit.
1st benchmark:
@State(Scope.Benchmark)
object BenchmarkState {
final val n = 10000
val input =
Array.range(0, n).map {
n =>
if (n % 2 == 0) {
n
} else {
"" + n
}
}
}
class Benchmark3a {
def test[A](x: A): Int = x match {
case i: Int => i
case _ => -1
}
@GenerateMicroBenchmark
def run() = {
var sum = 0
var i = 0
while (i < BenchmarkState.n) {
sum += test(BenchmarkState.input(i))
i +=1
}
sum
}
}
2nd benchmark
case class NumChannels(value: Int)
@State(Scope.Benchmark)
object BenchmarkState {
final val n = 10000
val input =
Array.range(0, n).map {
n =>
if (n % 2 == 0) {
NumChannels(n)
} else {
"" + n
}
}
}
class Benchmark3b {
def test[A](x: A): Int = x match {
case n: NumChannels => n.value
case _ => -1
}
@GenerateMicroBenchmark
def run() = {
var sum = 0
var i = 0
while (i < BenchmarkState.n) {
sum += test(BenchmarkState.input(i))
i +=1
}
sum
}
}
In previous versions I used Seq
and methods map
and sum
, and both versions perform equally as well, but they only achieve around 4 ops/ms.
Even using Array
and while
does not reveal a real difference.
So I would say that this (isolated) API design decision won't affect performance.
Resources