问题描述
我的基准测试结果出乎意料。
该基准测试的目的是说明Scala AnyVal在泛型中的表现不佳。我创建了一个类型AnyValId
并扩展了AnyVal
。
预期结果是在调用通用方法contains
或identity0
时使用gc分析器查看分配情况。
您能帮我弄清楚这里发生了什么吗?
这是板凳
import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 1,jvmArgsAppend = Array("-Djmh.stack.lines=3"))
@Threads(1)
@Warmup(iterations = 3,time = 1,timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 3,timeUnit = TimeUnit.SECONDS)
class MapBench {
var set: Set[AnyValId] = _
var id: AnyValId = _
@Setup
def setup() = {
set = Set(AnyValId(1))
id = AnyValId(1)
}
@Benchmark
def _idWith(): AnyValId = identity0(id)
@Benchmark
def contains(): Boolean =
set.contains(id)
def identity0[T](t: T) = t
}
case class AnyValId(i: Int) extends AnyVal
这是结果
[info] Benchmark Mode Cnt Score Error Units
[info] MapBench._idWith avgt 3 2,128 ± 2,015 ns/op
[info] MapBench._idWith:·gc.alloc.rate avgt 3 ≈ 10⁻⁴ MB/sec
[info] MapBench._idWith:·gc.alloc.rate.norm avgt 3 ≈ 10⁻⁶ B/op
[info] MapBench._idWith:·gc.count avgt 3 ≈ 0 counts
[info] MapBench.contains avgt 3 3,985 ± 0,668 ns/op
[info] MapBench.contains:·gc.alloc.rate avgt 3 ≈ 10⁻⁴ MB/sec
[info] MapBench.contains:·gc.alloc.rate.norm avgt 3 ≈ 10⁻⁶ B/op
[info] MapBench.contains:·gc.count avgt 3 ≈ 0 counts
通过REPL中的进一步调查进行更新
scala> case class Id(i: Int)
class Id
scala> Set(Id(1))
val res3: scala.collection.immutable.Set[Id] = Set(Id(1))
scala> def f = res3.contains(Id(2))
def f: Boolean
:javap f
这是字节码,因为包含是在Id
上参数化的,似乎需要Id
的实例才能调用它。因此,new
指令以字节码表示。 因此,REPL和JMH基准测试中的行为似乎有所不同。
public boolean f();
descriptor: ()Z
flags: (0x0001) ACC_PUBLIC
Code:
stack=5,locals=1,args_size=1
0: aload_0
1: invokevirtual #29 // Method $line32$$read$$iw$$$outer:()L$line32/$read;
4: invokevirtual #33 // Method $line32/$read.$line30$read:()L$line30/$read;
7: invokevirtual #36 // Method $line30/$read.$iw:()L$line30/$read$$iw;
10: invokevirtual #40 // Method $line30/$read$$iw.res3:()Lscala/collection/immutable/Set;
13: new #14 // class $line29/$read$$iw$Id
16: dup
17: getstatic #46 // Field $line29/$read$.MODULE$:L$line29/$read$;
20: invokevirtual #50 // Method $line29/$read$.INSTANCE:()L$line29/$read;
23: invokevirtual #53 // Method $line29/$read.$iw:()L$line29/$read$$iw;
26: iconst_2
27: invokespecial #57 // Method $line29/$read$$iw$Id."<init>":(L$line29/$read$$iw;I)V
30: invokeinterface #63,2 // InterfaceMethod scala/collection/immutable/Set.contains:(Ljava/lang/Object;)Z
35: ireturn
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 36 0 this L$line32/$read$$iw;