【问题标题】:Dot product in Scala without heap allocation没有堆分配的Scala中的点积
【发布时间】:2016-10-07 09:35:17
【问题描述】:

我有一个包含一些密集算术的 Scala 项目,它有时分配浮点数的速度比 GC 清理它们的速度要快。 (这与保留引用引起的内存泄漏无关,只是临时值的快速内存消耗。)我尝试使用原始类型的数组,并尽可能重用它们,但仍然有一些新的分配潜入。

让我困惑的一个片段,例如:

import org.specs2.mutable.Specification

class CalcTest extends Specification {

  def dot(a: Array[Float], b: Array[Float]): Float = {
    require(a.length == b.length, "array size mismatch")
    val n = a.length
    var sum: Float = 0f
    var i = 0
    while (i < n) {
      sum += a(i) * b(i)
      i += 1
    }
    sum
  }

  val vector = Array.tabulate(1000)(_.toFloat)

  "calculation" should {
    "use memory sparingly" >> {
      val before = Runtime.getRuntime().freeMemory()

      for (i <- 0 to 1000000)
        dot(vector, vector)

      val after = Runtime.getRuntime().freeMemory()
      (before - after) must be_<(1000L)  // actual result above 4M
    }
  }
}

我希望它只使用堆栈内存来计算点积,但显然它在堆上每次调用分配大约 4 个字节。这听起来可能不多,但它在我的代码中很快就加起来了。

我怀疑总和,但从字节码输出来看,它似乎在堆栈上:

    aload 1
    arraylength
    istore 3
    fconst_0
    fstore 4
    iconst_0
    istore 5
   l2
    iload 5
    iload 3
    if_icmpge l3
    fload 4
    aload 1
    iload 5
    faload
    aload 2
    iload 5
    faload
    fmul
    fadd
    fstore 4
    iload 5
    iconst_1
    iadd
    istore 5
    _goto l2
   l3
    fload 4
    freturn

是堆上的返回值吗?有没有办法完全避免这种开销?有没有更好的方法来调查和解决此类内存问题?

从我的项目的 visualVM 输出中,我只看到我分配了非常多的浮点数。很难跟踪像这样快速分配的小对象。它对于大对象和长时间拍摄的内存快照更有用。

更新:

我太专注于函数代码,在测试中错过了问题。如果我用while循环重写它,它会成功:

var i = 0
while (i < 1000000) {
  dot(vector, vector)
  i += 1
}

除了像这样的测试和使用 visualVM 内存快照之外,我仍然希望有更多关于调试此类问题的方法的想法。

【问题讨论】:

    标签: scala memory-management memory-leaks profiler


    【解决方案1】:

    范围实现

    for (i <- 0 to 1000000)
      dot(vector, vector)
    

    可能会使用一些内存,或者只是慢到让 JVM 在后台分配其他东西并破坏测试中使用的脆弱测量方法。

    例如,尝试将这些行修改为 while 循环。

    (本帖原版说for()等价于map(),这是错误的,这里等价于foreach(),因为它没有yield子句。)

    【讨论】:

    • 是的,我也刚刚注意到这一点。如果我用while循环重写测试,它会成功。
    • 这是不正确的,没有yieldfor 将转换为foreach,而不是map。我认为某处有一个编译器插件可以检测到这些琐碎的for“循环”并将它们转换为while循环。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-01
    • 1970-01-01
    • 2019-10-18
    • 1970-01-01
    • 2021-12-01
    相关资源
    最近更新 更多