Skip to content

Every CLI invocation pays 3.5-6.5s of new startup cost (php corpus: 1.4s on 0.9.0 vs 7.9s / 4.9s warm on 0.10.5) #1670

Open

Description

@DeusData

Version

0.10.5 (regression against 0.9.0)

Platform

macOS arm64; shipped release binaries of both versions

Summary

Every CLI invocation pays a fixed startup cost that 0.9.0 did not. On a small corpus (php, 2,807 files) where 0.9.0 completes in 1.4 s, 0.10.5 takes ~7.9 s — and 4.9 s even with a warm daemon, so roughly 3.5 s is not recoverable by the documented mitigation.

Measurement

Same corpus, same host, --mode full --persistence true.

wall
v0.9.0 1.4 s
v0.10.5, no daemon running 8.0 s / 7.9 s / 7.9 s
v0.10.5, after daemon start 4.9 s / 4.9 s

The three no-daemon runs used the same HOME and CBM_CACHE_DIR consecutively, so this is not a cold-cache artifact — the daemon does not persist between CLI invocations on its own, and each command re-pays the bootstrap. That is what the emitted hint is pointing at:

hint: this command started a temporary CBM daemon. `codebase-memory-mcp daemon start` keeps one
warm and removes this startup cost from every CLI command.

But "removes" overstates it: daemon start takes 7.9 s → 4.9 s, still 3.5 s above 0.9.0.

Why it matters

The absolute number is small, but it is paid per invocation, so it dominates exactly the workflows that call the CLI repeatedly:

  • hook-driven usage (hook_augment, PostToolUse hooks) fires per tool call
  • scripts and CI steps that invoke several commands in sequence
  • any small-repo or incremental use, where 3.5–6.5 s of fixed cost exceeds the actual work

It also distorts benchmarking: on small corpora the fixed cost is most of the wall time (perl: 0.6 s → 6.9 s is almost entirely this, not throughput).

Reproduction

W=$(mktemp -d)
for i in 1 2 3; do
  time env HOME="$W" CBM_CACHE_DIR="$W/.cache" ./codebase-memory-mcp cli index_repository \
    --repo-path /path/to/small-repo --mode full --name "run$i" --persistence true
done

Questions for triage

  1. How much of the 3.5 s warm floor is the version-cohort lease / rendezvous handshake, and can it be skipped when the daemon is already known-compatible?
  2. Should a CLI invocation start a temporary daemon at all when it is going to retire it moments later, or should the one-shot path bypass daemon coordination entirely?
  3. If the daemon is the intended steady state, should daemon start be part of install rather than a hint printed after the cost has already been paid?

Context: found while A/B-benching 0.9.0 vs 0.10.5 across 11 corpora for #1654. Separate from #1654 (Linux memory) and #1669 (cross-LSP throughput) — this one is platform-independent and affects every invocation.

Activity

  1. added
    bugSomething isn't working
    stability/performanceServer crashes, OOM, hangs, high CPU/memory
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Aug 18, 2026
  2. vitalca commented on Aug 19, 2026

    @vitalca

    The 0.10.x branch is totally unusable. I am on Linux, a lot of java, cpp, frontend repos. It's incredibly slooow!
    I downgraded back to 0.9.0

  3. AgathaCrystal commented on Sep 19, 2026

    @AgathaCrystal

    Still present in 0.11.0 on macOS arm64, and it looks like the executable self-hash.

    Measured on Apple Silicon, v0.11.0 installed at ~/.local/bin, binary size 301 MB:

    codebase-memory-mcp --version returns in 8 ms.

    Any one-shot call, e.g. codebase-memory-mcp cli list_projects, takes 3.5–5 s wall-clock with about 3.4 s of user CPU. With --progress it prints Preparing one-shot local CBM command... and then Completed list_projects (110 ms). The tool is fast; the preparation is not.
    The same ~3.4 s of CPU burns with HOME pointed at an empty cache folder. It then fails at admission with active cache ; requested cache . So the cost does not depend on index size.

    In stdio MCP mode the initialize reply arrives after 3.5 s. Every query after that takes 17–104 ms.
    sample on the stripped binary shows a single tight CPU loop plus pread calls on the main thread.

    For comparison, v0.9.0 on the same machine, with a fresh cache and one small repo indexed:

    one-shot cli list_projects: 0.10 s
    cli get_code_snippet: 15 ms
    stdio initialize: 25 ms

    This matches what the source describes. main_build_identity() calls cbm_index_supervisor_capture_build_fingerprint(), whose comment says computing the real fingerprint "hashes the ENTIRE executable image". src/main.c also notes that "the identity hash costs ~1.1 s of user CPU per invocation". On a 301 MB image that hash is the whole warm floor. The only skip, CBM_TEST_BUILD_FINGERPRINT, sits behind CBM_CLI_ENABLE_TEST_API and is not in release builds.

    Possible fixes:

    Cache the fingerprint under ${CBM_CACHE_DIR}, keyed by executable path, size, mtime and inode, so an unchanged binary is hashed once.
    Hash a bounded prefix, a suffix and the size, instead of the full image.
    Let the running daemon vouch for a client whose executable path and inode match the one it already verified.
    Related: #2058 (same cost on the hook path) and PR #1767, which caches the fingerprint per process only. That does not help one-shot CLI calls, where every invocation is a new process.

    Impact for us: scripts and hooks that need a single lookup pay 3.5 s each. Agent sessions are fine, because they pay it once.


    This was the summary i got from fable 5.1 after i ran into the same problem. I hope it helps!

  4. DeusData commented on Sep 19, 2026

    @DeusData
    OwnerAuthor

    Thank you for the startup measurements. Current startup still computes a full executable fingerprint once per fresh process; index_supervisor.c caches it only within that process. A warm daemon therefore does not eliminate this first hash from the next CLI invocation. This supports a real fresh-process cost, but does not attribute every reported second to hashing. A weaker fingerprint is not an approved fix.

  5. DeusData commented on Oct 19, 2026

    @DeusData
    OwnerAuthor

    Thank you for the measurements and the follow-up on 0.11.0. The distinction between fresh-process startup and work done after initialization remains useful here. I do not have a verified fix or new timing result to share in this follow-up; the previously noted fingerprint trade-off still needs a maintainer decision.

  6. DeusData commented on Oct 25, 2026

    @DeusData
    OwnerAuthor

    Thank you for the fresh-process startup measurements. The fingerprint cost and safe mitigation still need a maintainer decision; there is no new timing result to report. Sorry for the delay; this is waiting on maintainer follow-up, and I’ll follow up soon.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.stability/performanceServer crashes, OOM, hangs, high CPU/memory

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions