Skip to content

fix(wgpu): close a device profile on its last compute pass - #1592

Merged
ThierryCantin-Demers merged 1 commit into
mainfrom
fix/wgpu-profile-passes
Sep 3, 2026
Merged

fix(wgpu): close a device profile on its last compute pass#1592
ThierryCantin-Demers merged 1 commit into
mainfrom
fix/wgpu-profile-passes

Conversation

@ThierryCantin-Demers

@ThierryCantin-Demers ThierryCantin-Demers commented Sep 2, 2026

Copy link
Copy Markdown
Member

Closes #1421.

A profiled region gets timestamp writes only on the pass that drains its token, so stop_profile_setup resolves the end from the set the start came from and the window is the first compute pass alone. WgpuStream closes a pass every 32 dispatches, so any longer region reports a constant.

That constant becomes zero in launch_overhead, which derives its loop length from the measurement: a window that never grows drives warmup to ~670k iterations, and 23.9 µs over that count underflows Duration to zero. It prints N/A, and roofline_bounds has been costing a launch at zero on every wgpu backend.

The fix. A profile keeps one query set for its whole span. The pass that opens it writes both slots; every later pass while it is live rewrites only the end slot, so the last pass marks the end. No extra query sets, and the allocator is untouched.

Not the per-pass sets #1421 suggests: that is ~233k create_query_set calls per sample at these counts, and under Metal's 28-set budget the round-robin hands back the set a live profile started in and clobbers that start, giving zero again.

Measured, RTX 4070 Ti SUPER, profiling N launches:

launches main per launch this branch per launch
8 7.424 µs 928 ns 7.68 µs 960 ns
64 27.136 µs 424 ns 162.8 µs 2.544 µs
512 27.648 µs 54 ns 1.917 ms 3.745 µs
4096 27.392 µs 6 ns 14.366 ms 3.507 µs

Pinned within 2 % while the work grows 64×. One and eight launches are unchanged; the single-pass regime was already right. 3.5 µs per launch matches what CUDA measures on the same card. WGSL is the same shape.

cubecl-wgpu was not instantiating testgen_profiling!, which is why this survived; it is wired in here, and test_window_spans_every_pass_in_it reports a growth ratio of 0.76 on the unfixed code while both pre-existing profiling tests pass.

Green on cubecl-wgpu --features spirv (746 + 17), on lavapipe as a CI stand-in, and on CUDA against origin/main (same 17 pre-existing failures both sides, one-test delta is the new test).

Known, not fixed here

  • A cached zero outlives this fix: the throughput store has no probe-behaviour version, so a machine that recorded one keeps serving it until the cache is keyed properly.
  • roofline_bounds still charges an unmeasurable launch as free; that wants Bounds::launch_overhead to become optional.
  • Once Metal's 28-set budget is exhausted a new profile can open in a set an older live one started in. The QuerySetAllocator doc is corrected to say so; fixing it needs the allocator to know which sets are live.
  • HIP and Metal not run. Both pick up the new test; neither is touched by this change.

Validate your PR with burn.

It is important that you make sure that you don't introduce any bugs in burn.

Instructions

  • Create a new branch or fork of the cubek repo
  • Update the main Cargo.toml with this PR hash.
  • Fix any broken tests or compilation errors in cubek.
  • Submit a PR in cubek with your fixes and link it here.
  • Create a new branch or fork of the burn repo
  • Update the main Cargo.toml with this PR and the cubek PR hash.
  • Fix any broken tests or compilation errors in burn.
  • Submit a PR in burn with your fixes and link it here.

A profiled region gets timestamp writes only on the pass that drains its
token, so `stop_profile_setup` resolved the end from the same query set the
start came from and reported the first pass alone. Every later pass of the
region carries no timestamp at all.

Each pass of a live profile now rewrites the end slot of the profile's own
query set, so the last one to run marks the end. One set per profile either
way: the query-set budget and its round-robin are untouched.

The launch-overhead throughput probe is what this surfaced on. Its window
stayed pinned near 24 us whatever the loop length, and the benchmarker's
warmup, seeing a duration that never grows, drove the count to hundreds of
thousands; the constant divided by that count underflowed `Duration` to
exactly zero, which autotune read as launches being free on every wgpu
backend.

Closes #1421
@ThierryCantin-Demers
ThierryCantin-Demers marked this pull request as ready for review September 2, 2026 18:32
@ThierryCantin-Demers
ThierryCantin-Demers merged commit f6a3eef into main Sep 3, 2026
6 checks passed
@ThierryCantin-Demers
ThierryCantin-Demers deleted the fix/wgpu-profile-passes branch September 3, 2026 17:36
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.

wgpu Device timing: client.profile() measures only the first compute pass — multi-pass profiles silently under-report

2 participants