Add stochastic rounding and philox RNG under rocdl - #907
Conversation
There was a problem hiding this comment.
Pull request overview
Adds stochastic rounding support for FP32→BF16 and a small Philox-based counter RNG helper to provide reproducible random bits inside kernels, along with unit/device tests validating correctness and statistical properties.
Changes:
- Implement
philox_4x32(counter, key)andstochastic_round_bf16(x, rand)inpython/flydsl/expr/numeric.py. - Re-export these helpers via
flydsl.expr(throughpython/flydsl/expr/typing.py) so they’re reachable asfx.philox_4x32/fx.stochastic_round_bf16. - Add tests covering unbiased rounding math (CPU) and device behavior/uniformity (GPU).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/unit/test_stochastic_rounding.py | Adds L0 unbiasedness checks and L2 GPU tests for stochastic rounding + Philox RNG |
| python/flydsl/expr/typing.py | Re-exports the new helpers from expr.numeric through the fx public surface |
| python/flydsl/expr/numeric.py | Implements Philox 4x32 RNG and BF16 stochastic rounding helper |
Comments suppressed due to low confidence (2)
tests/unit/test_stochastic_rounding.py:58
- To actually exercise the public re-export, call the helpers via
fx.*(not via a direct import). This ensurestyping.__all__/expr.__init__re-exporting stays covered.
g = fx.block_idx.x * BLOCK + fx.thread_idx.x
r = philox_4x32(fx.Uint32(g), fx.Uint32(seed))[0]
fx.memref_store(stochastic_round_bf16(x, r), Out, g)
tests/unit/test_stochastic_rounding.py:90
- Same as above: use
fx.philox_4x32so the test covers the documentedfx.*entry point.
g = fx.block_idx.x * BLOCK + fx.thread_idx.x
fx.memref_store(fx.Int32(philox_4x32(fx.Uint32(g), fx.Uint32(seed))[0]), Out, g)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Are |
|
thanks @sjfeng1999 agree it does feel adhoc, Philox is a mature, widely used API: it is the generator, exposed as similarly, stochastic rounding is a standard technique rather than a single library function name. In practice it appears as a rounding mode of a narrowing conversion: PTX I could reorganize as:
does that direction work for you? |
I add standard rounding mode support in #916. I found that |
|
thanks checking |
|
Makes sense, thanks for adding #916. Agreed that STOCHASTIC does not fit the target-neutral I moved |
1bf7b66 to
1c6f5fb
Compare
|
@sjfeng1999 ready for a review |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
python/flydsl/expr/rocdl/random.py:71
stochastic_round_bf16currently perturbs the low 16 bits for all inputs; for NaNs this can carry into higher mantissa/exponent bits, so NaN payload (and in extreme cases the exponent) is not preserved. This contradicts the docstring claim that NaNs pass through unchanged and can make NaN handling non-deterministic.
bf16 shares the f32 exponent field, so Inf and NaN pass through unchanged
and no special-case handling is needed.
"""
u = Float32(x).bitcast(Uint32) + (Uint32(rand) & Uint32(0xFFFF))
return Uint16(u >> Uint32(16)).bitcast(BFloat16)
python/flydsl/expr/rocdl/random.py:27
- The PR description says this matches Random123 / NumPy / cuRAND Philox4_32_10, but the default here is 7 rounds. With the default, callers using
philox_4x32(counter, key)will not reproduce those reference generators.
This issue also appears on line 67 of the same file.
def philox_4x32(counter, key, rounds: int = 7):
tests/unit/test_stochastic_rounding.py:41
- This test (and the PR description) mentions NaN pass-through, but the current parameterization excludes NaN and the assertions assume the usual round-up count property. If
stochastic_round_bf16is meant to keep NaNs deterministic, add an explicit NaN case with a pass-through assertion.
@pytest.mark.parametrize("x", [1.0, 1.001953125, 3.14159, 1e-3, float("inf"), float("-inf")])
def test_stochastic_round_bf16_unbiased(x):
e74fd1c to
4d1431b
Compare
|
Main thing in the latest commit: random is a standalone library now (expr/random/, universal impl plus an optional per-target override). We should settle on what the complete API should look like before adding more one-off functions. Are these six |
|
Thanks, this is much nicer than what I had. The universal + per-target override split is clean, and the I pushed 37bfa99 on top: it amortizes the Philox draw across the epilogue block (one call per 8 outputs using all four words, instead of one draw per element that used a single word), which was most of the RS-path RNG cost. On the six functions: for stochastic rounding I only need raw bits, so Two smaller thoughts. I'd keep |
Adds stochastic rounding and a Philox counter RNG, placed under the rocdl target package rather than as top-level helpers, following the discussion on #907.
Stochastic rounding does not fit the target-neutral IEEE rounding modes that #916 added to
.to()(lowered througharith.truncf): it needs entropy and the hardware form is a target-specific instruction (CDNAv_cvt_*_sr, PTXcvt.rs). So it lives underrocdl/as a target-specific function. The f32 to bf16 path here is a portable software emulation; a CDNA hardwaresrvariant can slot in alongside it later.New API:
fx.rocdl.philox_4x32(counter, key)is the Random123 Philox 4x32 counter RNG (same generator asnumpy.random.Philox, cuRANDPhilox4_32_10, Tritontl.rand), returning four reproducible random words from a global index and a seed.fx.rocdl.stochastic_round_bf16(x, rand)perturbs the 16 mantissa bits bf16 discards with the random bits before truncating, so the probability of rounding up equals the fractional distance to the next bf16 value. Inf and NaN pass through unchanged.Consumer:
The RDNA WMMA GEMM (gfx11 and gfx12) gains a
roundingoption, default"rn"so nothing changes. Withrounding="rs"the f32 accumulator converts to bf16 through stochastic rounding, keyed per element from a runtimesr_seedlaunch argument.Tests:
The rounding is proven exactly unbiased in pure Python and covers Inf and NaN. Device tests confirm outputs land on the two nearest bf16 values with the right up probability and a matching mean, and that the RNG is uniform and collision free. The GEMM test checks the stochastic path stays within tolerance of round-to-nearest, varies with the runtime seed, and is reproducible for a fixed seed.