Idea: ship hot lists with releases #525

Closed
opened 2026-11-14 17:45:49 +00:00 by HumanBean · 1 comment
Owner

The idea

Longshot already has everything needed to build the hottest lifted game functions at -O2 with a play profile, but releases don't ship with it, so every player's build compiles all of the lifted code at -O1.

What exists today

  • scripts/hotlist.py turns a sampled play session (HOS_SAMPLE_ALL) into hot_funcs.txt: the lifted functions that make up the first 85 % of samples, hottest first, capped at 1,500, plus the game's Zstd decoder.
  • nx2/split_hot.py puts those functions into eight hot pieces compiled at -O2 with the play profile (RX_PGO, fitted to the pieces by nx2/pgo_pieces.py).
  • scripts/tools.py assemble profile packs the list and the profile for a release (RELEASING.md). make_game.py uses them when work/ has them.
  • Releases ship neither: tools.lock has no profile line ("No play profile yet").

What it buys and what it costs

With today's list, the 85 % cut comes to about 850 functions (849). Measured against main 0caf0618, the whole build before shaders:

Ryzen 9 7900X, 12 pinned threads i7-9700K, 6 jobs
No hot list 9:04 19:32
All 849 at -O2 + profile 10:34, +17 % 23:18, +19 %
First 300 at -O2 + profile not built 21:22, +9 %
First 300 at -O1 + profile not built 20:57, +7 %
  • The gain is small. All 849 gave about +3 % fps walking in the Market (RTX 3090: 16.54 → 15.96 ms, 4 sessions each). Standing still showed nothing.
  • The lab couldn't show it. On the i7-9700K with an RX 580 the game is GPU-bound and its sessions vary by ±2 ms.
  • A shorter list keeps most of it. The first 300 functions cover 83 % of what the 849 cover on the Market walk, with 58 % of the code. That is about a 70 % share instead of 85 %. The gain is expected to scale with coverage, so roughly +2.5 % fps. That's an estimate: 300 hasn't been measured on a CPU-bound PC.
  • PR 385 (merged) changes the trade. It cut a first build's compile by about 21 % on a 6-core PC, so a +9 % list still leaves players' builds faster than before 385. It leaves the hot pieces whole on purpose, so they may become the build's longest files; this needs one build with both to check.

The proposal: generate the list before each release and ship it

  1. Make the list from Longshot itself. The list and profile in hand were made by Hookshot, on a route nobody recorded. Record them from Longshot's own lifter over a broad route rather than one area. The pipeline tour (PR 111) would give the same route every time.
  2. Cut it at about 70 % instead of 85 %, for about 300 functions and +9 % build time. A fixed count (hotlist.py <run> <out> 1.0 300) keeps the build cost the same from one release to the next; a share gives a different count on every session.
  3. Ship it with the release: tools.py assemble profile, tools.py add, and the profile line in tools.lock.
  4. Re-record it when the lifter's output changes. The profile's counts are tied to the lifted C. The list names guest addresses, so it only needs to change with the game version or when functions stop being lifted code.
  5. Maybe: an option in the launcher (a faster build vs. a faster game, such as 100 / 300 / 500 functions). Against that: it's a knob for a few percent, and each choice is another binary to support. The builder could instead choose by the PC (more threads, a longer list).

Related

  • PR 30 keys the hot pieces by the profile's contents instead of its path. It should land first or with this: without it, every new checkout or launcher channel compiles the eight hot pieces again, and the object store (PR 371) can't share them.

Anything else

The play profile holds addresses and counts only, no game data; the owner has approved publishing it. Measurements and scripts are on the PC that made them, and they can be added here.

🤖 Generated with Claude Code

### The idea Longshot already has everything needed to build the hottest lifted game functions at `-O2` with a play profile, but releases don't ship with it, so every player's build compiles all of the lifted code at `-O1`. **What exists today** - `scripts/hotlist.py` turns a sampled play session (`HOS_SAMPLE_ALL`) into `hot_funcs.txt`: the lifted functions that make up the first 85 % of samples, hottest first, capped at 1,500, plus the game's Zstd decoder. - `nx2/split_hot.py` puts those functions into eight hot pieces compiled at `-O2` with the play profile (`RX_PGO`, fitted to the pieces by `nx2/pgo_pieces.py`). - `scripts/tools.py assemble profile` packs the list and the profile for a release (RELEASING.md). `make_game.py` uses them when `work/` has them. - Releases ship neither: `tools.lock` has no `profile` line ("No play profile yet"). **What it buys and what it costs** With today's list, the 85 % cut comes to about 850 functions (849). Measured against main 0caf0618, the whole build before shaders: | | Ryzen 9 7900X, 12 pinned threads | i7-9700K, 6 jobs | |---|---|---| | No hot list | 9:04 | 19:32 | | All 849 at -O2 + profile | 10:34, **+17 %** | 23:18, **+19 %** | | First 300 at -O2 + profile | not built | 21:22, **+9 %** | | First 300 at -O1 + profile | not built | 20:57, +7 % | - **The gain is small.** All 849 gave about +3 % fps walking in the Market (RTX 3090: 16.54 → 15.96 ms, 4 sessions each). Standing still showed nothing. - **The lab couldn't show it.** On the i7-9700K with an RX 580 the game is GPU-bound and its sessions vary by ±2 ms. - **A shorter list keeps most of it.** The first 300 functions cover 83 % of what the 849 cover on the Market walk, with 58 % of the code. That is about a 70 % share instead of 85 %. The gain is expected to scale with coverage, so roughly +2.5 % fps. That's an estimate: 300 hasn't been measured on a CPU-bound PC. - **[PR 385](https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/385) (merged) changes the trade.** It cut a first build's compile by about 21 % on a 6-core PC, so a +9 % list still leaves players' builds faster than before 385. It leaves the hot pieces whole on purpose, so they may become the build's longest files; this needs one build with both to check. **The proposal: generate the list before each release and ship it** 1. **Make the list from Longshot itself.** The list and profile in hand were made by Hookshot, on a route nobody recorded. Record them from Longshot's own lifter over a broad route rather than one area. The pipeline tour ([PR 111](https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/111)) would give the same route every time. 2. **Cut it at about 70 % instead of 85 %**, for about 300 functions and +9 % build time. A fixed count (`hotlist.py <run> <out> 1.0 300`) keeps the build cost the same from one release to the next; a share gives a different count on every session. 3. **Ship it with the release:** `tools.py assemble profile`, `tools.py add`, and the `profile` line in `tools.lock`. 4. **Re-record it when the lifter's output changes.** The profile's counts are tied to the lifted C. The list names guest addresses, so it only needs to change with the game version or when functions stop being lifted code. 5. **Maybe: an option in the launcher** (a faster build vs. a faster game, such as 100 / 300 / 500 functions). Against that: it's a knob for a few percent, and each choice is another binary to support. The builder could instead choose by the PC (more threads, a longer list). **Related** - [PR 30](https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/30) keys the hot pieces by the profile's contents instead of its path. It should land first or with this: without it, every new checkout or launcher channel compiles the eight hot pieces again, and the object store ([PR 371](https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/371)) can't share them. ### Anything else The play profile holds addresses and counts only, no game data; the owner has approved publishing it. Measurements and scripts are on the PC that made them, and they can be added here. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Duplicate of #505 (created twice more while the forge was timing out). Closing.

Duplicate of https://git.magicbean.dev/MagicBeanDev/Longshot/issues/505 (created twice more while the forge was timing out). Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot#525
No description provided.