build: document a faster development build and keep the lift when switching it #2518

Merged
hckrman101 merged 3 commits from build/fast-dev-build into main 2026-11-11 22:16:46 +00:00
hckrman101 commented 2026-11-11 21:36:00 +00:00 (Migrated from github.com)

A development build that only checks whether something works (the game starts, a menu, a screenshot, a log) still waits for the eight hot pieces, which are compiled at -O2 with the play profile and take about a third of the game's compile time. Their level can already be lowered with RX_HOT_OPT=O1, but that is not written down anywhere, and through make_game.py it also makes the next build translate the whole game again.

What changed

  • docs/development.md, Building: an RX_HOT_OPT row in the switches table and a short section, A faster build for development: what to set (RX_HOT_OPT=O1 with nx2/build.py, which then uses no profile; through make_game.py the shipped profile is still passed), when switching keeps the lift and the objects (the checkout's own work/ or HOOKSHOT_KEEP_BUILD_FILES=1; elsewhere make_game.py's clean-up keeps only the last build's, so a switch lifts again and recompiles the hot pieces), what it saves, and a clear warning that it is never for fps, frame times, build timings, hitch checks, profiles, or a release or player build.
  • CONTRIBUTING.md, Building: a one-line pointer to that section.
  • nx2/make_game.py: lift_key leaves RX_HOT_OPT and RX_PGO out (COMPILE_ONLY_ENV). Only nx2/build.py reads them, after the lift, so they never change the lifted C. Before this, setting RX_HOT_OPT for one build, or unsetting it again, changed the key and translated the game again (minutes). Any other A64_/OOT_/RX_ variable still changes the key.
  • tests/test_lift_cache.py: both variables, given to lift_key or in the environment, leave the key as it was; and no file of make_game.lift_sources() (the translator's files, which the key hashes) names them, so a translator change that starts reading one fails the check instead of reusing a stale lift.

The objects need no change: their keys include the flags, so -O1 and -O2 hot objects are cached side by side. make_game.py is not one of lift_sources(), so this edit costs no new lift. The direct build.py --standalone example runs without make_game.py's environment (RX_RELEASE, A64_HOT_CHUNK), so its runtime objects differ from a make_game.py build's; the docs say so.

Measured

These numbers come from existing builds, not from a new -O1 build: the per-file compile times in the build_times.json of six earlier full builds on one Linux desktop (Ryzen 9 7900X, 24 threads, 30 GB, other work running), plus peak memory measured during earlier build work.

Hot pieces (-O2, profile) The 72 other lifted files (-O1)
Compile time, all of them 1,270-1,810 s 2,820-3,750 s
Per MB of C 16-22 s 2.2-2.9 s
Longest single file 300-800 s ~360 s (code_021_1.c)
Peak memory per compiler 0.8-1.7 GB 1.4-1.6 GB (24 MB file)

The hot pieces are 81 MB of the 1.4 GB of lifted C. At the other files' rate they would take about 200 s instead of about 1,500 s, so the game's compile needs about a quarter less time in total, and the longest single file becomes an ordinary one, so a build after a new lift can finish several minutes sooner on a CPU with many threads. Peak memory barely moves, because the ordinary files already set it. The docs give these as estimates and say where they come from.

The game still plays with the hot pieces at -O1: the profile-counting build (RX_PGO=gen) already compiles them at -O1 and is played through to record the profile. Its frame rate was not measured for this PR, and the docs ask readers to check that it is fast enough for their test.

Tested

  • python -I -B tests/test_lift_cache.py passes after merging main (with #2324's check_lift_sources), and fails without the make_game.py change.
  • python -I -B tests/run_all.py on Linux, before merging main: 411 passed, 12 skipped (four checks that write to /tmp were run again with a writable /tmp, and pass).
A development build that only checks whether something works (the game starts, a menu, a screenshot, a log) still waits for the eight hot pieces, which are compiled at `-O2` with the play profile and take about a third of the game's compile time. Their level can already be lowered with `RX_HOT_OPT=O1`, but that is not written down anywhere, and through `make_game.py` it also makes the next build translate the whole game again. ## What changed - `docs/development.md`, *Building*: an `RX_HOT_OPT` row in the switches table and a short section, *A faster build for development*: what to set (`RX_HOT_OPT=O1` with `nx2/build.py`, which then uses no profile; through `make_game.py` the shipped profile is still passed), when switching keeps the lift and the objects (the checkout's own `work/` or `HOOKSHOT_KEEP_BUILD_FILES=1`; elsewhere `make_game.py`'s clean-up keeps only the last build's, so a switch lifts again and recompiles the hot pieces), what it saves, and a clear warning that it is never for fps, frame times, build timings, hitch checks, profiles, or a release or player build. - `CONTRIBUTING.md`, *Building*: a one-line pointer to that section. - `nx2/make_game.py`: `lift_key` leaves `RX_HOT_OPT` and `RX_PGO` out (`COMPILE_ONLY_ENV`). Only `nx2/build.py` reads them, after the lift, so they never change the lifted C. Before this, setting `RX_HOT_OPT` for one build, or unsetting it again, changed the key and translated the game again (minutes). Any other `A64_`/`OOT_`/`RX_` variable still changes the key. - `tests/test_lift_cache.py`: both variables, given to `lift_key` or in the environment, leave the key as it was; and no file of `make_game.lift_sources()` (the translator's files, which the key hashes) names them, so a translator change that starts reading one fails the check instead of reusing a stale lift. The objects need no change: their keys include the flags, so `-O1` and `-O2` hot objects are cached side by side. `make_game.py` is not one of `lift_sources()`, so this edit costs no new lift. The direct `build.py --standalone` example runs without `make_game.py`'s environment (`RX_RELEASE`, `A64_HOT_CHUNK`), so its runtime objects differ from a `make_game.py` build's; the docs say so. ## Measured These numbers come from existing builds, not from a new `-O1` build: the per-file compile times in the `build_times.json` of six earlier full builds on one Linux desktop (Ryzen 9 7900X, 24 threads, 30 GB, other work running), plus peak memory measured during earlier build work. | | Hot pieces (`-O2`, profile) | The 72 other lifted files (`-O1`) | |---|---|---| | Compile time, all of them | 1,270-1,810 s | 2,820-3,750 s | | Per MB of C | 16-22 s | 2.2-2.9 s | | Longest single file | 300-800 s | ~360 s (`code_021_1.c`) | | Peak memory per compiler | 0.8-1.7 GB | 1.4-1.6 GB (24 MB file) | The hot pieces are 81 MB of the 1.4 GB of lifted C. At the other files' rate they would take about 200 s instead of about 1,500 s, so the game's compile needs about a quarter less time in total, and the longest single file becomes an ordinary one, so a build after a new lift can finish several minutes sooner on a CPU with many threads. Peak memory barely moves, because the ordinary files already set it. The docs give these as estimates and say where they come from. The game still plays with the hot pieces at `-O1`: the profile-counting build (`RX_PGO=gen`) already compiles them at `-O1` and is played through to record the profile. Its frame rate was not measured for this PR, and the docs ask readers to check that it is fast enough for their test. ## Tested - `python -I -B tests/test_lift_cache.py` passes after merging `main` (with #2324's `check_lift_sources`), and fails without the `make_game.py` change. - `python -I -B tests/run_all.py` on Linux, before merging `main`: 411 passed, 12 skipped (four checks that write to `/tmp` were run again with a writable `/tmp`, and pass).
Commenting is not possible because the repository is archived.
No description provided.