-
Notifications
You must be signed in to change notification settings - Fork 63
feat(dispatch_combine): add blockwise FP4 (E2M1) combine transport #461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
4b88ace
0589070
c7548ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -264,10 +264,14 @@ __global__ void EpDispatchIntraNodeKernel(EpDispatchCombineArgs<T> args) { | |
| /* ---------------------------------------------------------------------------------------------- */ | ||
| template <typename T, bool UseP2PRead = true, bool EnableStdMoE = false, | ||
| bool UseFp8DirectCast = false, bool UseFp8BlockwiseQuant = false, bool UseWeights = true, | ||
| int Vec8Top8BlockElems = 0, int Vec8AccumNum = 8> | ||
| int Vec8Top8BlockElems = 0, int Vec8AccumNum = 8, bool UseFp4Combine = false> | ||
| __device__ __forceinline__ void EpCombineIntraNodeKernel_body(EpDispatchCombineArgs<T> args) { | ||
| using TokT = | ||
| std::conditional_t<UseFp8DirectCast || UseFp8BlockwiseQuant, core::CombineInternalFp8, T>; | ||
| // UseFp4Combine reuses the FP8-blockwise staging/scale layout but transports each element as | ||
| // packed FP4 (E2M1, 2/byte -> half the combine bytes). It is a variant of blockwise combine. | ||
| static_assert(!UseFp4Combine || UseFp8BlockwiseQuant, | ||
| "UseFp4Combine builds on the FP8-blockwise combine path"); | ||
| static_assert(!(UseFp8DirectCast && UseFp8BlockwiseQuant), | ||
| "Fp8 direct cast and blockwise quant are mutually exclusive"); | ||
| static_assert((!UseFp8DirectCast && !UseFp8BlockwiseQuant) || std::is_same_v<T, hip_bfloat16>, | ||
|
|
@@ -358,10 +362,17 @@ __device__ __forceinline__ void EpCombineIntraNodeKernel_body(EpDispatchCombineA | |
| uint8_t* destStagingPtr = args.intraNodeTokBufs.combineInp->template GetAs<uint8_t*>(destPe) + | ||
| SendBufSlotOffset(config, myPe, destLocalTokId) * combXferBytes; | ||
| if constexpr (UseFp8BlockwiseQuant) { | ||
| core::WarpQuantizeToFp8Blockwise<core::CombineInternalFp8>( | ||
| reinterpret_cast<core::CombineInternalFp8*>(destStagingPtr), | ||
| reinterpret_cast<float*>(destStagingPtr + hiddenBytes), | ||
| args.inpTokenBuf + tokenIdx * hiddenDim, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| if constexpr (UseFp4Combine) { | ||
| core::WarpQuantizeToFp4Blockwise<core::CombineInternalFp8>( | ||
| reinterpret_cast<core::CombineInternalFp8*>(destStagingPtr), | ||
| reinterpret_cast<float*>(destStagingPtr + hiddenBytes), | ||
| args.inpTokenBuf + tokenIdx * hiddenDim, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| } else { | ||
| core::WarpQuantizeToFp8Blockwise<core::CombineInternalFp8>( | ||
| reinterpret_cast<core::CombineInternalFp8*>(destStagingPtr), | ||
| reinterpret_cast<float*>(destStagingPtr + hiddenBytes), | ||
| args.inpTokenBuf + tokenIdx * hiddenDim, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| } | ||
| } else if constexpr (!std::is_same_v<T, TokT> && | ||
| std::is_same_v<TokT, core::CombineInternalFp8>) { | ||
| core::WarpCastBf16ToCombineInternalFp8<T>(reinterpret_cast<TokT*>(destStagingPtr), | ||
|
|
@@ -396,10 +407,17 @@ __device__ __forceinline__ void EpCombineIntraNodeKernel_body(EpDispatchCombineA | |
| uint8_t* destStagingPtr = args.intraNodeTokBufs.combineInp->template GetAs<uint8_t*>(destPe) + | ||
| SendBufSlotOffset(config, myPe, destLocalTokId) * combXferBytes; | ||
| if constexpr (UseFp8BlockwiseQuant) { | ||
| core::WarpQuantizeToFp8Blockwise<core::CombineInternalFp8>( | ||
| reinterpret_cast<core::CombineInternalFp8*>(destStagingPtr), | ||
| reinterpret_cast<float*>(destStagingPtr + hiddenBytes), | ||
| args.inpTokenBuf + tokenIdx * hiddenDim, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| if constexpr (UseFp4Combine) { | ||
| core::WarpQuantizeToFp4Blockwise<core::CombineInternalFp8>( | ||
| reinterpret_cast<core::CombineInternalFp8*>(destStagingPtr), | ||
| reinterpret_cast<float*>(destStagingPtr + hiddenBytes), | ||
| args.inpTokenBuf + tokenIdx * hiddenDim, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| } else { | ||
| core::WarpQuantizeToFp8Blockwise<core::CombineInternalFp8>( | ||
| reinterpret_cast<core::CombineInternalFp8*>(destStagingPtr), | ||
| reinterpret_cast<float*>(destStagingPtr + hiddenBytes), | ||
| args.inpTokenBuf + tokenIdx * hiddenDim, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| } | ||
| } else if constexpr (!std::is_same_v<T, TokT> && | ||
| std::is_same_v<TokT, core::CombineInternalFp8>) { | ||
| core::WarpCastBf16ToCombineInternalFp8<T>(reinterpret_cast<TokT*>(destStagingPtr), | ||
|
|
@@ -537,33 +555,71 @@ __device__ __forceinline__ void EpCombineIntraNodeKernel_body(EpDispatchCombineA | |
| MORI_TRACE_NEXT(seq, Slot::CombineDequantAccum); | ||
| if constexpr (Vec8Top8BlockElems != 0) { | ||
| if (mwIter.warpsPerItem == 1) { | ||
| core::WarpAccumFp8DequantFullBlockVec8Top8<T, core::CombineInternalFp8, | ||
| Vec8Top8BlockElems, Vec8AccumNum>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), hiddenDim); | ||
| if constexpr (UseFp4Combine) { | ||
| core::WarpAccumFp4DequantFullBlockVec8Top8<T, core::CombineInternalFp8, | ||
| Vec8Top8BlockElems, Vec8AccumNum>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), hiddenDim); | ||
| } else { | ||
| core::WarpAccumFp8DequantFullBlockVec8Top8<T, core::CombineInternalFp8, | ||
| Vec8Top8BlockElems, Vec8AccumNum>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), hiddenDim); | ||
| } | ||
| } else if ((hiddenDimOffset & 0x7) == 0 && (hiddenDimSize & 0x7) == 0) { | ||
| core::WarpAccumFp8DequantSegmentBlockVec8Top8<T, core::CombineInternalFp8, | ||
| Vec8Top8BlockElems, Vec8AccumNum>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), hiddenDimOffset, hiddenDimSize); | ||
| if constexpr (UseFp4Combine) { | ||
| core::WarpAccumFp4DequantSegmentBlockVec8Top8<T, core::CombineInternalFp8, | ||
| Vec8Top8BlockElems, Vec8AccumNum>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), hiddenDimOffset, | ||
| hiddenDimSize); | ||
| } else { | ||
| core::WarpAccumFp8DequantSegmentBlockVec8Top8<T, core::CombineInternalFp8, | ||
| Vec8Top8BlockElems, Vec8AccumNum>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), hiddenDimOffset, | ||
| hiddenDimSize); | ||
| } | ||
| } else { | ||
| // Misaligned segment: vec8 helper would fault on the load. Tiny scalar fallback. | ||
| core::WarpAccumFp8DequantSegmentScalarTop8<T, core::CombineInternalFp8, | ||
| Vec8Top8BlockElems, Vec8AccumNum>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), hiddenDimOffset, hiddenDimSize); | ||
| if constexpr (UseFp4Combine) { | ||
| core::WarpAccumFp4DequantSegment<T, core::CombineInternalFp8>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), Vec8AccumNum, hiddenDimOffset, | ||
| hiddenDimSize, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| } else { | ||
| core::WarpAccumFp8DequantSegmentScalarTop8<T, core::CombineInternalFp8, | ||
| Vec8Top8BlockElems, Vec8AccumNum>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), hiddenDimOffset, | ||
| hiddenDimSize); | ||
| } | ||
| } | ||
| } else { | ||
| if (mwIter.warpsPerItem == 1) { | ||
| core::WarpAccumFp8DequantFull<T, core::CombineInternalFp8>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), validAccumCount, hiddenDim, | ||
| args.fp8BlockwiseCombineScaleDim); | ||
| if constexpr (UseFp4Combine) { | ||
| core::WarpAccumFp4DequantFull<T, core::CombineInternalFp8>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), validAccumCount, hiddenDim, | ||
| args.fp8BlockwiseCombineScaleDim); | ||
| } else { | ||
| core::WarpAccumFp8DequantFull<T, core::CombineInternalFp8>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), validAccumCount, hiddenDim, | ||
| args.fp8BlockwiseCombineScaleDim); | ||
| } | ||
| } else { | ||
| core::WarpAccumFp8DequantSegment<T, core::CombineInternalFp8>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), validAccumCount, hiddenDimOffset, | ||
| hiddenDimSize, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| if constexpr (UseFp4Combine) { | ||
| core::WarpAccumFp4DequantSegment<T, core::CombineInternalFp8>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), validAccumCount, | ||
| hiddenDimOffset, hiddenDimSize, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| } else { | ||
| core::WarpAccumFp8DequantSegment<T, core::CombineInternalFp8>( | ||
| outPtr, reinterpret_cast<const core::CombineInternalFp8* const*>(srcPtrs), | ||
| reinterpret_cast<const float* const*>(srcScalePtrs), validAccumCount, | ||
| hiddenDimOffset, hiddenDimSize, hiddenDim, args.fp8BlockwiseCombineScaleDim); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. duplicate every call. is possilble for any helper function? |
||
| } | ||
| } | ||
| } else if constexpr (!std::is_same_v<T, TokT> && | ||
|
|
@@ -590,10 +646,10 @@ __device__ __forceinline__ void EpCombineIntraNodeKernel_body(EpDispatchCombineA | |
|
|
||
| template <typename T, bool UseP2PRead = true, bool EnableStdMoE = false, | ||
| bool UseFp8DirectCast = false, bool UseFp8BlockwiseQuant = false, bool UseWeights = true, | ||
| int Vec8Top8BlockElems = 0, int Vec8AccumNum = 8> | ||
| int Vec8Top8BlockElems = 0, int Vec8AccumNum = 8, bool UseFp4Combine = false> | ||
| __global__ void EpCombineIntraNodeKernel(EpDispatchCombineArgs<T> args) { | ||
| EpCombineIntraNodeKernel_body<T, UseP2PRead, EnableStdMoE, UseFp8DirectCast, UseFp8BlockwiseQuant, | ||
| UseWeights, Vec8Top8BlockElems, Vec8AccumNum>(args); | ||
| UseWeights, Vec8Top8BlockElems, Vec8AccumNum, UseFp4Combine>(args); | ||
| } | ||
|
|
||
| } // namespace moe | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -623,6 +623,11 @@ def check_combine_result( | |
| elif getattr(self.config, "quant_type", "none") == "fp8_blockwise": | ||
| # FP8 E4M3 quantization can exceed 5% after combine accumulation. | ||
| atol, rtol = 7e-2, 7e-2 | ||
| elif getattr(self.config, "quant_type", "none") == "fp4_blockwise": | ||
| # FP4 (E2M1) is far coarser (~1 mantissa bit); blockwise-scaled + summed over | ||
| # top-k experts stays within a loose bound. This guards against gross corruption | ||
| # (wrong packing/indexing) rather than asserting FP8-level precision. | ||
| atol, rtol = 2.5e-1, 2.5e-1 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please help to comfirm that atol,rtol=0.25 is actually right? |
||
| result_match = torch.allclose( | ||
| got.float(), expected.float(), atol=atol, rtol=rtol | ||
| ) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
selection is arch-blind. On non-gfx950 this still picks _fp4bwq and silently falls to the software path. Please fail-fast (mirror the fp8_direct_cast arch guard).