What used to be good idea, may become a subpar approach soon.
Understanding hardware
Tenchiques used by hardware are tricky and change fast, but general ideas are simple and stay the same:
temporal bet;
spacial bet;
pattern bet.
Temporal bet
If you've used a resource, you'll use it again soon.
Temporal bet: illustration
first read of an integer from memory takes ~100ns;
second read of same integer that follows first one will take <1ns.
Spacial bet
If you've read some data, you'll soon read more data close to it.
Spacial bet: illustration
val a: Array[Array[Int]] = ...
val i = 0while (i < n) {
val j = 0while (j < n) {
if(fast) {
a[i][j] += a[i][j]
} else { // slow
a[j][i] += a[j][i]
}
j += 1
}
i += 1
}
Pattern bet
Behaviour of your program has simple repeating patterns.
Pattern bet: illustration
val a: Array[Int] = Array.fill(1000000)(scala.util.Random.nextInt)
// a = a.sorted // speeds up the loop below by 6xval i = 0while (i < n) {
if (a[i] > 0)
a[i] += 1
i += 1
}
Made a change and your app became faster?
Be aware of layout changes:
layout of code can impact performance by ~5%.
even size of starting environment can! Includes folder name!
See Mytkowicz, Todd, et al. "Producing wrong data without doing anything obviously wrong!."
Advice
getting performance is hard, needs a lot of exploration;
exploration needs to be repeated - old recipes don't age well;
loosing performance is easy, seemingly small changes can kill it;
tracking performance from start is crucial;
measure on an isolated machine with the same environment for all runs.