Scala 相同运行时类但不同静态类型的对象的不同性能

Scala 相同运行时类但不同静态类型的对象的不同性能,scala,performance,scala-collections,Scala,Performance,Scala Collections,考虑以下jmh基准 @State(Scope.Benchmark) @BenchmarkMode(Array(Mode.Throughput)) class So59893913 { def seq(xs: Seq[Int]) = xs.sum def range(xs: Range) = xs.sum val xs = 1 until 100000000 @Benchmark def _seq = seq(xs) @Benchmark def _range = range

考虑以下jmh基准

@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.Throughput))
class So59893913 {
  def seq(xs: Seq[Int]) = xs.sum
  def range(xs: Range) = xs.sum

  val xs = 1 until 100000000
  @Benchmark def _seq = seq(xs)
  @Benchmark def _range = range(xs)
}
给定的
xs
引用了运行时类
Range的同一对象。Inclusive
作为参数传递给
seq
Range
方法,因此动态调度应该调用
sum
的相同实现,尽管方法参数的声明静态类型不同,为什么性能差异如此之大,如下所示

sbt“jmh:run-i10-wi5-f2-t1-prof gc bench.So59893913”


特别要注意的是,
gc.alloc.rate
指标之间的差异。

两件事正在发生

首先,当
xs
具有静态类型
Range
时,对
sum
的调用是单态方法调用(因为
sum
Range
中是最终的),JVM可以轻松地内联该方法并进一步优化它。当
xs
具有静态类型
Seq
时,它将成为一个无法内联和完全优化的巨型方法调用

第二个问题是,被调用的方法实际上并不相同。编译器在
范围内生成两个
求和
方法:

scala> :javap -p scala.collection.immutable.Range
Compiled from "Range.scala"
public abstract class scala.collection.immutable.Range extends scala.collection.immutable.AbstractSeq<java.lang.Object> implements scala.collection.immutable.IndexedSeq<java.lang.Object>, scala.collection.immutable.StrictOptimizedSeqOps<java.lang.Object, scala.collection.immutable.IndexedSeq, scala.collection.immutable.IndexedSeq<java.lang.Object>>, java.io.Serializable {
...
public final <B> int sum(scala.math.Numeric<B>);
...
public final java.lang.Object sum(scala.math.Numeric);
...
}
正如您所看到的,这个方法只调用另一个
sum
方法,并将
int
放入
java.lang.Integer


因此,在方法
seq
中,编译器只知道存在返回类型为
java.lang.Object
sum
方法,并调用该方法。它可能没有内联,它返回的
java.lang.Integer
必须再次取消绑定,以便
seq
可以返回
int
。在
range
中,编译器可以生成对“real”
sum
方法的调用,而无需对结果进行装箱和取消装箱。JVM在内联和优化代码方面也可以做得更好。

这是哪个Scala版本?
scalaVersion:=“2.13.1”
scala> :javap -p scala.collection.immutable.Range
Compiled from "Range.scala"
public abstract class scala.collection.immutable.Range extends scala.collection.immutable.AbstractSeq<java.lang.Object> implements scala.collection.immutable.IndexedSeq<java.lang.Object>, scala.collection.immutable.StrictOptimizedSeqOps<java.lang.Object, scala.collection.immutable.IndexedSeq, scala.collection.immutable.IndexedSeq<java.lang.Object>>, java.io.Serializable {
...
public final <B> int sum(scala.math.Numeric<B>);
...
public final java.lang.Object sum(scala.math.Numeric);
...
}
  public final java.lang.Object sum(scala.math.Numeric);
    Code:
       0: aload_0
       1: aload_1
       2: invokevirtual #898                // Method sum:(Lscala/math/Numeric;)I
       5: invokestatic  #893                // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
       8: areturn