首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Array.tabulate中的双装箱

Array.tabulate中的双装箱
EN

Stack Overflow用户
提问于 2018-02-07 15:38:31
回答 1查看 151关注 0票数 0

我遇到了一个装箱问题,这会对我的Scala代码的性能产生负面影响。我提取了相关的代码,这仍然显示了问题,并增加了一些奇怪之处。我有一个2D双数组的如下表示,它允许我通过提供我的函数来对它执行转换:

代码语言:javascript
复制
case class Container(
  a: Array[Array[Double]] = Array.tabulate[Double](10000, 10000)((x,y) => x.toDouble * y)
) {
  def transformXY(f: (Double, Double, Double) => Double): Container = {
    Container(Array.tabulate[Double](a.length, a.length) { (x, y) =>
      f(x, y, a(x)(y))
    })
  }

  def transform(f: Double => Double): Container = {
    Container(Array.tabulate[Double](a.length, a.length) { (x, y) =>
      f(a(x)(y))
    })
  }
}

下面的代码为我再现了这个问题:

代码语言:javascript
复制
object Main extends App {

  def now = System.currentTimeMillis()

  val iters = 3

  def doTransformsXY() = {
    var t = Container()
    for (i <- 0 until iters) {
      val start = now
      t = t.transformXY { (x, y, h) =>
        h + math.sqrt(x * x + y * y)
      }
      println(s"transformXY: Duration ${now - start}")
    }
  }

  def doTransforms() = {
    var t = Container()
    for (i <- 0 until iters) {
      val start = now
      t = t.transform { h =>
        h + math.sqrt(h * h * h)
      }
      println(s"transform: Duration ${now - start}")
    }
  }

  if (true) { // Shows a lot of boxing if enabled
    doTransformsXY()
  }

  if (true) { // Shows a lot of boxing again - if enabled
    doTransformsXY()
  }

  if (true) { // Shows java8.JFunction...apply()
    doTransforms()
  }

  if (true) { // Shows java8.JFunction...apply() if doTransforms() is enabled
    doTransformsXY()
  }

}

当我运行这段代码并使用Java VisualVM对其进行示例时,我经历了以下情况:

  • doTransformsXY运行时,我看到在scala.runtime.BoxesRunTime.boxToDouble()上花费了很多时间
  • 一旦doTransforms运行,就没有更多的时间花在装箱上了,示例显示的是scala.runtime.java8.JFunction2$mcDII$sp.apply()
  • 我再次运行doTransformsXY,仍然没有显着的装箱,时间再次增长在scala.runtime.java8.JFunction2$mcDII$sp.apply()中。

这是与Scala2.12.4,Windows x64 jdk1.8.0_92

我的主要问题是关于装箱的问题,我在我的生产代码中也看到了这个问题:

  • 为什么Double装箱发生在Array.tabulate?我是否需要进行过程化(同时循环,手动Array创建)来避免这种情况?

我的第二个问题是:

  • 为什么在我调用transform变体之后就不再进行装箱了?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-02-07 17:33:25

为什么在我调用转换变体之后就不再进行拳击了?

我没有复制它。如果我仔细地暂停VM并检查JProfiler,它仍然会执行大量的装箱和双重分配。这正是我所期望的,我有一个解释。

查看标准库中的Function1Function2特性,我们可以看到@专用注释:

代码语言:javascript
复制
trait Function1[@specialized(Int, Long, Float, Double) -T1, @specialized(Unit, Boolean, Int, Float, Long, Double) +R]
trait Function2[@specialized(Int, Long, Double) -T1, @specialized(Int, Long, Double) -T2, @specialized(Unit, Boolean, Int, Float, Long, Double) +R]

Function3只是

代码语言:javascript
复制
trait Function3[-T1, -T2, -T3, +R]

@specialized使您可以避免使用原语对泛型进行装箱。但这是以编译器不得不生成额外的方法和类为代价的,因此超过某个阈值,它只会产生大量可笑的代码(如果不是完全崩溃的话)。因此,如果我的数学是正确的,Function有4(T1上的规范)x6(规范R) = 24份每个专门的方法和24个额外的类,除了apply和一个通用的特性。

哦,顺便说一下,这些方法都是用$mcJNI类型签名固定的。因此,以$mcDII结尾的方法是一个特殊的重载,它返回一个Double,并接受两个Int作为参数。这是您要传递给tabulate内部转换的函数类型,即这一部分

代码语言:javascript
复制
(x, y) => f(a(x)(y))

而对f的调用应该与$mcDD一起显示(返回一个Double并接受一个Double)。

然而,打电话

代码语言:javascript
复制
f(x, y, a(x)(y))

就会变成

代码语言:javascript
复制
unbox(f(box(x), box(y), box(a(x)(y))))

所以我对你的解释已经够麻烦了。是时候解决问题了。要使这两种方法的装箱达到等效的形状,请创建一个专门的接口:

代码语言:javascript
复制
trait DoubleFunction3 {
  def apply(a: Double, b: Double, c: Double): Double
}

并在transformXY中重写签名

代码语言:javascript
复制
def transformXY(f: DoubleFunction3): Container = //... same code

因为它是Scala 2.12,而且在这个特性中只有一个抽象方法,所以仍然可以传递lambdas,所以下面的代码:

代码语言:javascript
复制
  t = t.transformXY { (x, y, h) =>
    h + math.sqrt(x * x + y * y)
  }

不需要改变。

现在您可能会注意到,这并不能完全消除装箱,因为tabulate也会导致这种情况。这是一维tabulate的定义。

代码语言:javascript
复制
  def tabulate[T: ClassTag](n: Int)(f: Int => T): Array[T] = {
    val b = newBuilder[T]
    b.sizeHint(n)
    var i = 0
    while (i < n) {
      b += f(i)
      i += 1
    }
    b.result()
  }

请注意,它与泛型Builder[T]一起工作,调用方法+=(elem: T)Builder本身并不是专门化的,因此它在创建数组时会执行浪费的装箱/取消装箱操作。您的解决方法是编写一个直接使用Double而不是T的版本,用于您需要的维度。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48667703

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档