Scala:有没有办法改善这种卷积代码的计时性能?

时间:2018-11-07 16:39:16

标签: scala performance convolution timing

大家好。 我一直在尝试改善此代码在Scala中的执行时间:

def TwoDimensionalConvolution(Input: => Array[Int], Height: => Int, Width: => Int, Kernel: => Array[Int]): Array[Int] = {
  val R = (Kernel.length - 1) >> 1

  val result = Array.ofDim[Int](Input.length)
  for (i <- 0 until Height) {
    for (j <- 0 until Width) {
      var acc = 0
      for (k <- j - R to j + R)
        if (k >= 0 && k < Width)
          acc += Input(i * Width + k) * Kernel(k + R - j)
      result(j * Height + i) = acc >> 8
    }
  }

  val Output = Array.ofDim[Int](Input.length)
  for (i <- 0 until Width) {
    for (j <- 0 until Height) {
      var acc = 0
      for (k <- j - R to j + R)
        if (k >= 0 && k < Height)
          acc += result(i * Height + k) * Kernel(k + R - j)
      Output(j * Width + i) = acc >> 8
    }
  }
  Output
}

我的代码使用行和列的线性卷积执行二维卷积。请注意,输入数组不是二维的,但是访问它的方式是线性的。 k的for循环使您可以在每一行或每一列上进行迭代,而不必对边缘进行零填充。

我也尽力避免尽可能多地存储中间结果,但是如您所见,保存卷积行的result变量是执行列卷积所必需的。

我不认为自己是Scala的专家,尽管我有一些C / C ++的经验,但我无法进一步改善时序。

事实是,我对我所拥有的一些函数式编程知识一无所知。

一些Scala专家可以向我提出一些建议吗? 在此先感谢大家阅读。

2 个答案:

答案 0 :(得分:3)

我建议的一种优化是使用k优化循环。 你有:

for (k <- j - R to j + R)
    if (k >= 0 && k < Height)
      acc += result(i * Height + k) * Kernel(k + R - j)

但是,如果k is < 0 || k >= Height,则循环不执行任何操作,因此您不必要地进行迭代。

也许更改为:

for (k <- (j - R max 0) to (j + R min Height - 1))
     acc += result(i * Height + k) * Kernel(k + R - j)

这消除了if语句,并确保不消耗额外的迭代次数。

答案 1 :(得分:2)

绝对要从删除名称参数(: => ...)开始。它们成为函数,将在方法的每次使用中进行评估,即使您仅将变量和文字传递给方法调用,仍然会有不小的开销。

尝试将for循环替换为while。这将产生多少效果取决于Scala版本以及是否将优化标志传递给编译器。参见例如http://dynamicsofprogramming.blogspot.com/2017/01/performance-of-scala-for-loops.html用于比较,包括Scala 2.12。

在评估效果时,您还需要小心:使用ScalameterJMH