大家好。 我一直在尝试改善此代码在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专家可以向我提出一些建议吗? 在此先感谢大家阅读。
答案 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。
在评估效果时,您还需要小心:使用Scalameter或JMH。