Skip to content

perf(nix): build Crane layers with the binary's feature resolution - #1925

Merged
ryoppippi merged 2 commits into
perf/isolate-pricing-snapshotsfrom
perf/align-crane-layer-features
Oct 18, 2026
Merged

ryoppippi merged 2 commits into
perf/isolate-pricing-snapshotsfrom
perf/align-crane-layer-features

Conversation

@ryoppippi

@ryoppippi ryoppippi commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Stack created with GitHub Stacks CLI • Give Feedback 💬

Summary

The Crane artifact layers now compile the workspace crates with exactly the feature set of the final -p ccusage --bin ccusage build. As a result, the final derivation no longer recompiles ccusage-core, ccusage-adapter-common, and every adapter.

What Changed

  • nix/cargo-artifacts.nix: every layer runs the final package's own build command (commonArgs.cargoExtraArgs, which also covers --target for the static and cross builds). Each layer builds a source tree that starts from the dependency layer's Crane dummy source, with the layer's real crates and the real root Cargo.toml/Cargo.lock copied on top.
  • package.nix, nix/static-package.nix, nix/darwin-x64-package.nix: pass depsOnlyArgs.dummySrc to the layers. cargoTargetArgs is no longer needed.
  • The foundation layer no longer builds ccusage-test-support. The README of that crate is updated to match.

Why

Cargo unifies dependency features over the packages selected with -p. Building a layer's subset resolved different features than the final build:

  • serde_json lost float_roundtrip, which only ccusage-pricing-snapshots enables.
  • ccusage-test-support added rustix/fs and changed serde_core and cc.

As a result, the layer's crates had different fingerprints from the final build's, and the final step recompiled all of them.

Cargo detects dummy-to-real transitions through the fingerprint, not mtimes. Crane points every dummy manifest at a dummy build script. ccusage-cli-parser and ccusage-config stay out of the layers, as their READMEs describe.

Nothing reused the separate ccusage-test-support build:

  • Its own selection rebuilt sqlite3-src with different cc features.
  • The test derivation recompiles all 110 crates either way: 2m34s with it, 2m35s without.

Testing

  • Final step on aarch64-darwin, using alternating nix build --rebuild runs:
    • Before: cargo step of 32-37s. It compiled 25 workspace crates.
    • After: 20-27s. It compiles only ccusage, ccusage-pricing-snapshots, ccusage-cli-parser, and ccusage-config.
  • Foundation layer: 23s to 4s. Adapters layer: about 11s to about 10s.
  • The release binary is unchanged except for LC_UUID and the code-signature page hash that covers it:
    • After removing the ad-hoc signature and zeroing the UUID, the parent, a --rebuild of the parent, and this branch's binary all hash to 938a16b0…03de.
    • Two --rebuild runs of the unchanged parent derivation already differ in exactly those bytes.
    • Size is unchanged at 3,661,648 B.
    • --version, --help, and daily --json --offline produce identical output.
  • nix build of checks.aarch64-darwin.{ccusage-clippy,ccusage-hawk,config-schema} and .#ccusage-tests passes.
  • The static musl and darwin-x64 packages, x86_64-linux ccusage, and the checks all evaluate.

The final `cargo build -p ccusage --bin ccusage` recompiled
ccusage-core, ccusage-adapter-common and every adapter even when the
foundation and adapters layers were fresh. Each layer built its crates
with its own `-p` list, and Cargo unifies dependency features over the
selected packages: without ccusage-pricing-snapshots in the selection,
serde_json lost float_roundtrip, and ccusage-test-support pulled in
rustix's fs feature, so the layer's crates got different fingerprints
from the final build's.

Every layer now runs the final package's own build command over a
source tree where its crates are real and the rest of the workspace
keeps Crane's dummy sources from the dependency layer. The dependency
units and feature sets therefore match the final build exactly, and the
final layer only compiles ccusage, ccusage-pricing-snapshots and the
two leaf crates that were already kept out of the layers
(ccusage-cli-parser, ccusage-config). Dummy-to-real transitions are
caught by the fingerprint because Crane points every dummy manifest at
a dummy build script. ccusage-test-support is not in the binary's
graph, so the foundation layer builds it in a second invocation as
before.

