Recently, I came across unexpected code optimization and wanted to verify that my interpretation of what I was observing was correct. The following is a simplified example of the situation:
let demo = let swap fst snd i = if i = fst then snd else if i = snd then fst else i [ for i in 1 .. 10000 -> swap 1 ii ] let demo2 = let swap (fst: int) snd i = if i = fst then snd else if i = snd then fst else i [ for i in 1 .. 10000 -> swap 1 ii ]
The only difference between the two blocks of code is that in the second case, I explicitly declare the swap arguments as integers. However, when I run 2 fragments in fsi C # time, I get:
Case 1 Real: 00: 00: 00.011, CPU: 00: 00: 00.000, GC gen0: 0, gen1: 0, gen2: 0
Case 2: 00: 00: 00.004, CPU: 00: 00: 00.015, GC gen0: 0, gen1: 0, gen2: 0
i.e. the second snapshot starts 3 times faster than the first. The absolute difference in performance here is obviously not a problem, but if I used the swap function many times, it would accumulate.
My assumption is that the reason for the performance hit is that in the first case, swap is generic and โrequires equalityโ, and checks to see if it supports int, and the second not to check. Is this the reason this is happening, or am I missing something else? And more generally, should automatic generalization be considered a double-edged sword, that is, an amazing feature that can have unexpected performance implications?
source share