scala - 自动装箱是否比自定义包装器类型表现更好?

标签 scala generics autoboxing

如果我有一个通用参数,我通过与Int等基元的模式匹配来解析该参数,那么自动装箱是否比使用自定义包装类型更便宜?例如

def test[A](x: A): Int = x match {
  case i: Int => i
  case _ => -1
}

对比

case class NumChannels(value: Int)

def test[A](x: A): Int = x match {
  case n: NumChannels => n.value
  case _ => -1
}

第一种方法是否提供任何性能优势?如果该方法使用 Any 来代替,这种情况是否相同:

def test(x: Any): Int = ...

最佳答案

如果您查看 javap 的输出(仅不同的部分):

  • 使用Int的版本:
 10: invokestatic  #17                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
 13: istore_3      
 14: iload_3       
  • 使用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

人们可能会认为第一个版本应该更快。使用 Any 的第三个版本的结果与第一个版本相同。

然而,使用 JMH 的微基准测试没有显示出真正的差异:

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 

使用 Oracle JDK 1.8、Scala 2.10.3、Linux 32 位。


第一个基准:

@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
  }
}

第二个基准

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
  }
}

在之前的版本中,我使用了 Seq 以及方法 mapsum,两个版本的性能相当,但它们只达到了 4 左右操作/毫秒。

即使使用 Arraywhile 也没有揭示出真正的区别。

所以我想说这个(独立的)API 设计决策不会影响性能。


资源

关于scala - 自动装箱是否比自定义包装器类型表现更好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24799706/

相关文章:

function - Scala 方法 = trait { ... } 含义

java - 对泛型类和 ArrayLists 的困惑

java - 在Java中,将原始类型添​​加/减去/乘以/划分为装箱类型是否会创建装箱类型的新对象?

c# - 自动装箱和拆箱在 Java 和 C# 中的行为是否不同

java - 对象或原始类型

scala - 使用 onComplete 时限制 Scala Future block

java - dpkg : error processing package oracle-java8-installer (--configure):

scala - 我可以将中间结果存储在 Scala 大小写匹配中吗?

来自传播运算符的 typescript 联合类型

Java泛型类型的泛型类型