我有两个案例类别:addSmall
和addBig
。addSmall
仅包含一个字段。addBig
包含多个字段。
case class AddSmall(set: Set[Int] = Set.empty[Int]) {
def add(e: Int) = copy(set + e)
}
case class AddBig(set: Set[Int] = Set.empty[Int]) extends Foo {
def add(e: Int) = copy(set + e)
}
trait Foo {
val a = "a"; val b = "b"; val c = "c"; val d = "d"; val e = "e"
val f = "f"; val g = "g"; val h = "h"; val i = "i"; val j = "j"
val k = "k"; val l = "l"; val m = "m"; val n = "n"; val o = "o"
val p = "p"; val q = "q"; val r = "r"; val s = "s"; val t = "t"
}
使用JMH的快速基准测试表明,即使我仅更改一个字段,复制addBig
对象的方式也更为复杂。
import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
@State(Scope.Benchmark)
class AddState {
var elem: Int = _
var addSmall: AddSmall = _
var addBig: AddBig = _
@Setup(Level.Trial)
def setup(): Unit = {
addSmall = AddSmall()
addBig = AddBig()
elem = 1
}
}
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Array(Mode.Throughput))
class SetBenchmark {
@Benchmark
def addSmall(state: AddState): AddSmall = {
state.addSmall.add(state.elem)
}
@Benchmark
def addBig(state: AddState): AddBig = {
state.addBig.add(state.elem)
}
}
结果显示,复制addBig
的速度比复制addSmall
的速度慢10倍以上!
> jmh:run -i 5 -wi 5 -f1 -t1
[info] Benchmark Mode Cnt Score Error Units
[info] LocalBenchmarks.Set.SetBenchmark.addBig thrpt 5 10732.569 ± 349.577 ops/ms
[info] LocalBenchmarks.Set.SetBenchmark.addSmall thrpt 5 126711.722 ± 10538.611 ops/ms
为什么复制对象的addBig
要慢得多?据我了解结构共享,由于所有字段都是不可变的,因此复制对象应该非常有效,因为它只需要存储更改(“增量”)(在这种情况下仅是集合s
),因此应该给定与addSmall
相同的性能。
我猜,这是因为AddBig
类扩展了Foo
特性,该特性具有所有这些String
字段-a
至t
。看起来,在结果对象中,如果将它们与Java进行比较,它们将被声明为常规字段,而不是static
字段,因此为该对象分配内存可能是复制性能降低的根本原因。希望这会有所帮助!