docs(build): record the thin-binary layout and its measured effect

Replace the 'known remaining inefficiency' section with the layout that
now exists, plus the measured before/after (clean release build: 2m14s
-> 1m46s wall, keydr units 61.0s -> 32.4s, peak ~2.0 GB unchanged).

Add a maintenance note: new modules go in src/lib.rs, not src/main.rs.
Re-declaring them in the binary would silently restore the double
compile this refactor removed.
This commit is contained in:
2026-08-07 01:08:44 +00:00
parent 0a74fe3143
commit f8d3bb5b2b
+28 -24
View File
@@ -9,8 +9,9 @@ cargo build --release
```
Plain cargo. No wrapper, no memory ceiling, no reduced optimisation.
A clean release build peaks at **1.92 GB** and takes **2m17s** on a
4-core / 7.6 GB VM.
A clean release build peaks at **~2.0 GB** and takes **1m46s** on a
4-core / 7.6 GB VM; an incremental rebuild after touching one file is a
few seconds.
If that's not what you're seeing, read on.
@@ -87,35 +88,38 @@ Nothing there reduces the optimisation of shipped code.
---
## Known remaining inefficiency
## Project layout
`src/main.rs` re-declares the whole module tree (`mod app; mod config;
...`) instead of depending on the `keydr` lib target, so **every module
compiles twice** — once for the lib, once for the bin. The `i18n!` macro
must therefore be invoked in both crate roots (`t!()` expands to
`crate::_rust_i18n_t`, so it must exist in each crate).
All application code lives in the **library** (`src/lib.rs``src/run.rs`
and the module tree). `src/main.rs` is a 10-line entry point that calls
`keydr::run()`.
Measured with `cargo build --release --timings` on a clean tree, the two
duplicated units are the slowest in the whole graph:
This matters for build time. `main.rs` used to re-declare the whole module
tree (`mod app; mod config; ...`), which made the binary a *second
independent crate*: every module — and the entire rust-i18n translation
table — was compiled twice, and 281 unit tests ran twice.
Measured effect of consolidating it, clean release build:
```
33.7s keydr (bin)
26.0s keydr (lib)
1.4s generate_test_profiles (bin)
------
61.0s of 292.2s total unit-seconds (21%)
before after
keydr (bin) 33.7s 0.3s
keydr (lib) 26.0s 31.0s
keydr total 61.0s 32.4s (-28.6s)
total unit-seconds 292.2s 264.5s
wall clock 2m14s 1m46s (-28s, -21%)
```
So restructuring `main.rs` to `use keydr::...` would save roughly 26s of
compile work — real, but nowhere near half the build. Wall-clock saving is
smaller still, since those units partly overlap with dependency
compilation across 3 parallel jobs (292s of unit-work compressed into
~2m15s wall). Worth doing for code hygiene; not a build-performance
emergency.
Test coverage is unchanged — 341 unique tests before and after. The
execution count dropping from 622 to 340 is the duplicate run disappearing.
Note the remaining ~79% is dependency compilation (`reqwest` 12.7s,
`clap_builder` 11.4s, `toml_edit` 9.3s, `tokio` 8.0s...), which is
one-time work that incremental builds skip entirely.
**Keep it this way:** if you add a module, declare it in `src/lib.rs`, not
`src/main.rs`. Re-declaring modules in the binary would silently restore
the double compile.
The remaining ~79% of build time is dependency compilation (`reqwest`
12.7s, `clap_builder` 11.4s, `toml_edit` 9.3s, `tokio` 8.0s...), which
incremental builds skip entirely.
---