Scala Code Optimization

One question I’ve always had about programming in Scala was how much influence programming in idiomatic Scala really changes things. To partly answer this a small code example that does a moment distribution was created.

The last version of the joist program used the Three Moment Equation, a matrix method of finding the panel moments. Two of the inputs needed for this to work are the area under the moment for each individual panel and the centre of gravity of each of these moments. I’ve decided to change to the classic moment distribution, which uses the fixed end moments of the load in each panel and then a moment distribution to get the results. The Three Moment Equation uses formulas that were given to me; I have no proof or reference to indicate the formulas are correct. I could do the calculus to check them but my calc is so rusty as to be dangerous; on the other hand I can find the fixed moment formulas from a number of reliable sources.

When creating the new moment functions, initially code the code looked much like typical Java code:

var j: Int = 0 
val a: Array[Double] = new Array[Double](size)
for (i <- 0 to panels.length) {
    if (i == 0) a(i) = 1.0
    else if (i > 0 && i < panels.length) {
        a(i + j) = ...
        a(i + j + 1) = ...
        j = j + 1
        }
    else a(i + j) = 1.0
}

Note the creation of the variable j and following use in the loop. This isn’t the correct way to write Scala.

A more Scala like way to write this would be:

val size: Int = panels.length
val a: Array[Double] = new Array[Double](size)
for (i <- 0 to size) {
    if (i == 0) a(i) = 1.0 
    else if (i > 0 && i < size - 1) {
        if (i , 2 == 1) a(i) = ...
        else a(i) = ...
    }
    else a(size - 1) = 1.0
}

As you can see the variable j has been removed, there is less array manipulation and more calculations done in the loop to get the values out.

This sort optimization was done in a number of places. One might even go as far as creating tail recursive functions but that remains a challenge for another day. To test the optimizations the Canadian Institute of Steel Construction sample joist was run through the algorithm, 1000 times in 10 different sessions.

The results are:

Non-Optimized (seconds) Optimized (seconds)
8.534 12.554
11.989 11.884
12.261 11.712
11.817 8.436
11.378 11.553
11.767 12.877
11.692 7.203
11.852 9.956
11.552 12.109
12.159 10.880
Average 11.500 Average 10.916

When it comes to speed the optimizations have improved the speed for these runs by 0.58 of a second. Hardly noticeable but in other use cases this could make a considerable difference.

What about memory?

The whole project started at 36906 bytes and was reduced to 36651 bytes, a saving of 255 bytes. Not much, but a good trend.

If you have a method, or class, that gets used extensively in your Scala program it will be helped by doing optimizations to make it more idiomatic.