On aarch64-darwin the final derivation's cargo step drops from
32-37s to 23-27s (three alternating --rebuild runs each). The shipped
binary is unchanged apart from LC_UUID and the ad-hoc signature page
hash that covers it; two --rebuild runs of the parent derivation
already differ in exactly those bytes.
The foundation layer built ccusage-test-support in a second Cargo
invocation for the test derivation, but nothing reused it. Its own
package selection resolves cc without `parallel` and tempfile without
`getrandom`, so it recompiled sqlite3-src's bundled C and a test-only
dependency set that neither the binary nor `cargo test --release
--workspace` reuses: the test derivation rebuilt all 110 crates it
compiles, assert_fs and regex-automata included, either way.

Dropping it cuts the foundation layer's cargo step from about 17.6s to
4s, and that layer gates both the binary and the config-schema
preflight. The test derivation's compile time is unchanged (2m34s
before, 2m35s after), and with no second invocation left the layers no
longer need cargoTargetArgs.
@ryoppippi
ryoppippi added this pull request to stack #1926 October 18, 2026 15:25
@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: dca2227b-b710-4342-8407-c7fd8b17b0d9
📥 Commits

Reviewing files that changed from the base of the PR and between a36c243 and 26be662.

📒 Files selected for processing: 5
  • nix/cargo-artifacts.nix
  • nix/darwin-x64-package.nix
  • nix/static-package.nix
  • package.nix
  • rust/crates/ccusage-test-support/README.md
+---------------------------------------------------------------------------------------------------------------------+
| The average user doesn't give a damn what happens, as long as (1) it works and (2) it's fast. - Daniel J. Bernstein |
+---------------------------------------------------------------------------------------------------------------------+
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ryoppippi ryoppippi changed the title perf/align crane layer features perf(nix): build Crane layers with the binary's feature resolution Oct 18, 2026
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ccusage-guide 26be662 Commit Preview URL

Branch Preview URL
Oct 18 2026, 03:25 PM

@pkg-pr-new

pkg-pr-new Bot commented Oct 18, 2026

Copy link
Copy Markdown

Open in StackBlitz

ccusage

npx https://pkg.pr.new/ccusage@1925

@ccusage/ccusage-darwin-arm64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-arm64@1925

@ccusage/ccusage-darwin-x64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-x64@1925

@ccusage/ccusage-linux-arm64

npx https://pkg.pr.new/@ccusage/ccusage-linux-arm64@1925

@ccusage/ccusage-linux-x64

npx https://pkg.pr.new/@ccusage/ccusage-linux-x64@1925

@ccusage/ccusage-win32-x64

npx https://pkg.pr.new/@ccusage/ccusage-win32-x64@1925

commit: 26be662

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 26be66213d6a
Base SHA: a36c2435daf9

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 364.3ms 2.76 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 334.8ms 3.01 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 142.6ms 7.06 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 129.2ms 7.79 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 44.1ms 38.9ms 1.13x 55.25 MiB 55.50 MiB 1.00x 0.04 MiB/s 0.04 MiB/s
claude session --offline --json 0.00 MiB 36.2ms 34.9ms 1.04x 55.50 MiB 55.75 MiB 1.00x 0.04 MiB/s 0.04 MiB/s
codex daily --offline --json 0.00 MiB 39.4ms 38.6ms 1.02x 55.75 MiB 55.25 MiB 0.99x 0.02 MiB/s 0.02 MiB/s
codex session --offline --json 0.00 MiB 34.4ms 37.8ms 0.91x 55.75 MiB 55.50 MiB 1.00x 0.02 MiB/s 0.02 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 374.4ms 365.5ms 1.02x 937.09 MiB 923.09 MiB 0.99x 2.69 GiB/s 2.75 GiB/s
codex --offline --json 1.01 GiB 140.8ms 141.1ms 1.00x 493.43 MiB 523.44 MiB 1.06x 7.15 GiB/s 7.14 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 24.03 KiB 24.03 KiB -0.00 KiB 1.00x
installed native package binary 4906.34 KiB 4906.34 KiB +0.00 KiB 1.00x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 26be66213d6a
Base SHA: a36c2435daf9

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 360.7ms 2.79 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 348.8ms 2.89 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 138.0ms 7.29 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 116.5ms 8.64 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 72.7ms 20.7ms 3.51x 55.50 MiB 22.96 MiB 0.41x 0.02 MiB/s 0.07 MiB/s
claude session --offline --json 0.00 MiB 65.5ms 20.0ms 3.28x 55.00 MiB 22.96 MiB 0.42x 0.02 MiB/s 0.08 MiB/s
codex daily --offline --json 0.00 MiB 58.7ms 20.8ms 2.82x 55.50 MiB 28.96 MiB 0.52x 0.01 MiB/s 0.04 MiB/s
codex session --offline --json 0.00 MiB 45.6ms 13.0ms 3.49x 55.50 MiB 28.97 MiB 0.52x 0.02 MiB/s 0.07 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 368.8ms 344.5ms 1.07x 931.10 MiB 943.09 MiB 1.01x 2.73 GiB/s 2.92 GiB/s
codex --offline --json 1.01 GiB 154.3ms 122.4ms 1.26x 535.44 MiB 523.44 MiB 0.98x 6.52 GiB/s 8.23 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 24.03 KiB 24.03 KiB -0.00 KiB 1.00x
installed native package binary 4906.34 KiB 4906.34 KiB +0.00 KiB 1.00x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@ryoppippi
ryoppippi marked this pull request as ready for review October 18, 2026 17:03
@ryoppippi
ryoppippi merged commit 8fe3f47 into main Oct 18, 2026
45 of 50 checks passed
@ryoppippi
ryoppippi deleted the perf/align-crane-layer-features branch October 18, 2026 17:03

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — reviewed the full PR scope (fde9835 plus 26be662) building Crane layers with the final binary's feature resolution.

  • Feature-unified layer builds — each layer runs cargoWithProfile build -p ccusage --bin ccusage over dummySrc with only that layer's crates made real, so dependency units match the final build.
  • Test-support removal — the foundation layer no longer builds ccusage-test-support in a second invocation; the now-unused cargoTargetArgs plumbing is removed.
  • Layer wiring — package.nix, nix/static-package.nix, and nix/darwin-x64-package.nix pass dummySrc into the layers, and the test-support README documents its out-of-layer status.

Pullfrog  | View workflow run | Using muse-spark-1.3-contributor (free via Pullfrog for OSS) | 𝕏

Buster210 added a commit to Buster210/ccusage that referenced this pull request Oct 20, 2026
Upstream tip: c76874e (2026-10-20, chore(pricing): update models.dev snapshot)
Previous fork base: 0c58b88 (the upstream commit this fork was synced to)

Synced 83 upstream commits on top of 0c58b88: 56 automated pricing
snapshot refreshes and the following meaningful ones:

  1cd29f5 chore(deps): update pnpm to v12.13.0 (ccusage#1907)
  e4f63ba chore(deps): update node.js to >=24.22.0 (ccusage#1908)
  130e502 ci: regenerate bun.nix automatically (ccusage#1909)
  259ec33 feat(adapter): report stored cost in JSON model breakdowns (ccusage#1903)
  c1eb448 chore: release v20.0.35 (ccusage#1901)
  2a0875f ci(pricing): update snapshots on push to main and daily instead of hourly (ccusage#1914)
  b090428 chore(deps): update github/codeql-action action to v4.39.0 (ccusage#1912)
  4dcf7f5 fix(nix): pin models-dev-gen remeda to models.dev's version and stop Renovate drift (ccusage#1915)
  46edb28 fix(nix): stop the dev shell from copying the whole repository (ccusage#1917)
  58ec8f1 fix(nix): let commits succeed in worktrees without a loaded dev shell (ccusage#1920)
  a570a6f fix(nix): vendor Cargo deps without import-from-derivation (ccusage#1918)
  f6d28c5 fix(antigravity): map model IDs 1418/1419 to gemini-4-argon (ccusage#1905)
  ce439e5 fix(antigravity): map model ID 1050 to gemini-3.1-flash-lite (ccusage#1902)
  e62a66f chore(deps): update pnpm to v12.14.0 (ccusage#1923)
  117ae68 ci(nix): make the macOS Nix cache save a trimmed store with the Crane layers (ccusage#1921)
  7a063a0 ci: shorten the Windows native build critical path (ccusage#1924)
  85ebca3 perf(rust): move pricing snapshots into a leaf crate (ccusage#1922)
  8fe3f47 perf(nix): build Crane layers with the binary's feature resolution (ccusage#1925)
  ac289aa perf(pricing): give the pricing refresh a verify-only TLS 1.3 rustls provider (ccusage#1927)
  d097f74 chore(deps): update rust crate rustls to 0.23.46 (ccusage#1928)
  f8aaf89 docs: use the compact pkg.pr.new URL for bunx previews [ci skip] (ccusage#1929)
  f56bb3d chore: release v20.0.36 (ccusage#1916)
  aaafb41 chore(deps): update dependency vitepress-plugin-group-icons to ^1.8.1 (ccusage#1931)
  0861e7f fix(claude): count requestless resp_ content blocks once (ccusage#1935)
  867c9f3 chore: release v20.0.37 (ccusage#1933)
  fa40bfd chore(deps): update songmu/tagpr action to v1.21.3 (ccusage#1937)
  de99d15 chore(deps): update rust crate zstd to 0.14.3 (ccusage#1936)

Fork wins kept: the SQLite on-disk cache with --live-only and the per-namespace
ledger, the pi adapter and its per-path namespaces, the OpenCode V2 loader, the
FxHash/Cow hot paths, the table renderer, clear-cache, and the tag-only release
workflow. Upstream's own release and tagpr machinery stays out.

Hand-resolved conflicts:

  rust/adapters/claude/src/lib.rs: upstream's resp_ message-id dedupe (ccusage#1935) lands
    beside the fork's requestless replay and collapse_replays rules.
  rust/adapters/claude/src/README.md: upstream's wording for the msg_/resp_ exception.
  rust/Cargo.toml: zstd 0.14.3, keeping the fork's exact `=` pin style.
  .github/workflows/release.yaml: the fork's tag-only release, without upstream's
    tagpr job or the songmu/tagpr action bump.
  apps/ccusage/package.json, docs/package.json, package.json: keep 20.0.34;
    upstream's 20.0.37 bump is dropped.
  packages/ccusage-*/package.json (6 files): stay deleted; the fork ships no npm
    wrapper packages and the release workflow does not read them.
  rust/adapters/claude/src/daily.rs: stays deleted; the fork's daily path lives in
    lib.rs and shares the dedupe helper the fix changes.

Fixes made while landing this sync, folded in so the merge stands green on its own:

  fix(ci): save the Nix store from the native build composites. The nested
    cache-nix-action read an empty primary-key at post-job and every native
    build failed; upstream's defer-save pattern is the fix.
  fix(rust): keep the agent-recorded cost for deleted sources. The recorded_cost
    ledger column carries it, so storedCost survives a deleted file. The ledger
    schema version stays at 2: the column is not a LedgerEntry field, so the blob
    layout is unchanged and the previously released binary keeps working.
  fix(rust): make the file fingerprint helper private, the one hawk finding. It is
    inherited from the pre-sync master, not introduced here.

Keep 20.0.34 numbering; DO NOT push a v20.0.34 tag from this fork (release.yaml
would publish fork bytes under upstream identity).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant