Add stochastic rounding and philox RNG helpers - #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? |
Adds stochastic rounding for f32 to bf16, plus a small counter-based RNG to feed it, and wires it into the RDNA bf16 GEMM epilogue as the first user. FlyDSL had no stochastic rounding of any kind, which matters for low-precision accumulation and training where round-to-nearest is biased.
Primitives:
stochastic_round_bf16(x, rand)perturbs the 16 mantissa bits that bf16 discards with the supplied random bits before truncating, so the probability of rounding up equals the fractional distance to the next bf16 value. bf16 shares the f32 exponent, so Inf and NaN pass through unchanged and no special casing is needed.philox_4x32(counter, key)is a counter-based PRNG returning four random words, so a kernel can draw reproducible randomness from a global index and a seed without carrying RNG state.Both are reachable as
fx.stochastic_round_bf16andfx.philox_4x32.Consumer:
The RDNA WMMA GEMM (gfx11 and gfx12 variants) gains a
roundingoption, defaulting to"rn"so nothing changes. Withrounding="rs"the f32 accumulator converts to bf16 through stochastic rounding, keyed per output element fromsr_seed, instead of round-to-nearest.Tests:
The rounding is proven exactly unbiased in pure Python (over the full 16-bit random range the number of round-ups equals the discarded low bits) 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-rounding path stays within tolerance of round-to-nearest, changes with the seed, and is reproducible for a fixed seed.