runST isn't free
While optimizing some code I discovered that
runST isn't free. I had a function on the form:
f ... = ...let x = runST in (f x)...
Manually transforming it into
f ... = runST (g ...) where g ... = do ... x <- ... g x ...
(The real example is at https://github.com/tibbe/unordered-containers/commit/58b7815a057b3575c58b746d5971d59d806b1333)
improved performance quite a bit on this, already highly tuned, function. Unfortunately, combining all the calls to
runST into one and pulling them "up" is not all good. The code is now less modular, has a somewhat over specified evaluation order, and generally looks more imperative.
The cause of the decrease in performance is that
runSTRep cannot be inlined, which causes allocation of closures inline in the code (where none should be necessary.)
The comment next to
runSTRep explains why it's implemented the way it is, but I wonder if matters could be improved by making it a primop. If we create a fresh state token every time, instead of reusing
realWorld#, it should be impossible for mutable structures to let-float and become CAFs (which is what
runSTRep tries to avoid.)