Skip to content

[DSv4][P5-5] Shared Expert MLP: strict CUDA + Triton kernels - #387

Open
jyizheng wants to merge 4 commits into
RL-Align:dsv4-p5-devfrom
jyizheng:ajiao/p5-5-shared-expert-mlp
Open

[DSv4][P5-5] Shared Expert MLP: strict CUDA + Triton kernels#387
jyizheng wants to merge 4 commits into
RL-Align:dsv4-p5-devfrom
jyizheng:ajiao/p5-5-shared-expert-mlp

Conversation

@jyizheng

@jyizheng jyizheng commented Sep 4, 2026

Copy link
Copy Markdown

Implements shared_expert_mlp_fwd/bwd (#64) per the P5-S0 contract: every valid token runs fc1 -> one-round SwiGLU -> fc2 on BF16 frozen weights; backward returns dX only (FP32 accumulator), and the shared output stays independent of the routed path (combine belongs to P6).

Both backends reproduce oracle-fp32-serial-v1 byte-for-byte on the same device: one lane owns one output element and reduces serially in ascending k, multiply and add rounded separately (__fmul_rn/__fadd_rn on CUDA, libdevice mul_rn/add_rn on Triton). No cross-lane floating-point reduction exists, so results are batch/padding invariant by construction. The one-round SwiGLU core ships in shared mode (p_s = None, no clamp, per S0 decision D6) as the reuse point for P5-2 (#63).

Acceptance (B300 / sm_103, torch 2.14+cu130)

check_p5.py --provider ...:CudaSharedExpertProvider   --device cuda  ->  PASS (all boundaries byte-equal)
check_p5.py --provider ...:TritonSharedExpertProvider --device cuda  ->  PASS (all boundaries byte-equal)
pytest tests/test_shared_expert_mlp.py                               ->  14 passed

Tests cover: fixture byte-equality (shared_t1/shared_t16), batch/padding invariance (fwd(x)[t] == fwd(x[t:t+1]) per row), no dW (weight hashes stable, no grad), shared/routed boundary independence, CUDA/Triton cross-backend byte-equality at (T=256, H=1024, F=512), and fail-closed behavior (CPU input / missing backend / foreign numeric profile raise; no silent oracle fallback).

Benchmark (torch-native vs Triton vs CUDA, H=4096 F=2048, fwd+bwd ms)

T torch-native (cuBLAS, non-deterministic) Triton strict CUDA strict
16 0.212 2.08 2.05
256 0.193 24.4 25.2
2048 0.233 191 199

The strict backends are asserted byte-equal to each other on every shape. The gap vs cuBLAS is the cost of the serial one-lane-per-element reduction; epilogue/perf work is the P5 step-10 follow-up, out of scope here.

Notes

  • Sigmoid is transcendental, so its bits follow the libm implementation: nvcc expf bit-matches torch.sigmoid (0/4M mismatches on a device probe) while tl.exp (exp2-based) and libdevice __nv_expf do not (~45% / ~10% of values off by 1 ulp). The CUDA kernel computes 1/(1+expf(-x)) inline; the Triton path takes torch.sigmoid(gate) as a kernel input and fuses the remaining SwiGLU math.
  • All Triton arithmetic goes through libdevice *_rn: the compiler may contract a*b+c into an FMA, which showed up as a 1-ulp dgate drift on 2/262144 elements at T=256. A cross-backend byte-equality test at that size locks the regression in.
  • provenance() records split_k=1, reduction=serial-ascending-k, no-FMA rounding, per the P5-5 provenance requirement.

…n#64)

Implements shared_expert_mlp_fwd/bwd per the P5-S0 contract: every valid
token runs fc1 -> one-round SwiGLU -> fc2 on BF16 frozen weights, backward
returns dX only (FP32 accumulator), and the shared output stays independent
of the routed path.

Both backends reproduce the FP32 oracle's numeric profile
oracle-fp32-serial-v1 byte-for-byte on the same device: one lane owns one
output element and reduces serially in ascending k, multiply and add rounded
separately (__fmul_rn/__fadd_rn on CUDA, uncontracted IEEE fp32 in Triton),
sigmoid computed as 1/(1+expf(-x)) to match torch.sigmoid on FP32 CUDA
tensors. No cross-lane floating-point reduction exists anywhere, so results
are batch/padding invariant by construction (fwd(x)[t] == fwd(x[t:t+1])
byte-equal). The one-round SwiGLU core runs in shared mode (p_s=None, no
clamp, per S0 decision D6) and is the reuse point for P5-2 (RL-Align#63).

Providers subclass ReferenceProvider and override only the two shared-expert
methods, so the full acceptance command runs unchanged; unsupported input
(non-CUDA device, missing extension or triton, foreign numeric profile)
raises instead of falling back (fail-closed). Provenance records split_k=1 /
serial-ascending-k / no-FMA per the P5-5 provenance requirement.

Acceptance:
  python scripts/check_p5.py --provider rl_engine.moe.backends.shared_expert:CudaSharedExpertProvider --device cuda
  python scripts/check_p5.py --provider rl_engine.moe.backends.shared_expert:TritonSharedExpertProvider --device cuda
  pytest tests/test_shared_expert_mlp.py
  python benchmarks/benchmark_shared_expert_mlp.py
tl.exp is the fast exp2-based path and does not bit-match torch.sigmoid;
libdevice __nv_expf does (0/4M mismatches on the device probe).
Neither tl.exp (exp2-based) nor libdevice __nv_expf bit-matches the nvcc
expf inside torch.sigmoid (~45% / ~10% of fp32 values differ by 1 ulp); the
tiny fixtures passed only because the BF16 round absorbed the difference,
and the T=256 benchmark cross-check caught the divergence. The Triton path
now takes torch.sigmoid(gate) as a kernel input and fuses the remaining
SwiGLU math; a (256, 1024, 512) cross-backend byte-equality test locks the
regression in.
… _rn ops

The compiler may contract a * b + c into an FMA; at T=256 that rounded
dsilu = sig * (1 + g * (1 - sig)) differently on 2/262144 dgate elements
(1 ulp after the BF16 round). All mul/add/sub in the Triton strict GEMM and
SwiGLU kernels now go through libdevice add_rn/mul_rn/sub_rn, the exact
Triton spelling of the CUDA kernel's __fadd_rn/__fmul_rn/__fsub_rn.
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 539c9dfe-3cff-4d68-ab08-6223fdacbd6b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@Flink-ddd Flink-ddd added DSv4 deepseek-P5 platform: cuda Specific optimizations or bugs in NVIDIA graphics cards (such as FlashInfer, TMA optimizations) labels Sep 5, 2026

@KJLdefeated KJLdefeated left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clean and clear implementations. I pulled the branch onto dsv4-p5-dev and verified both backends on an H100 (torch 2.14+cu130, Triton 3.8). CUDA is byte-equal to the oracle on both fixtures, the T=256 cross-backend check, and a 2M-element SwiGLU sweep, and backward is batch-invariant too. Good Work!

Some issues about performance:
The performance is 1000x slower than cublas. I think there is still room for optimize.
Can you reference to Det GEMM to see if we can reuse this kernel or reference this design?
Note that reusing it may not align with oracle I provided, but we can ignore it for now.
Our goal is ensure batch-invariance and also high performance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deepseek-P5 DSv4 platform: cuda Specific optimizations or bugs in NVIDIA graphics cards (such as FlashInfer, TMA optimizations)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants