Skip to content

Commit 4da1c6f

Browse files
committed
Eliminate duplicate code, offload bwd to TE for fused qproj.
Requires TE PR: NVIDIA/TransformerEngine#3330 Signed-off-by: Chase Block <cblock@nvidia.com>
1 parent 7822da3 commit 4da1c6f

1 file changed

Lines changed: 12 additions & 102 deletions

File tree

megatron/core/transformer/multi_latent_attention.py

Lines changed: 12 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
from megatron.core.tensor_parallel.mappings import (
3434
gather_from_sequence_parallel_region,
3535
gather_from_tensor_model_parallel_region,
36-
reduce_from_tensor_model_parallel_region,
3736
scatter_to_sequence_parallel_region,
3837
)
3938
from megatron.core.transformer.attention import Attention, LinearProjBuilder
@@ -73,7 +72,6 @@
7372
mxfp8_quantize_only,
7473
mxfp8_transpose_swizzle,
7574
)
76-
from transformer_engine.pytorch.cpp_extensions import general_gemm
7775
from transformer_engine.pytorch.quantized_tensor import QuantizedTensor
7876
from transformer_engine.pytorch.tensor.mxfp8_tensor import MXFP8Quantizer
7977

@@ -99,7 +97,6 @@
9997
FusedMLAQUpProjRopeQuant,
10098
mxfp8_quantize_only,
10199
mxfp8_transpose_swizzle,
102-
general_gemm,
103100
QuantizedTensor,
104101
MXFP8Quantizer,
105102
) = (None, None, None, None, None, None, None, None, None, None, None, None, None, None)
@@ -187,105 +184,18 @@ def backward(ctx, dq):
187184
# grad w.r.t. the (pre-RoPE) up-proj GEMM output; bf16.
188185
dq2d = dq3.reshape(tokens, nh * q_head_dim).contiguous()
189186

190-
# Dispatch the adjoints on the projection precision (== forward kernel), inferred from w_q:
191-
# QuantizedTensor -> fp8 projection (mxfp8in); plain bf16 tensor
192-
# -> 16-bit projection (bf16in).
193-
if isinstance(w_q, QuantizedTensor):
194-
# FP8 projection: fp8 adjoints via TE general_gemm
195-
# x_saved is the columnwise MXFP8 activation
196-
# split_accumulator=True matches the MXFP8 recipe default for grad GEMMs.
197-
grad_output_quantizer = MXFP8Quantizer(
198-
fp8_dtype=tex.DType.kFloat8E4M3, rowwise=True, columnwise=True
199-
)
200-
# Pre-swizzle the grad-output scales at cast time, so general_gemm's in-GEMM swizzle
201-
# is a no-op (avoids the extra cast_only + standalone row/col swizzle). Layout only,
202-
# no effect on numerics.
203-
grad_output_quantizer.optimize_for_gemm = True
204-
gy = grad_output_quantizer(dq2d) # MXFP8: rowwise (dgrad) + columnwise (wgrad)
205-
206-
w_q.update_usage(rowwise_usage=True, columnwise_usage=True)
207-
208-
# dgrad: grad_input = grad_output @ weight
209-
# (A=weight[colwise], B=grad_output[rowwise], NN)
210-
grad_x = general_gemm(
211-
w_q, gy, layout="NN", grad=True, out_dtype=act_dtype, use_split_accumulator=True
212-
)[0].reshape(s, b, -1)
213-
214-
# wgrad: grad_weight = grad_output^T @ input
215-
# (A=input[colwise], B=grad_output[colwise], NT)
216-
if ctx.wgrad_store is not None and ctx.wgrad_store.delay_wgrad_compute():
217-
if ctx.fuse_wgrad_accumulation and hasattr(w_q, "main_grad"):
218-
219-
def _wgrad(x_, gy_):
220-
# Accumulate the fp8 wgrad directly into fp32 main_grad (out=main_grad,
221-
# accumulate=True), exactly as TE's fused-wgrad path does.
222-
general_gemm(
223-
x_,
224-
gy_,
225-
layout="NT",
226-
grad=True,
227-
out=w_q.main_grad,
228-
out_dtype=w_q.main_grad.dtype,
229-
accumulate=True,
230-
use_split_accumulator=True,
231-
)
232-
return w_q.main_grad, None
233-
234-
ctx.wgrad_store.put([x_saved, gy], _wgrad)
235-
if hasattr(w_q, "grad_added_to_main_grad"):
236-
w_q.grad_added_to_main_grad = True
237-
ret_grad_w = torch.empty_like(w_q) # dummy; discarded by the reduce hook
238-
else:
239-
ctx.wgrad_store.put(
240-
[x_saved, gy],
241-
lambda x_, gy_: (
242-
general_gemm(
243-
x_,
244-
gy_,
245-
layout="NT",
246-
grad=True,
247-
out_dtype=act_dtype,
248-
use_split_accumulator=True,
249-
)[0],
250-
None,
251-
),
252-
)
253-
ret_grad_w = None
254-
else:
255-
ret_grad_w = general_gemm(
256-
x_saved,
257-
gy,
258-
layout="NT",
259-
grad=True,
260-
out_dtype=act_dtype,
261-
use_split_accumulator=True,
262-
)[0]
263-
else:
264-
# === 16-bit projection: bf16 adjoints, matching the unfused bf16 up-proj backward.
265-
# x_saved and w_q are bf16. ===
266-
grad_x = (dq2d @ w_q).reshape(s, b, -1) # [s, b, q_lora], bf16
267-
268-
if ctx.wgrad_store is not None and ctx.wgrad_store.delay_wgrad_compute():
269-
if ctx.fuse_wgrad_accumulation and hasattr(w_q, "main_grad"):
270-
271-
def _wgrad(dq2d_, x_):
272-
w_q.main_grad.add_(
273-
(dq2d_.t() @ x_).to(w_q.main_grad.dtype).reshape(w_q.main_grad.shape)
274-
)
275-
return w_q.main_grad, None
276-
277-
ctx.wgrad_store.put([dq2d, x_saved], _wgrad)
278-
if hasattr(w_q, "grad_added_to_main_grad"):
279-
w_q.grad_added_to_main_grad = True
280-
ret_grad_w = torch.empty_like(w_q) # dummy; discarded by the reduce hook
281-
else:
282-
ctx.wgrad_store.put([dq2d, x_saved], lambda dq_, x_: (dq_.t() @ x_, None))
283-
ret_grad_w = None
284-
else:
285-
ret_grad_w = dq2d.t() @ x_saved # [nh*q_head_dim, q_lora], bf16
286-
287-
if get_pg_size(ctx.tp_group) > 1 and not ctx.sequence_parallel:
288-
grad_x = reduce_from_tensor_model_parallel_region(grad_x, group=ctx.tp_group)
187+
# Delegate the projection backward to TE's _linear_backward (via backward_linear)
188+
grad_x, ret_grad_w = FusedMLAQUpProjRopeQuant.backward_linear(
189+
grad_output=dq2d,
190+
x_saved=x_saved,
191+
w_q=w_q,
192+
act_dtype=act_dtype,
193+
wgrad_store=ctx.wgrad_store,
194+
fuse_wgrad_accumulation=ctx.fuse_wgrad_accumulation,
195+
tp_group=ctx.tp_group,
196+
sequence_parallel=ctx.sequence_parallel,
197+
)
198+
grad_x = grad_x.reshape(s, b, -1)
289199

290200
# grads for: q_normed, w_q, cos, sin, then 10 non-tensor args
291201
# (including tp_group, sequence_parallel)

0 commit comments

Comments
 (0)