Scala:有没有办法提高这个卷积码的时序性能?
Scala: Is there a way to improve the timing performance of this convolution code?
大家好。
我一直在尝试改善这段代码在 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 专家可以给我一些建议吗?
在此先感谢大家的阅读。
一定要从删除别名参数开始 (: => ...
)。它们成为函数,在方法中的每次使用时都会对其进行评估,即使您只是将变量和文字传递给方法调用,仍然会有不小的开销。
尝试用 while
替换 for
循环。这将产生多少影响将取决于 Scala 版本以及您是否将优化标志传递给编译器。参见例如http://dynamicsofprogramming.blogspot.com/2017/01/performance-of-scala-for-loops.html 用于比较,包括 Scala 2.12。
您在衡量性能时也需要小心:使用Scalameter or JMH。
我建议的一个优化是用 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
语句并确保不会消耗额外的迭代。
大家好。 我一直在尝试改善这段代码在 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 专家可以给我一些建议吗? 在此先感谢大家的阅读。
一定要从删除别名参数开始 (: => ...
)。它们成为函数,在方法中的每次使用时都会对其进行评估,即使您只是将变量和文字传递给方法调用,仍然会有不小的开销。
尝试用 while
替换 for
循环。这将产生多少影响将取决于 Scala 版本以及您是否将优化标志传递给编译器。参见例如http://dynamicsofprogramming.blogspot.com/2017/01/performance-of-scala-for-loops.html 用于比较,包括 Scala 2.12。
您在衡量性能时也需要小心:使用Scalameter or JMH。
我建议的一个优化是用 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
语句并确保不会消耗额外的迭代。