FastStringTable reallocates a `FastMutInt` for each `FastZString`
Performance analysis reported that we allocate 100_000 FastMutInt
s 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 FastMutInt
s, 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.