当前位置: 首页 > 知识库问答 >
问题:

自动装箱比自定义包装类型的性能更好吗?

齐迪
2023-03-14

如果我有一个泛型参数,我通过模式匹配到基元(如 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 = ...

?

共有1个答案

南宫正阳
2023-03-14

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

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

人们可以假设第一个版本应该更快。使用“任何”的第三个版本将与第一个版本相同。

然而,使用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 

使用甲骨文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
  }
}

在以前的版本中,我使用了< code>Seq和方法< code>map和< code>sum,两个版本的性能相当,但它们只能实现大约4 ops/ms。

即使在期间使用Array也不会揭示真正的区别。

所以我认为这个(孤立的)API设计决策不会影响性能。

资源

    < li >如何将JMH与sbt一起用于Scala基准测试?(我使用了所选答案中描述的设置)

 类似资料:
  • 有以下代码: 它打印: 12 这个不能编译。为什么?

  • 问题内容: 通常,编译器会生成代码以执行装箱和拆箱。但是,如果不需要带框的值,编译器怎么办?(Oracle标准)编译器是否足够智能以优化它? 看一下这个方法: 唯一相关的信息是,因此将例如数组的每个值装箱将是无用的。像下面的代码: 编译器会实际插入用于对数组的每个值进行装箱的代码吗? 问题答案: 您的代码中没有自动装箱。实际上,鉴于: 虽然可以将an自动装箱到,但Java 不会 将an自动装箱到。

  • 可以用Java定义自定义自动装箱吗? 在键入函数的参数时,我需要自动将转换为我的类。对于两个参数,我最终编写了四个非常相似的方法: 所以我可以输入: 然而,我认为这对于10个参数(我想做的)变得难以管理;我想我需要写1024个类似的方法。 我正在考虑的另一个解决方案是编写一个方法,例如: 但是我可以键入数字和混合在一起作为参数吗?

  • 问题内容: 为什么第二段代码更快? 问题答案: 自动装箱使用,内部将Integer对象缓存为小整数(默认情况下为-128至127,但是最大值可以使用“ java.lang.Integer.IntegerCache.high”属性进行配置-请参见Integer.valueOf的源代码) ,因此与直接调用不同。因为在调用之前可以快速检查整数值的大小,所以直接调用要快一些(尽管如果您有很多小整数,它会使

  • 问题内容: 我在参考Java规范的缺点回答另一个问题时看到了这一点: 还有更多缺点,这是一个微妙的话题。查看该出: 这里将打印“ long”(我自己未检查),因为编译器选择加宽而不是自动装箱。使用自动装箱时要小心,否则请不要使用它! 我们确定这实际上是扩大而不是自动装箱的示例,还是完全是其他东西? 在我的初始扫描中,我将同意这样的说法,即基于声明为原始而非对象,输出将为“长” 。但是,如果您更改了

  • 本文向大家介绍自动装箱和拆箱?相关面试题,主要包含被问及自动装箱和拆箱?时的应答技巧和注意事项,需要的朋友参考一下 自动装箱是Java 编译器在基本数据类型和对应的对象包装类型之间做的一个转化。 比如:把int转化成 Integer,double转化成 Double,等等。反之就是自动拆箱。 原始类型: boolean,char,byte,short,int,long,float,double