如何解决为什么Scala中没有AnyVal和泛型的分配
我的基准测试结果出乎意料。
该基准测试的目的是说明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;
解决方法
我想我了解发生了什么。我在基准测试中犯了两个错误
-
Set
仅包含一个元素,因此与Set$Set1
而非{{ 1}}。因此,我已修复了设置以使用更长的Set的问题。 -
因此,将调用
contains
。它通过调用equals
将int装箱。但是,此方法在内部使用缓存。因此,因为整数在[-127,128]范围内,所以没有分配。
再说一次,这个免责声明确实有意义。
记住:以下数字仅为数据。为了获得可重用的见解, 您需要了解数字为何如此。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。