Commit 33eeee5
Fix unbounded thunk accumulation in GenDict constants update
Every call sequence updates the constants dictionary with the lazy
Data.Map's `unionWith Set.union`, which allocates a suspended
`Set.union old new` per colliding key. Such a suspension is only forced
when the generator samples that key's AbiType (genWithDict), and only
the argument types of fuzzed functions are ever sampled. Since callseq
unconditionally inserts an AbiAddressType entry into the additions map
(even when no contract was created), any target ABI without an
`address`-typed parameter accumulates a never-forced chain of union
thunks: one per call sequence per worker, for the campaign lifetime.
On a minimal one-function contract this leaks ~0.4 B/call (~31 kB/s at
~75k calls/s), 100% of live-heap growth: the heap census shows a
single monotonically growing THUNK band, info-table profiling points at
the Map.unionWith specialization's collision branch, and retainer
profiling roots it at callseq's WorkerState.genDict. Note this is
distinct from the dictionary's known value growth -- it happens even
when no new constants are harvested at all.
Use Data.Map.Strict.unionWith so per-key sets are forced as they are
unioned. Measured on the reproducer: live-heap slope drops from
~31,010 B/s to ~3 B/s over 93M calls, with identical coverage and
corpus behavior.
Co-Authored-By: Claude <noreply@anthropic.com>1 parent 1b444b6 commit 33eeee5
1 file changed
Lines changed: 8 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
| |||
502 | 503 | | |
503 | 504 | | |
504 | 505 | | |
505 | | - | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
506 | 513 | | |
507 | 514 | | |
508 | 515 | | |
| |||
0 commit comments