Skip to content

HermitCrab analysis: lean trace keeps shape text instead of Word objects #563

Open

Description

@johnml1135

TraceManager retains about 1 GB on the worst Mbugwe word because every trace node pins a Word or a deep clone of one. The smallest trigger is parsing Mbugwe yɔɔna with IsTracing = true: 1,031.95 MiB retained (peak 1,035 MiB) for 21,200 analysis events and 41,130 synthesis steps. Any CLI, test or API user who traces a heavy word can run out of memory, while consumers read only shape strings.

Problem (census, memory)

Study: C2-trace-memory.md in the round inputs. TraceManager appends one Trace node per callback and never removes one. Nodes pin a Word by live reference, or by Word.Clone() at six sites (TraceManager.cs:38, 49, 130, 136, 185, 202). Each Word carries its own shape, node feature structures and dictionaries. Measured retained trace: Mbugwe yɔɔna 1,031.95 MiB, kojeema 1,009 MiB, woocha 667 MiB; Aweti tsãnekozokwawut 652 MiB. Per counted step about 17 KB on yɔɔna, 3.4 KB to 36 KB across heavy words. A traced census run that pins no Words peaked at 203.31 MiB over all 40 Mbugwe words (an inference, not a like-for-like measurement). FieldWorks Try-a-Word uses its own FwXmlTraceManager, which keeps strings only, so FieldWorks is unaffected; this concerns the CLI, tests and API.

This is a memory track: steps are 0% by construction.

Proposed change

Add an opt-in TraceManager.RetainWords (default true, behaviour unchanged). When false, no callback stores a Word and no Clone() runs; each node stores the text the CLI would print (regex form for analysis node types, as ParseCommand.cs:78-98) in new read-only Trace.InputText and OutputText, computed at most once per frozen Word in an internal per-Word field that the copy constructor does not copy (no dictionary keyed on words). In full mode the text properties are filled lazily. The CLI sets RetainWords = false. XML docs state that Input and Output are null in lean mode.

Soundness

Does not touch the search: analysis events, synthesis steps and parses are identical by construction, verified by tests (same node types, sources, subrule indices, failure reasons and text node by node in both modes; parses equal with tracing off, full and lean; CLI output byte-identical on the test grammar).

Memory bound

Text per distinct traced Word (about 50-150 bytes) plus the node itself (about 120 bytes, estimated). Freed with the trace. Retained trace bytes est. -90 to -97% (a study estimate, unmeasured), so yɔɔna falls from about 1 GB to the order of tens of MB.

Measurement plan

Add a parse_product_trace_lean arm beside the existing product-trace arm in the census harness context mode. Per word report allocated MB, peak MB, retained trace MB, node count and parse count for both arms on Mbugwe (stride 40), Aweti (stride 2), Sena (stride 100), Amharic (limit 28), Indonesian (all 70) and conformance. Steps check: census counts identical with and without the change on Indonesian and Mbugwe yɔɔna. Stress fixture "Deep derivation": one unordered stratum, 10 optional derivational suffixes with short ambiguous allomorphs, 15 roots, 10 long words (6-9 suffixes) so the tree grows combinatorially.

Kill criterion

If lean mode cuts retained trace bytes on yɔɔna by under 50%, stop and report what still holds the memory (node count, Word.CurrentTrace links, text, other). Any change in parses or step counts: blocked.

Overlaps

Related

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    • Status
      🆕 New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions