Repository navigation
The CLI was paying for a web server on every command (#173) - #777
Merged
Merged
Conversation
`nodus.cli.cli` imported `nodus.services.server` at module scope for the four commands that need it -- `serve`, `snapshot`, `snapshots`, `restore`. That module imports FastAPI, uvicorn and pydantic, so `nodus run`, `nodus fmt`, `nodus check` and `nodus --version` all paid for a web server nobody asked for. The imports are lazy now, inside those four commands. Measured alternating before/after in one window, best of three each, so machine drift shows as noise rather than signal: import nodus.cli.cli 1435ms -> 652ms (2.20x) nodus --version 1592ms -> 640ms (2.49x) nodus run hello.nd 1711ms -> 1011ms (1.69x) Roughly 700ms off every CLI invocation. This is the standing blocker on #173's PyPy path, which is why it is filed there. PyPy's *interpreter* starts faster than CPython's (104ms vs 184ms); what makes it unadoptable is importing Nodus's own module tree -- run-once code the JIT never warms, against `nodus run`'s 200ms default deadline. Same shape as CIRC-001 (#103), which made vm.py's workflow-runner imports lazy, and carrying the same instruction: do not hoist these back. `tests/test_cli_import_cost.py` asserts on `sys.modules` in a subprocess. A behavioural test cannot see this -- the CLI worked either way, only slower -- and by the time any other test runs, something has usually imported FastAPI already. Falsified by restoring the eager import: three subtests red, both controls still green. Two things found on the way: **The recorded throughput figure was stale.** Measured with the VM's own instruction counter rather than an estimate per loop iteration: 650-850K instr/s at 17 instructions per iteration, against the ~200K #173 records. `tools/benchmark_runtime.py` reports startup and throughput so neither number has to be transcribed again. An earlier reading of mine assumed "about 6 instructions per iteration" and was wrong by nearly 3x, which is why the counter is used. **An unanchored assertion was reporting a defect that was not there.** `assertNotIn("import re", CLI_SOURCE)` also matches `import restore_snapshot`, and did the moment these imports moved. It matches whole lines now, and was falsified against both `import re` and `from re import ...`. The throughput ceiling itself is unchanged; this is startup, not dispatch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K4Y5EtwACvnLXh6MEtY82v
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.
You were right that the CLI import cost had been looked at before — it was
measured during the #702 PyPy work and recorded as the blocker. What had not
been done is asking whether it was reducible. It is, and by a lot.
The finding
nodus.cli.cliimportednodus.services.serverat module scope for the fourcommands that need it (
serve,snapshot,snapshots,restore). That moduleimports FastAPI, uvicorn and pydantic — so
nodus run,nodus fmt,nodus checkandnodus --versionall paid for a web server nobody asked for.Alternating before/after in one window, best of three each, so machine drift
shows as noise rather than signal:
import nodus.cli.clinodus --versionnodus run hello.ndRoughly 700 ms off every CLI invocation.
Same shape as CIRC-001 (#103), which made
vm.py's workflow-runner importslazy, and carrying the same instruction: do not hoist these back.
Why it is filed under #173
This is the standing blocker on that issue's PyPy path. PyPy's interpreter
starts faster than CPython's (104 ms vs 184 ms); what makes it unadoptable is
importing Nodus's own module tree — run-once code the JIT never warms, against
nodus run's 200 ms default deadline. This does not make PyPy adoptable on itsown, but it removes the largest single term.
The throughput ceiling is unchanged. This is startup, not dispatch.
Two things found on the way
#173's recorded throughput figure is stale. Measured with the VM's own
instruction counter rather than an estimate per iteration: 650–850K instr/s
at 17 instructions per iteration, against the ~200K the issue records.
tools/benchmark_runtime.pynow reports startup and throughput so neither hasto be transcribed again. My own first reading assumed "about 6 instructions per
iteration" and was wrong by nearly 3x — which is why it uses the counter.
An unanchored assertion was reporting a defect that was not there.
assertNotIn("import re", CLI_SOURCE)also matchesimport restore_snapshot,and did the moment these imports moved. It matches whole lines now, falsified
against both
import reandfrom re import ....Testing
tests/test_cli_import_cost.pyasserts onsys.modulesin a subprocess — abehavioural test cannot see this (the CLI worked either way, only slower), and
by the time any other test runs something has usually imported FastAPI already.
Falsified by restoring the eager import: three subtests red, both controls
green. The controls matter here — an assertion that the CLI imports no web
framework is equally satisfied by a
climodule that fails to import at all, soone control checks it still imports what it uses and another checks the server
module still has its framework.
ruffclean ·nodus_gate --static --closed-issues --shapesPASS (7/7, 0 new)test_cli_completion.py::BashExecutionTests, which shell out tobash, failidentically on this box before this branch, and are green on CI
🤖 Generated with Claude Code
https://claude.ai/code/session_01K4Y5EtwACvnLXh6MEtY82v