perf(host): the write marks' collection steps over 256 clear marks at a time #63
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
beanstalk: approved @ 1697603
beanstalk: approved @ 2c5a62e
beanstalk: approved @ 4fd82b7
beanstalk: approved @ 64d6d09
beanstalk: approved @ 6f91fae
beanstalk: approved @ 70a78f9
beanstalk: approved @ 8d361dc
beanstalk: approved @ 9d8356e
beanstalk: approved @ abf2227
beanstalk: approved @ d82f202
beanstalk: approved @ e5f1e40
beanstalk: approved @ ee8ab5f
ci: approved
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/macos
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
regression
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
ci: approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!63
Loading…
Reference in a new issue
No description provided.
Delete branch "HumanBean/Longshot:perf/collect-wide"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Every submit collects the pages the game wrote since the last one, on the thread that replays the frame. The walk over the write marks was most of that cost, and almost every mark it looks at is clear. This takes about 0.12-0.24 ms a frame of CPU time off the replay thread in Kokiri Forest; no frame-rate gain is claimed.
What changed
collect_in(host/rx_backend.c) walks one mark byte per 4 KB page over every range the GPU can see. It stepped 32 marks at a time while none was set. It now tests 256 marks at once first (32 words; the compiler makes four AVX2 loads of them), and falls back to the 32 and 8 mark steps where something is marked.HOS_COLLECT_WIDE=0turns the wide step off (for A/B).tests/test_collect_wide.pycompilescollect_infrom the source and compares it with a plain walk over random marks and ranges (aligned or not, short, empty, past the guest's end, past the array's end), with the switch on and off, and checks that the wide step is really taken.Flags and settings
HOS_COLLECT_WIDE0turns the 256-mark step off, leaving the 32 and 8 mark steps as before (for A/B)The switch is in the switch reference of
docs/development.md("The GPU" table).Measurements
Baseline: this PR @
20e0b2d9, HOS_COLLECT_WIDE=0main was not the comparison: the switch turns the change off inside the same build, so the two sides differ in nothing else.
main:
15d4ae8c(2026-11-12)this PR: perf/collect-wide @
20e0b2d9Hardware: RTX 3090, NVIDIA 620.70.06, Linux (CPU and RAM not recorded in this description)
Scene: Kokiri Forest, the pristine save, standing; 2048x1152 window, default options, 6 submits a frame
Method: four runs in the order A B B A (A is
HOS_COLLECT_WIDE=0, B the default); the renderer's own line,vk: a submit's memory work (ms): ... writes collected, 600 submits a line, 8 lines a runHow to reproduce: start the game into Kokiri Forest on the pristine save with default options, stand still, and read the
vk: a submit's memory work (ms)lines of the log, once withHOS_COLLECT_WIDE=0and once without itThe timed build was on an older base than the branch head, and the numbers are not re-measured yet. The game that was timed was built with this change on main
15d4ae8c. The change then moved onto a newer main without conflict as20e0b2d9, and the branch headc15b0c9dmerges the current main (94bbc205) into it, without conflict, and adds the switch's row todocs/development.md. No game was built or timed on the merged tree. Why the numbers should still hold: the file the change is in,host/rx_backend.c, is the same on the merged tree as in the commit that was timed apart from the project's rename (compared by diff after putting the new name in the old file: no line differs), and the new test compilescollect_infrom the merged source. A clean re-measure (unchanged main beside the change, at least three interleaved runs a side, medians, fps and frame time) is still owed: the PC these numbers come from is swapping heavily (about 24 GB in swap, and the last five timed runs on it were void for memory), so it waits for a PC that is not.20e0b2d9, HOS_COLLECT_WIDE=020e0b2d9, default (wide step on)That is 0.02-0.04 ms a submit, about 0.12-0.24 ms a frame of CPU time on the replay thread.
No frame-rate gain is claimed. In this scene the frame is bounded by the GPU's time and the present's wait for it, not by this thread's CPU time, so the fps of the runs does not move outside its noise (the four runs, in the order run: 56.6, 68.4, 59.5 and 62.25 fps, on a PC that was swapping throughout; they say nothing either way).
Not measured: the 1% low, the median and p99 frame time, and a run of unchanged main beside the switch. The log lines themselves are not pasted here, only the figures read from them.
Testing
Tested:
tests/test_collect_wide.py(new), on Linux: the function compiled from the source against a plain walk, with the switch on and off. Run again on the branch headc15b0c9d(the tree merged with main94bbc205): passes.python -I -B tests/run_all.py: 501 passed, 2 failed, 26 skipped on this PC (Linux, 4 at once), the same on the tree of the earlier merge with main (e2a4d453); not run again on the merge with main94bbc205, where only this PR's own test was run. The two that fail need tools this PC lacks (zstandard,libtk) and fail on unchanged main here too.Not tested:
Needs other testers:
HOS_COLLECT_WIDE=0(request to open)Screenshots
Nothing on screen.
Risk and rollback
The walk decides which written pages reach the GPU: a mistake in it would show as stale or missing data on screen. The test compares it with a plain walk over random marks and ranges.
HOS_COLLECT_WIDE=0turns the wide step off; reverting the commit is enough to remove it.Reviewer notes
Read
collect_ininhost/rx_backend.cfirst: the 256-mark test is in front of the 32 and 8 mark steps, which are unchanged.Issues
None.
Checklist
For everyone
type(scope): description).main. (By a merge, not a rebase: main94bbc205is merged into the branch headc15b0c9d, without conflict.)python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.docs/tested-configurations.mdfor each measured run. (Not added.)MKWVK_API_VERSIONbumped (gpu/vk/vk_api.h'sMkwVkHostorMkwVkRendererchanged). (n/a)mainand passing here. (n/a: not a fix. A game-free test of the new walk is added.)tests/that reads changed source updated in this PR, not weakened. (n/a)docs/development.md.)The description is now in the template's sections. No code changed and no new numbers: the same facts as before, with the commits the measurements were taken on named, and the checklist ticked only where true. Three For everyone boxes are not true yet (main has moved since the branch merged it; the test requests are not open; the timed build was on an older main, with no median), so this goes back to work in progress.
perf(host): the write marks' collection steps over 256 clear marks at a timeto WIP: perf(host): the write marks' collection steps over 256 clear marks at a timeMerged current main (
94bbc205) into the branch (headc15b0c9d, no conflict) and added the HOS_COLLECT_WIDE row to docs/development.md; the PR's test passes on the merged tree. No new numbers: the re-measure is waiting for a PC that is not swapping, so this stays work in progress.WIP: perf(host): the write marks' collection steps over 256 clear marks at a timeto perf(host): the write marks' collection steps over 256 clear marks at a time