As a scala newbie, I tried to write a method running over some 2-dimentional data. This method is called multiple times, so performance matters.
First I coded it with for comprehension
private def sumWithRange(xEnd: Int, yEnd: Int) = {
var sum = 0
for {
x <- 0 until xEnd
y <- 0 until yEnd
} sum += 1
sum
}
And then with while loop:
private def sumWithWhileLoop(xEnd: Int, yEnd: Int) = {
var sum = 0
var x = 0
while (x < xEnd) {
var y = 0
while (y < yEnd) {
sum += 1
y += 1
}
x += 1
}
sum
}
I was surprised how slowly the range-comprehension runs, so I wrote a ScalaMeter benchmark:
object TestDoubleLoopPerformance {
val standardConfig = config(
Key.exec.minWarmupRuns -> 5,
Key.exec.maxWarmupRuns -> 10,
Key.exec.benchRuns -> 10,
// Key.verbose -> true
) withWarmer(new Warmer.Default)
def main(args: Array[String]): Unit = {
val whileLoopTime = standardConfig measure {
for (iter <- 0 to 1000) {
sumWithWhileLoop(1000, 2000)
}
}
println(s"while loop time: $whileLoopTime")
val rangeLoopTime = standardConfig measure {
for (iter <- 0 to 1000) {
sumWithRange(1000, 2000)
}
}
println(s"range loop time: $rangeLoopTime")
}
}
The results:
while loop time: 0.3065984 ms
range loop time: 2585.5892847 ms
In addition, with verbose output, multiple GCs are reported for the for comprehension version.
I realize that for each x, new Range is created, which explains the sluggishness,
Is there a way to modify the for-comprehension version to run faster (on par with the while loop)?
// EDIT: I tried to desugar the for comprehension:
var sum = 0
(0 until xEnd).foreach(x => {
val yRange = 0 until yEnd
yRange.foreach(y => sum += 1)
})
and then pull up the yRange:
var sum = 0
val yRange = 0 until yEnd
(0 until xEnd).foreach(x => {
yRange.foreach(y => sum += 1)
})
It reports running time of circa 287ms - much better than before, but still not satisfactory