HermitCrab allocation: FeatureStruct clone dominates; jemalloc not applicable
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 38/100
- issue の種類
- 機能追加
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- csharp
- 領域
- performance
調査の方向性
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.
索引モデルが issue の本文から書いたものです。
説明
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.
- 主要言語
- C#
- スター
- 29
- フォーク
- 16
- 平均マージ
- 4日 11時間
- マージ済み PR(30日)
- 14
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドなし
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
sillsdev/machine のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
HermitCrab: one partial rule switches off the final-template prune for the whole grammar; Mbugwe chaanjiyɛ never finishes対応中かも @johnml1135 が今日担当しました。 オープン
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
メンテナーはふだん 1 日以内に返信
-
Hermit Crab performance
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
メンテナーはふだん 1 日以内に返信
-
Hermit Crab performance
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
メンテナーはふだん 1 日以内に返信
sillsdev/machine の issue をすべて見る
似ている issue
-
bug emitter:client:csharp
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
microsoft/typespec#12572 · コメント 1 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
needs-area-label
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 2 日以内に返信
-
AWS Lambda: KeyNotFoundException drops HTTP-triggered transactions when allowAllHeaders is disabledオープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
newrelic/newrelic-dotnet-agent#3949 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
area-networking
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
dotnet/aspnetcore#70545 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 87/100
メンテナーはふだん 1 日以内に返信