自动装箱的性能是否优于自定义包装类型?

时间:2014-07-17 09:28:04

标签: scala generics autoboxing

如果我有一个通用参数,我通过模式匹配解析到Int之类的原语,自动装箱比使用自定义包装类型便宜吗? E.g。

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 = ...

1 个答案:

答案 0 :(得分:2)

如果你看一下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的第3个版本与第一个版本相同。

然而,使用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设计决定不会影响性能。


资源