HermitCrab allocation: FeatureStruct clone dominates; jemalloc not applicable
I maintainer di solito rispondono entro 1 giorno
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 38/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- csharp
- Ambito
- performance
Direzione di ricerca
The profiling points to FeatureStruct.Clone and VariableBindings cloning, but the issue does not name source files or tests. Start by locating FeatureStruct.Clone and the copy constructor, then trace callers that clone VariableBindings in Unify, Subsumes, and IsUnifiable. Compare structural sharing or copy-on-write and avoiding the copies map for structs without reentrancies; done means reduced cloning with measurements on a quiet machine using the same harness and workloads.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
HermitCrab allocation: FeatureStruct cloning is about 60% of parse bytes; allocator and GC-mode levers are dead ends
Claude-drafted.
About 60% of HermitCrab parse allocation bytes come from FeatureStruct clone machinery, and no allocator or GC-mode change removes that cost. The one host-side lever, DOTNET_GCgen0size=10000000 (hex, 256 MB), cuts gen0 collections about 10x for a noisy wall-time gain of roughly 6 to 14%. The next step is to reduce the cloning itself, not to tune the allocator.
What we know (numbers and method in the details):
- The clone machinery (the
copiesDictionary and its buckets,_definiteand VariableBindings clones, SymbolicFeatureValue) is the bulk of allocated bytes on Amharic. GC pause is 450-670 ms of a 2.4-3.1 s pass, 15-25% of wall time. - jemalloc via LD_PRELOAD and jemalloc.NET do not apply. Under 1% of allocation events are native, and the jemalloc timing matched a no-op GC control.
- Server GC, non-concurrent GC, TieredPGO=0 and TieredCompilation=0 gave no win. The last two were 32-35% slower on Amharic.
- The pooled
copiesscratch map cuts allocated bytes 10-17%, but wall time is mixed across grammars, so it is not proposed as a PR.
Next: reduce FeatureStruct cloning (structural sharing or copy-on-write, and no copies map for structs without reentrancies) and re-measure on a quiet machine with the same harness. Related open issues: #586 and #587.
Where the bytes go (GCAllocationTick sampling, Amharic 40 words, about 55k ticks)
- 13.1%
Entry[FeatureValue,FeatureValue][]and 12.1%Int32[](Dictionary buckets): the per-clonecopiesmap in the FeatureStruct copy constructor, plus the_definitemaps. - 12.4%
Entry[string,FeatureValue][]: VariableBindings clones in Unify, Subsumes and IsUnifiable. VariableBindings clones on every call. - 12.3%
Entry[Feature,FeatureValue][]:_definiteof each cloned FeatureStruct. - 11.2% SymbolicFeatureValue, 7.0% UlongSymbolicFeatureValueFlags, 3.4% Annotation, 2.8% FSA Register arrays.
- Net: about 60% of bytes are FeatureStruct clone machinery. FeatureStruct.Clone allocates a Dictionary, buckets and entries per clone even when the struct has no reentrancies.
Method and caveats
- Harness: a benchmark program in the machine worktree (branch
alloc-bench, built against origin/master 0cbf85f1, not pushed). It loads PanGlosssamples/datafor amharic, aweti and mbugwe (*-hc.xml), buildsMorpher(..., maxDegreeOfParallelism 1), and parses the first N words of the matching word list. - Per process: 1 warm pass and 2 timed passes; a new process per sample; configs interleaved per round; net10.0 Release.
- Each cell is n=5 to 15. "Median" is the mean of the two timed passes per process, then the median across processes.
- Machine load average ran 7.4 to 24.5 on 20 cores. Absolute times are not comparable with a quiet machine, and wall-time spread is 20-40%.
- Single thread only. The default multi-word parallel case under Server GC was not measured, and the
Morpherdefault parallelism and Server GC interplay was not studied. - The Sena word list in PanGloss
samples/dataparsed zero words in the harness. Not investigated.
Run 2: Amharic 40 words, 8 rounds, interleaved (most controlled)
| config | median ms | min-max | vs base | GC pause ms | gen0/1/2 GCs |
|---|---|---|---|---|---|
| DOTNET_GCgen0size=0x10000000 (256 MB) | 2668 | 2361-3152 | -13.5% | 450 | 21/2/0 |
| gen0size 256 MB + gcConcurrent=0 | 2708 | 2373-3478 | -12.2% | 569 | 21/0/0 |
| gen0size 1 GB | 2786 | 2358-3260 | -9.7% | 470 | 21/2/0 |
| GCgen0MaxBudget=1 GB (no-op control, no GC change) | 2862 | 2256-3741 | -7.2% | 650 | 229/90/9 |
| LD_PRELOAD jemalloc | 2864 | 2125-3354 | -7.2% | 636 | 228/80/8 |
| base | 3086 | 2497-4010 | 0 | 670 | 229/89/9 |
| gcConcurrent=0 | 3268 | 2326-4469 | +5.9% | 582 | 84/19/3 |
The no-op control (GCgen0MaxBudget, identical GC counts) reads -7.2%, the same as jemalloc, so the jemalloc result is noise. Allocated MB is identical in all configs (2745-2747).
Run 1: Amharic 60 words and Aweti 15 words, 5 rounds (medians vs base)
| config | Amharic | Aweti |
|---|---|---|
| jemalloc | -6.9% | -12.9% |
| gen0size 256 MB | -6.2% | -14.1% |
| gen0size 64 MB | +2.7% | +26.1% |
| gcServer=1 | +4.9% | -4.9% |
| gcConcurrent=0 | +9.2% | +8.4% |
| TieredPGO=0 | +32.0% | -3.9% |
| TieredCompilation=0 | +35.2% | +11.6% |
Base spreads: 2790-3838 ms (Amharic) and 706-1052 ms (Aweti). Effects under about 10% are not distinguishable from noise.
Why GC config is not a PR
GCgen0sizeis the only GC knob with a consistent direction: -6 to -14% in two batches, GC pause 670 to 450 ms, gen0 GCs 229 to 21.- It has no runtimeconfig name (environment variable only), so a library cannot set it, and a host's GC mode is the host's decision.
- The gain is within about 2x the noise floor.
- Proposed docs-only note for hosts: "DOTNET_GCgen0size=10000000 (hex, 256 MB) may cut HermitCrab gen0 collections 10x; measure first."
GCgen0MaxBudgetraised to 1 GB did nothing (equal to base within noise).
Pooled `copies` scratch map in FeatureStruct.Clone (code lever, not proposed as a PR)
The patch takes a thread-static dictionary out of its slot, clears it after each Clone, and is not a result cache. Bytes allocated per pass are deterministic: Amharic 2747 to 2292 MB (-17%), Aweti 493 to 442 MB (-10%).
Interleaved A/B, same build otherwise:
| workload | n | median ms base to pool | paired ratio (pool/base) | pool faster |
|---|---|---|---|---|
| Amharic 40 (run 3) | 10 | 2566 to 2362 (-8.0%) | 0.883 | 8/10 |
| Aweti 15 (run 3) | 10 | 793 to 760 (-4.2%) | 1.028 | 3/10 |
| Amharic 40 (run 4) | 15 | 2671 to 2457 (-8.0%) | 0.954 | 11/15 |
| Mbugwe 12 (run 4) | 15 | 1791 to 1741 (-2.8%) | 1.018 | 6/15 |
Byte cuts of 10-17% do not become a repeatable wall-time win. The Amharic gain is the only one at the 5% level, and it is smaller in paired ratio than in medians. Aweti and Mbugwe are flat to slightly slower on paired ratio. It is a candidate to revisit on a quiet machine with CPU counters.
jemalloc and jemalloc.NET
- jemalloc LD_PRELOAD: a full Aweti parse made 28,562 native mallocs (about 10 MB live) against 493 MB of managed allocation in one pass. The GC reserves through mmap, bypassing malloc. Native malloc is well under 1% of allocation events.
- jemalloc.NET (allisterb): last commit and release v0.2.4 on 2018-01-11, so unmaintained since 2018. It targets netcoreapp2.0 (C# 7.1), has no NuGet package, and ships Windows-oriented native binaries with no Linux binaries. It offers native buffers of unmanaged primitives only. HermitCrab's hot data are reference-type object graphs, so there is no site where a primitive buffer would replace the dominant allocations. Machine ships netstandard2.0 libraries, so the dependency is not acceptable.
- Lingua principale
- C#
- Stelle
- 29
- Fork
- 16
- Merge medio
- 4g 11h
- PR unite (30g)
- 14
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di sillsdev/machine
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
I maintainer di solito rispondono entro 1 giorno
-
HermitCrab: one partial rule switches off the final-template prune for the whole grammar; Mbugwe chaanjiyɛ never finishesForse già presa @johnml1135 l’ha presa oggi. Aperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
I maintainer di solito rispondono entro 1 giorno
-
Hermit Crab performance
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
I maintainer di solito rispondono entro 1 giorno
-
Hermit Crab performance
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di sillsdev/machine
Issue simili
-
bug emitter:client:csharp
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
microsoft/typespec#12572 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
needs-area-label
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 2 giorni
-
AWS Lambda: KeyNotFoundException drops HTTP-triggered transactions when allowAllHeaders is disabledApertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
newrelic/newrelic-dotnet-agent#3949 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
area-networking
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dotnet/aspnetcore#70545 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 87/100
I maintainer di solito rispondono entro 1 giorno