UnitInfo's are not shared across multiple home units
## Summary
The `unitInfoMap` in `UnitState` is not shared across multiple home units.
This means, for every home unit, we store a lot of duplicate `UnitInfo`s. Moreover, each `UnitState` (one per home unit), retains a reference to the package databases it uses.
For `cabal`, that means we retain for each home unit the whole cabal package store. While `UnitInfo`s are fairly compact, the memory usage of `UnitInfo`s accumulates quickly in GHC sessions with many home units!
### Benchmark
We do a quick and dirty experiment to estimate how much memory this amounts to. We load Haskell Language Server into a single GHCi session:
```sh
$ git clone git@github.com:haskell/haskell-language-server.git
# One time using the global cabal package db which can contain a lot of packages
$ cabal build --only-dependencies all --disable-tests --disable-benchmarks
# one time with only exactly
$ cabal --store-dir store-test build --builddir dist-test --only-dependencies all --disable-tests --disable-benchmarks
# Now, let's get the raw ghc options for running GHCi in order to pass `+RTS -s`
# we do this using `cabal repl --enable-multi-repl all -v3 --keep-temp-files` for both configurations
# and copy pasting the GHCi invocations
```
My global package db:
```
> ghc-pkg-9.10.3 -f ~/.local/state/cabal/store/ghc-9.10.3-415c/package.db/ list | wc -l
6996
```
HLS in this configuration has 34 home units (`cabal target all --disable-tests --disable-benchmarks`)
Running GHCi with the global cabal package db:
```sh
$ /home/hugin/.ghcup/bin/ghc-9.14.1 --interactive '-package-env=-' -j32 -no-user-package-db -unit '...' .... -e ':q' +RTS -s -RTS
37,778,060,856 bytes allocated in the heap
10,058,666,824 bytes copied during GC
1,583,344,360 bytes maximum residency (13 sample(s))
41,891,096 bytes maximum slop
3954 MiB total memory in use (0 MiB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 1437 colls, 905 par 8.925s 3.403s 0.0024s 0.0529s
Gen 1 13 colls, 3 par 22.876s 2.091s 0.1608s 0.4330s
Parallel GC work balance: 52.90% (serial 0%, perfect 100%)
TASKS: 99 (1 bound, 98 peak workers (98 total), using -N32)
SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.001s ( 0.000s elapsed)
MUT time 24.135s ( 9.054s elapsed)
GC time 31.801s ( 5.494s elapsed)
EXIT time 0.228s ( 0.002s elapsed)
Total time 56.164s ( 14.550s elapsed)
Alloc rate 1,565,301,978 bytes per MUT second
Productivity 43.0% of total user, 62.2% of total elapsed
```
---
Running the same session with the local store:
```sh
$ /home/hugin/.ghcup/bin/ghc-9.14.1 --interactive '-package-env=-' -j32 -no-user-package-db -unit '...' .... -e ':q' +RTS -s -RTS
34,359,622,072 bytes allocated in the heap
8,139,204,128 bytes copied during GC
875,779,232 bytes maximum residency (13 sample(s))
21,769,056 bytes maximum slop
2235 MiB total memory in use (0 MiB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 2410 colls, 1394 par 8.277s 2.445s 0.0010s 0.0197s
Gen 1 13 colls, 6 par 16.169s 1.004s 0.0772s 0.1808s
Parallel GC work balance: 47.33% (serial 0%, perfect 100%)
TASKS: 99 (1 bound, 98 peak workers (98 total), using -N32)
SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
INIT time 0.001s ( 0.000s elapsed)
MUT time 21.008s ( 7.082s elapsed)
GC time 24.446s ( 3.448s elapsed)
EXIT time 0.008s ( 0.002s elapsed)
Total time 45.462s ( 10.533s elapsed)
Alloc rate 1,635,578,055 bytes per MUT second
Productivity 46.2% of total user, 67.2% of total elapsed
```
Note that these numbers are not quite consistent, measurements I took a week ago were far less dramatic (3.1 GB vs 2.2 GB), but the sentiment is consistent.
## Solution
Deduplicating `UnitInfo`s across home units sounds like it can be quite a big win for GHCi and other consumers of the GHC API.
## Environment
* GHC version used: GHC 9.10.3, but the experiments are repeatable with GHC 10.1 as well.
Optional:
* Operating System: Arch Linux
* System Architecture: x86
issue