Repository navigation
perf(indexing): faster cold index that yields to foreground work - #78
Merged
Merged
Conversation
The first index after install or restart parsed every trace on one core (~50s here) and then spent ~17s CPU on matched-pace statistics. Cold summaries now fill from a spawn-context worker pool that is capped at half the CPUs (two on battery), follows measured idle CPU with AIMD, and moves workers to macOS Utility QoS when the machine saturates so foreground work is not slowed. Any pool failure falls back to the unchanged sequential path. Pace matching precomputes per-turn features with identical float operations and random draws, so results stay bit-identical. Co-Authored-By: Claude Code <noreply@anthropic.com>
Idle pool workers never see EOF when the server is killed, so they outlived it and, on Windows, pinned the runtime directory a reinstall moves. Workers now watch the parent's sentinel and run from the temp directory. Linux ignores peripheral batteries and idle USB-C ports, macOS keeps any low-power flag, malformed probe readings degrade to unknown, and the in-process OpenCode exclusion is an adapter capability. Co-Authored-By: Claude Code <noreply@anthropic.com>
…nity Worker processes on Windows (hidden-console inheritance, reinstall moves while workers run, parent-exit handles) are unverified on a real host, so a new PlatformCapabilities.parallel_index keeps Windows on the existing sequential path and drops the untested ctypes probes. Windows still gets the matched-pace speedup. Linux sizes the pool from sched_getaffinity so containers and pinned services are not oversubscribed, and the pool is skipped on Pythons without multiprocessing.parent_process. Co-Authored-By: Claude Code <noreply@anthropic.com>
…s verified Linux enablement rested on code reading alone. Nice applies only within a scheduler group and CPU quotas are invisible to the idle-CPU probe, so Linux now keeps the sequential cold index like Windows until worker processes are proven on a Linux host; its probes stay for that. Tests exercise the parallel path on any host and assert per-platform enablement. Co-Authored-By: Claude Code <noreply@anthropic.com>
Each worker can peak at several hundred MB on a large trace, and the idle-CPU throttle cannot see swapping, so six workers on an 8 GB Mac could push it into swap. The pool now allows one worker per 4 GiB of physical memory (two when unknown), and the kernel memory-pressure level caps the in-flight limit at half the pool on warn and one worker on critical. Spawn failure and a killed worker are covered by tests that fall back to identical sequential rows. Co-Authored-By: Claude Code <noreply@anthropic.com>
A worker keeps its peak memory (about 500 MB after the largest trace) until it exits, and neither gc nor allocator pressure relief returns it, so lowering the dispatch limit alone did not relieve memory. Pressure now drains in-flight work and restarts the pool at the capped size; a failed restart hands the rest to the sequential path. Measured: forcing critical mid-build dropped worker memory from about 1.5 GB to one worker. Co-Authored-By: Claude Code <noreply@anthropic.com>
Shrinking used the combined limit, so a CPU dip during warn pressure could restart the pool at one worker for the rest of the build. The pool now shrinks to the memory cap (half on warn, one on critical) while the CPU limit still governs dispatch. Adds an end-to-end test that a failed restart falls back to identical sequential rows. Co-Authored-By: Claude Code <noreply@anthropic.com>
The Swift build needs only the staged source and toolchain, yet it waited for indexing to finish. It now starts in the background right after the server LaunchAgent, and the installer waits only for what is left of it before the same output replay, failure check, smoke test, and menu bar registration. Any failure or interruption stops the build's process tree and removes its captured output. Co-Authored-By: Claude Code <noreply@anthropic.com>
On a Mac without the Command Line Tools, swiftc opens Apple's install dialog; starting it in the background would show that dialog unannounced while the installer reports indexing. The early build now runs only when `xcode-select -p` (which never prompts) reports a developer directory with swiftc; otherwise the build runs after the server is ready, as before. The wait message no longer promises a minute-long compile when the build already finished. Co-Authored-By: Claude Code <noreply@anthropic.com>
…essage The installer tests now provide a deterministic `xcode-select -p` developer directory so the background build does not depend on the test host's toolchain, and the failed-build test expects the background-build wait line after Server ready. Co-Authored-By: Claude Code <noreply@anthropic.com>
Keeps the background-build cleanup test from racing the 2s readiness window on slow hosts. Co-Authored-By: Claude Code <noreply@anthropic.com>
# Conflicts: # tests/test_installer_modes.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PlatformCapabilities.parallel_index. All platforms get the matched-pace speedup. Linux keeps its tested/proc/stat/power_supplyprobes for later enablement; the untested Windows ctypes probes were removed.token_meter/services/indexing.pycaps the pool at half the logical CPUs (at most 6, and 2 on battery or Low Power Mode). Every second it adjusts the number of workers from measured idle CPU (additive increase, multiplicative decrease): it halves them when less than one CPU is idle and adds one while two or more are idle. When the host is saturated, workers move to macOS Utility QoS. Measured here: lowering priority with nice alone slowed a CPU-bound foreground job by 30–60%, while Utility QoS had no measurable impact. Workers run niced with utility disk I/O, exit with the server (they watch its process handle), and run from the temp directory.PlatformServicesgainslower_background_priority,set_background_mode,power_constrained, andcpu_activityfor macOS (QoS,pmset,host_statistics), Linux (/proc/stat,power_supply), and Windows (SetPriorityClass,GetSystemTimes,GetSystemPowerStatus). Each one is best-effort: it returns unknown and never raises.warnand one worker oncritical. Because a Python worker keeps its peak memory until it exits, pressure also restarts the pool at that smaller size. Measured: forcingcriticalmid-build cut worker memory from about 1.5 GB to one worker (≤ ~750 MB). If the OS refuses to start a worker, a worker is killed, or a restart fails, indexing falls back to the sequential path with identical results (all covered by tests).xcode-select -p, which never prompts), the macOS installer compiles the menu bar companion in the background while the server indexes, then waits only for what is left. Without a toolchain it builds after the server is ready, exactly asmaindoes, so the installer never opens Apple's developer-tools dialog unexpectedly. Output, failure messages, smoke check and LaunchAgent order are unchanged; a failed or interrupted install stops the build and removes its temp files. Live, with a cold build: Server ready 20s, menu bar companion ready 0s after that.main.Results (this Mac, 2,240 sessions, heavily shared machine)
state_readyDiscovery (~16s) is unchanged and is the next target.
Test plan
python3 -m unittest discover -s tests: 1,514 tests, 0 failures (mainmerged in, including thetest_goalsfix)tests/integration/test_cold_index.py: fixture parity, real spawned workers including a forked Codex child, every fallback path, AIMD bound, single-flight, worker exit on parent kill.tests/domain/test_matched_pace_parity.py: frozenmainreference, checked against an injected change.tests/contracts/test_platform_background_work.py.py_compile, embedded JS parse,bash -n,git diff --check/healthand/menubarOK, both LaunchAgents running, source/runtime parity checked, 6 workers observed. A restart with workers running left 0 behind.5990bf0.cross_sessionrows equal the sequential path with no executor created; suite also passes with the server platform simulated as Linux🤖 Generated with Claude Code