FastStringTable reallocates a `FastMutInt` for each `FastZString`
Performance analysis reported that we allocate 100_000 FastMutInts on the agda code base in a ghc session:
key; total; count; max; avg
ghc-9.9-inplace:GHC.Data.FastMutInt:FastMutInt[ARR_WORDS]; 1847472; 115467; 16; 16.0
In FastStringTable, we count the number of z-encoded FastStrings
that exist in a GHC session.
We UNPACK the counters to not waste memory, but live retainer
analysis showed that we allocate a lot of FastMutInts, retained by
mkFastZString.
We lazily compute the FastZString, only incrementing the counter when the FastZString is
forced.
The function mkFastStringWith calls mkZFastString and boxes the
FastMutInt, leading to the following core:
mkFastStringWith
= \ mk_fs _ ->
= case stringTable of
{ FastStringTable _ n_zencs segments# _ ->
...
case ((mk_fs (I# ...) (FastMutInt n_zencs)) -- <-- this reallocates
`cast` <Co:2> :: ...)
...
Marking this field as NOUNPACK avoids this reboxing, eliminating the
allocation of a fresh FastMutInt on every FastString allocation.
Edited by Hannes Siebenhandl