[Pytorch] Add support for row-wise quanted input for grouped gemm - #3244
[Pytorch] Add support for row-wise quanted input for grouped gemm#3244YangFei1990 wants to merge 15 commits into
Conversation
Signed-off-by: YangFei1990 <feiw@nvidia.com>
| with_columnwise: bool, | ||
| tensor_offsets: Optional[torch.Tensor] = None, | ||
| ) -> None: | ||
| """Prepare a rowwise-only MXFP8 grouped input for grouped GEMM (in place). |
There was a problem hiding this comment.
Looks like this helper actually "prepares" colwise besides rowwise. I think we should make the function name and docstring clearer and more consistent.
| raise ValueError("Pre-quantized MXFP8 grouped input is missing rowwise data.") | ||
| if grouped_x._with_gemm_swizzled_scales: | ||
| raise NotImplementedError( | ||
| "Pre-quantized MXFP8 grouped input must have scales in compact format." |
| scale_shape = ( | ||
| round_up_to_nearest_multiple(total_tokens, 128), | ||
| round_up_to_nearest_multiple(cols // 32, 4), | ||
| ) |
There was a problem hiding this comment.
Do we need to do this while we already have 128 per-expert-alignment in Dispatch? The total tokens should already be divisible by 128.
For hidden size, there will be an assertion in Dispatch to expect % 512 = 0.
Here, I think we can add assertions instead.
|
|
||
| def prepare_prequantized_mxfp8_grouped_input( | ||
| grouped_x: GroupedTensorStorage, | ||
| quantizer: MXFP8Quantizer, |
There was a problem hiding this comment.
Actually, is there a GroupedQuantizer in TE/PyT? If so, we probably need to use it instead.
There was a problem hiding this comment.
I do not see a GroupedQuantizer in Pytorch. I see what used is tex.group_quantize. Let me know if I missed it. Also can you help me understand the benefit of GroupedQuantizer and how it compared with current Pytorch approach?
Signed-off-by: YangFei1990 <feiw@nvidia.com>
Signed-off-by: YangFei1990 <feiw@nvidia.com>
Greptile SummaryAdds row-wise prequantized MXFP8 support for grouped GEMM.
Confidence Score: 4/5The PR is not yet safe to merge because fused grouped-MLP forward still asserts when expert weights are frozen but another input requires gradients. The frozen-weight fused path creates only rowwise GEMM storage, while forward context preparation still passes that tensor to a helper that requires columnwise storage; the corresponding test configuration is skipped. Files Needing Attention: transformer_engine/pytorch/ops/fused/grouped_mlp.py, tests/pytorch/test_grouped_mlp.py Important Files Changed
Sequence DiagramsequenceDiagram
participant Dispatch as FP8 token dispatch
participant Op as Grouped Linear / MLP
participant Native as TE native bindings
participant GEMM as Grouped GEMM
Dispatch->>Op: Rowwise-only MXFP8 GroupedTensor
Op->>Native: Swizzle rowwise scales
alt Weight gradients required
Native->>Native: Dequantize and columnwise-requantize
end
Op->>GEMM: GEMM-ready grouped tensor
GEMM-->>Op: Forward or backward result
Reviews (8): Last reviewed commit: "Merge branch 'main' into mxfp8_input_gro..." | Re-trigger Greptile |
Signed-off-by: YangFei1990 <feiw@nvidia.com>
Signed-off-by: YangFei1990 <feiw@nvidia.com>
Signed-off-by: YangFei1990 <feiw@nvidia.com>
|
/te-ci pytorch |
| requires_grad=False, | ||
| with_gemm_swizzled_scales=False, | ||
| ) | ||
| tex.swizzle_scales_for_gemm_(tmp) |
There was a problem hiding this comment.
It feels like we are swizzling as if the activation is a dense tensor, which is only valid is the activation has tokens-per-expert padded to 128 multiple or higher (like 256).
How are we handling the swizzle of columnwise scales?
There was a problem hiding this comment.
Yes this is the precondition, that we require the input dimension (both row and col) to be 128 multiple or higher. While I built in this way, please let me know if we should handle padding here.
There was a problem hiding this comment.
@zhongbozhu I think the group_quantize + swizzle fusion generates the swizzled scales for columnwise.
There was a problem hiding this comment.
what you are doing is essentially this right?
group_quantize_colwise_only_swizzle_fused( group_dequantize( mxfp8_input_compact_scales ) )
It would be better if we have a fused kernel for this process, is this already planned?
| ) | ||
|
|
||
|
|
||
| def prepare_prequantized_mxfp8_input_for_gemm( |
There was a problem hiding this comment.
This function needs to be heavily unit tested like here:
| dbias = None | ||
| dequantized = None | ||
| if with_columnwise or with_dbias or with_dequantized: | ||
| dequantized = tex.group_dequantize(grouped_x, TE_DType[dtype]).rowwise_data.view( |
There was a problem hiding this comment.
Does group dequantize work with already swizzled scales? Would be better if we put an assert here.
There was a problem hiding this comment.
We have an assert above in line 177
| fp8_dtype=quantizer.dtype, | ||
| rowwise_data=grouped_x.rowwise_data.view(total_tokens, cols), | ||
| rowwise_scale_inv=grouped_x.scale_inv.view(scale_shape), | ||
| columnwise_data=None, |
There was a problem hiding this comment.
Why is colwise data None if we have if with_columnwise: above?
There was a problem hiding this comment.
When we have colwise_quantizer.optimize_for_gemm = True, the colwise data should be already swizzled in the quantize kernel. We need to manually swizzle for rowwise data because it is generated from NCCL EP dispatch.
There was a problem hiding this comment.
Thanks, would be better if this kernel is also fused.
Nit: In case we don't have the kernel yet, I would still prefer having a tex. API calling unfused kernels in C++, and replace its implementation with a fused kernel when it's ready.
| ) -> "GroupedTensor": | ||
| """Rowwise-only MXFP8 GroupedTensor with compact scales (FP8 dispatch wire format).""" | ||
| wire_quantizer = MXFP8Quantizer( | ||
| fp8_dtype=tex.DType.kFloat8E4M3, rowwise=True, columnwise=False |
There was a problem hiding this comment.
Recommendation is to use TE_DType instead of tex.DType when constructing quantizers now, could we please do that here?
| # columnwise copy for wgrad. ``scale_bias`` needs the | ||
| # high-precision grad below, so it takes the dequantized tensor | ||
| # instead of the fused dbias. | ||
| grouped_fc2_dy = grouped_storage_from_grouped_tensor(grad_output) |
There was a problem hiding this comment.
There is already copy method defined in GroupedTensorStorage class to achieve this functionality of creating a shallow storage copy. Could we please use that and remove this method?
| grouped_fc2_dy = grouped_storage_from_grouped_tensor(grad_output) | |
| grouped_fc2_dy = grad_output.copy() |
There was a problem hiding this comment.
Same applies in all other places where this method is used,
| ) | ||
| tmp = MXFP8Tensor( | ||
| shape=(total_tokens, cols), | ||
| dtype=dtype, | ||
| fp8_dtype=quantizer.dtype, | ||
| rowwise_data=grouped_x.rowwise_data.view(total_tokens, cols), | ||
| rowwise_scale_inv=grouped_x.scale_inv.view(scale_shape), | ||
| columnwise_data=None, | ||
| columnwise_scale_inv=None, | ||
| quantizer=quantizer, | ||
| requires_grad=False, | ||
| with_gemm_swizzled_scales=False, | ||
| ) | ||
| tex.swizzle_scales_for_gemm_(tmp) | ||
| grouped_x.scale_inv = tmp._rowwise_scale_inv.view(-1) | ||
| grouped_x._with_gemm_swizzled_scales = True |
There was a problem hiding this comment.
I think the better way should be to directly use the grouped_swizzle API which handles variable dims as well.
This would work only if all per expert dims are padded to 128.
| ) | ||
|
|
||
|
|
||
| def prepare_prequantized_mxfp8_input_for_gemm( |
There was a problem hiding this comment.
I am uncomfortable with the complexity of this function.
I think we should define two functions, one for the prepare_rowwise_mxfp8_input for forward pass and preparing_colwise_mxfp8_input for the backward pass.
To be honest I dont think prepare_rowwise_mxfp8_input is even needed, Simply calling grouped_swizzle API on the activation should be enough which can be done directly before the GEMM in grouped_linear.py and grouped_mlp.py. However, the perf of the variable swizzle kernel is not being well studied. So it would be good to evaluate that before making the change.
For colwise, I am assuming the rowwise dequant + quant is well studied in terms of convergence. And so having a sperate function call for that is ideal which also takes care of the dbias computation
| requires_grad=False, | ||
| with_gemm_swizzled_scales=False, | ||
| ) | ||
| tex.swizzle_scales_for_gemm_(tmp) |
There was a problem hiding this comment.
@zhongbozhu I think the group_quantize + swizzle fusion generates the swizzled scales for columnwise.
Signed-off-by: YangFei1990 <feiw@nvidia.com>
Signed-off-by: YangFei1990 <feiw@nvidia.com>
Signed-off-by: YangFei1990 <feiw@nvidia.com>
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
Signed-off-by: YangFei1990 <feiw@nvidia.com>
Signed-off-by: YangFei1990 <feiw@nvidia.com>
for more information, see https://pre-commit.ci
|
/te-ci L1 pytorch |
Description
To support the NCCL EP FP8 dispatch, this PR introduce a capability to allow grouped gemm to take input that is already quantized in row-wise.
Fixes # (issue)
Type of change
Checklist: