Add bounded deterministic parallel translation-unit indexing #54
Labels
No labels
area/cli
area/core
area/database
area/indexing
area/query
type/epic
type/feature
type/foundation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
JayKobe6k/ClariTree#54
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem and impact
ClariTree processes translation units serially even when multiple CPUs are available. On the pinned 1,103-TU
melee-event-matchworkload, the reported four-CPU cgroup consumed about 0.905 effective cores over the whole failed service while parsing alone took 1,372.076 seconds. Source inspection and focused parse-only windows show that ClariTree's outer schedule leaves CPU capacity idle. They do not show that scheduling is the only constraint: per-TU fact volume is also a strong intrinsic cost driver.This issue is separate from full-project memory bounding in #53: concurrent native ASTs can increase peak memory, so parallelism must be explicitly bounded and validated against that work. The coordinator and one-worker compatibility path are independently actionable, but #53 should establish headroom before multiple workers become the default.
Environment and reproduction
5d04beabeb4bcd00d27e69dd318e5834b097cf30melee-event-matchcommit6b6f8a8a987c2e871b8b96baa863aaa99e406f33compile_commands.jsonSHA-256:0c025066b2f5ed7940a668d4fc97b912d8eb5d467fb7bb33ba0e72ab7d50ea16AllowedCPUs=0-3,CPUQuota=400%,taskset -c 0-3),MemoryMax=32G,MemorySwapMax=0, andRuntimeMaxSec=7200The sanitized retained full-run log independently verifies the parser output, OOM result, runtime, CPU time, memory peak, and zero cgroup swap, but it does not encode the transient-unit CPU properties. The CPU envelope comes from the reproduction record.
Run the ignored
profile_lineagerelease benchmark against all 1,103 ordered commands. For focused parse-only reproduction, run 20-unit windows with an offset, per-unit timing, and parse-only exit. These diagnostic controls are currently local instrumentation rather than committed public options.Baseline and profiling evidence
Full run:
Five isolated 20-TU parse-only windows show that the sampled later regions emit more parser facts than the first window while every window remains essentially single-core:
The per-TU timer is read before computing and printing that TU's fact-count line. The window-level parse and task-clock totals include the diagnostic counting and stderr output between units. Use these windows for within-method distribution and correlation evidence, not as production parser-throughput baselines.
Across all 100 sampled units, integer-millisecond elapsed time has Pearson correlation
r=0.938with the sum of the 21 emitted parser cardinalities,r=0.948with references, andr=0.890with macro expansions. The largest sampled TU by aggregate cardinality, ordinal 1005 (src/melee/mp/mplib.c), took 2.529 s and emitted 194,922 reported facts. This supports fact volume as a major intrinsic cost driver. Together with the source's serial loop, the near equality of task-clock and wall time shows that the windows do not overlap ClariTree translation-unit requests; libclang may still use incidental helper threads inside one request.The full-run log SHA-256 is
aedc41d38b8999e04a36928d54c1ece7cd672419359e595d19abba742c07218a. The sampled-window log SHA-256 is9b059123bddcbc21afddb99d4420c01e36fe5808e43450e08efae5ae3c0c1049. These are ignored local artifacts; the sanitized aggregate evidence is reproduced above.For Amdahl context only, the delivered first-200 run in closed issue #52 spent 43.071 s parsing and 25.489 s in semantic analysis. Holding analysis fixed and assuming perfect four-way parser scaling gives 43.071 / 4 + 25.489 = 36.257 s versus the measured 68.560-second parse-plus-analysis phase sum, a maximum reduction of 47.117%. This excludes persistence and coordination costs and is an upper bound, not an expected end-to-end gain.
Root cause
index_compilation_planuses one synchronousforloop and oneSourceIndexPortcall at a time.SourceIndexPortexposes only a single-TU synchronous operation.LibclangIndexeris deliberately single-thread-confined and neitherSendnorSync; the pinnedclangwrapper permits only one runtime instance per process.LibclangIndexer::indexsynchronously parses, collects the complete AST, performs all lowering passes, collects preprocessing/diagnostics/dependencies, and validates owned IR before returning.The serial schedule is source-verified. Libclang may use incidental internal helper threads, but ClariTree never overlaps two translation-unit requests.
Proposed implementation
Introduce bounded, deterministic process-level translation-unit indexing. Keep scheduling policy and plan-order semantics in the shared application service behind typed ports; keep process launch, executable discovery, IPC, and libclang ownership in a dedicated adapter so CLI, HTTP, MCP, and GUI frontends remain logic-free:
LibclangIndexeras the one-worker implementation and test oracle.LibclangIndexerruntime and receiving typed indexing requests.Alternatives considered
LibclangIndexeracross a thread pool: incompatible with its non-Send/non-Syncprocess-global runtime boundary.unsafeto force thread sharing: violates the wrapper's invariants and project safety rules.clangwrapper with broad direct FFI solely for parallelism: much larger safety and maintenance scope; consider only if a narrow, proven safe runtime design exists.Related parser work #44 removed cursor-map CPU overhead but intentionally did not solve process-level parallel scheduling. Update preflight work #42 removed one full parsing pass but leaves the production indexing pass serial.
Expected benefit, confidence, and risks
Acceptance criteria
IndexedTranslationUnitoutputs, diagnostics, dependencies, anchors, and order are exactly equal to the serial oracle; count-only parity is insufficient.Rollback
Set the configured worker count to one, retaining the existing serial path. If exact parity, process cleanup, memory bounds, or the measured scaling target fails, reject the concurrent implementation and preserve the profiling evidence.