Skip to content

fix: fixed-order fp32 tree reductions (zerfoo T135.2)#175

Merged
dndungu merged 4 commits into
mainfrom
wave-1-task-T135.2
Jul 3, 2026
Merged

fix: fixed-order fp32 tree reductions (zerfoo T135.2)#175
dndungu merged 4 commits into
mainfrom
wave-1-task-T135.2

Conversation

@dndungu

@dndungu dndungu commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

zerfoo T135.2 / UC-H2-003, UC-GH-5 -- fixed-order fp32 tree reductions

Audits every CPU reduction in ztensor for accumulation order and dtype and
converts the naive left-to-right folds to fixed-order pairwise (tree)
accumulation. The tree shape is a pure function of the reduced length (recursive
halving snapped to a 128-element base block, numpy-style), so results are
bitwise-identical across GOMAXPROCS and run to run, and agreement with the
PyTorch oracle tightens. Accumulation dtype is preserved per site (no narrowing).

Reductions converted

Op File Before After Acc dtype
Sum/ReduceSum (all-axis) compute/cpu_engine.go naive fold in T pairwise in T T (fp32)
Sum/ReduceSum (per-axis stripe) compute/cpu_engine.go naive fold in T pairwise in T T (fp32)
ReduceMean compute/cpu_engine.go via Sum inherits pairwise T (fp32)
Softmax denominator (generic path) compute/cpu_engine.go naive fold in T pairwise in T T (fp32)
RMSNorm mean(x^2) (non-arm64 fallback) internal/xblas/rmsnorm_generic.go naive fp32 fold pairwise fp32 fp32
Arithmetic.Sum (float32/float64) numeric/native_ops.go naive fold pairwise in-type

Accuracy (measured, float32 vs Kahan-float64 ground truth)

Relative-error tightening; no input regresses:

  • ReduceSum: 16.6x (n=1024) -> 941.6x (n=65536)
  • RMSNorm sum-of-squares: 4.2x (d=2048) -> 24.5x (d=8192)
  • Softmax denominator: up to 52x (n=4096); equal for small n that fits one base block

Explicitly NOT converted (feeds T135.5 ZTENSOR_DETERMINISTIC / out of scope)

  • CUDA reduction kernels (internal/cuda/kernels/*.cu: rmsnorm, fused_softmax_vmul, flash_*, reductions using atomicAdd/warp-shuffle): cannot build/run on the darwin dev host; GPU priority held by a concurrent task this wave. Cross-block atomicAdd order is the real run-to-run nondeterminism source -> T135.5.
  • arm64 NEON RMSNormF32 (internal/xblas/rmsnorm_arm64.s): already fixed-order fp32 (8-way strided + horizontal reduce), deterministic; a pairwise/float64 SIMD rewrite is deferred. This is the GB10 (aarch64) production path, so the RMSNorm oracle diff is unchanged by this PR.
  • xblas SoftmaxF32 (softmax_generic.go): already accumulates the denominator in float64 (single sequential loop, deterministic); float64 naive error (~1e-16*n) is orders below the fp32 oracle tolerance, so no change needed.
  • float16/bfloat16 Arithmetic.Sum: left pairwise-in-type; widening to a float32 accumulator (aten AccumulateType) deferred to T135.5.
  • MatMul/GEMV dot-products, flash-decode online-softmax: reduction-class but outside the T3.2 op list (perf-critical GEMM; flash-decode owned by the T133.x cluster) and already float64 where hot.
  • Gradient-clipping norms: not present in ztensor (they live in zerfoo training/); flagged for a zerfoo-side follow-up.

Tests (run on Linux CI -- authoritative)

  • compute/reduce_pairwise_test.go: base-case == naive for n<=block; run-to-run determinism; tighter-than-naive vs float64 ref; ReduceSum bitwise-identical across GOMAXPROCS 1/2/4/8; softmax determinism.
  • numeric/reduce_pairwise_test.go, internal/xblas/reduce_pairwise_test.go: determinism + tightening + RMSNorm scale accuracy.

Docs: reduction accumulation policy added to docs/design.md.

Local verification limited to go build/go vet/golangci-lint + a standalone numeric proof: ztensor test binaries SIGSEGV at cuda device-init on the darwin host (ztensor#171, being fixed concurrently). GB10 oracle sweep recorded as follow-up evidence for T135.3.

dndungu added 4 commits July 2, 2026 19:52
Float32Ops.Sum and Float64Ops.Sum now accumulate in a recursive pairwise
(tree) order whose split points depend only on length, not on chunking.
Bitwise-stable run to run and O(log n) rather than O(n) rounding error,
without narrowing the accumulation dtype. Verifies UC-H2-003 / UC-GH-5.
…135.2)

Non-arm64 RMSNormF32 fallback now sums x^2 in fixed-order pairwise float32,
matching the fp32 accumulation of the arm64 NEON path while shrinking the
naive-fold error. The arm64 SIMD path is already fixed-order fp32 (8-way
strided) and is unchanged. Verifies UC-GH-5.
…foo T135.2)

Sum/ReduceSum/ReduceMean (via Sum) and the Softmax denominator now use a
fixed-order pairwise tree reduction (compute/reduce_pairwise.go). The
accumulation order is a pure function of the reduced length, so results are
bitwise-identical across GOMAXPROCS and run to run, and agreement with the
PyTorch oracle tightens (16-940x lower float32 relerr for ReduceSum) with no
dtype change. Verifies UC-H2-003 / UC-GH-5.
@dndungu dndungu merged commit 8bd3074 into main Jul 3, 2026
1 check passed
@dndungu dndungu deleted the wave-1-task-T135.2 branch July 3, 2026 02:55
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.

1 participant