Skip to content

Commit b02fdd3

Browse files
Remove optimizer changes for single-purpose PR
1 parent 9ead42a commit b02fdd3

5 files changed

Lines changed: 156 additions & 302 deletions

File tree

transformer_engine/plugin/core/backends/flagos/flagos.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,9 @@ def multi_tensor_adam(
159159
bias_correction: int,
160160
weight_decay: float,
161161
) -> None:
162-
if chunk_size is None:
163-
return multi_tensor_adam_fl
164162
return multi_tensor_adam_fl(
165-
chunk_size=chunk_size, noop_flag=noop_flag, tensor_lists=tensor_lists,
166-
lr=lr, beta1=beta1, beta2=beta2, epsilon=epsilon,
167-
step=step, mode=mode, bias_correction=bias_correction, weight_decay=weight_decay,
163+
chunk_size, noop_flag, tensor_lists, lr, beta1, beta2, epsilon,
164+
step, mode, bias_correction, weight_decay,
168165
)
169166
def multi_tensor_adam_param_remainder(
170167
self,
@@ -180,12 +177,10 @@ def multi_tensor_adam_param_remainder(
180177
bias_correction: int,
181178
weight_decay: float,
182179
) -> None:
183-
if chunk_size is None:
184-
return multi_tensor_adam_param_remainder_fl
185180
return multi_tensor_adam_param_remainder_fl(
186-
chunk_size=chunk_size, noop_flag=noop_flag, tensor_lists=tensor_lists,
187-
lr=lr, beta1=beta1, beta2=beta2, epsilon=epsilon,
188-
step=step, mode=mode, bias_correction=bias_correction, weight_decay=weight_decay,
181+
chunk_size, noop_flag, tensor_lists,
182+
lr, beta1, beta2, epsilon,
183+
step, mode, bias_correction, weight_decay,
189184
)
190185

191186
# Misc

transformer_engine/plugin/core/backends/flagos/impl/fused_adam.py

Lines changed: 70 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,15 @@ def multi_tensor_adam_fl(
1414
lr: float,
1515
beta1: float,
1616
beta2: float,
17-
epsilon: float,
17+
eps: float,
1818
step: int,
1919
mode: int,
2020
bias_correction: int,
2121
weight_decay: float,
2222
inv_scale: Optional[float] = 1.0,
2323
out_dtype: Optional[torch.dtype] = None,
2424
) -> None:
25-
"""
26-
Adam optimizer implementation matching CUDA exactly.
2725

28-
mode == 0: L2 regularization (add weight_decay * param to gradient before moment update)
29-
mode == 1: AdamW (add weight_decay * param to update after moment computation)
30-
"""
3126
num_lists = len(tensor_lists)
3227
assert num_lists in [4, 5], f"Expected 4 or 5 tensor lists, got {num_lists}"
3328

@@ -55,67 +50,66 @@ def multi_tensor_adam_fl(
5550
if not g.is_contiguous():
5651
g = g.contiguous()
5752

58-
# Convert to float for computation (matches CUDA's MATH_T = float)
59-
g = g.float()
60-
p_float = p.float()
61-
6253
if inv_scale is not None and inv_scale != 1.0:
6354
g = flag_gems.mul(g, inv_scale)
6455

65-
if mode == 0: # L2 regularization
66-
# Add weight decay to gradient before moment update
67-
g = flag_gems.add(g, p_float, alpha=weight_decay)
68-
69-
# Update moments with modified gradient
70-
flag_gems.add_(flag_gems.mul_(m, beta1), g, alpha=1-beta1)
71-
flag_gems.add_(flag_gems.mul_(v, beta2), flag_gems.mul(g, g), alpha=1-beta2)
72-
73-
# Bias correction
74-
m_corr = flag_gems.true_divide(m, bias_correction1)
75-
v_corr = flag_gems.true_divide(v, bias_correction2)
56+
m = flag_gems.add_(flag_gems.mul_(m, beta1), g, alpha=1-beta1)
57+
v = flag_gems.add_(flag_gems.mul_(v, beta2), flag_gems.mul_(flag_gems.mul_(g, g), 1 - beta2))
7658

77-
# Compute update
78-
denom = flag_gems.add(flag_gems.sqrt(v_corr), epsilon)
79-
update = flag_gems.true_divide(m_corr, denom)
59+
m_corr = m.clone()
60+
v_corr = v.clone()
61+
if bias_correction == 1:
62+
m_corr = flag_gems.true_divide(m_corr, bias_correction1)
63+
v_corr = flag_gems.true_divide(v_corr, bias_correction2)
8064

81-
# Update parameter
82-
p.add_(update, alpha=-lr)
83-
else: # mode == 1, AdamW (decoupled weight decay)
84-
# Update moments with original gradient
85-
flag_gems.add_(flag_gems.mul_(m, beta1), g, alpha=1-beta1)
86-
flag_gems.add_(flag_gems.mul_(v, beta2), flag_gems.mul(g, g), alpha=1-beta2)
65+
update = flag_gems.true_divide(m_corr, flag_gems.add(flag_gems.sqrt(v_corr), eps))
8766

88-
# Bias correction
89-
m_corr = flag_gems.true_divide(m, bias_correction1)
90-
v_corr = flag_gems.true_divide(v, bias_correction2)
67+
if is_adamw:
68+
p = flag_gems.mul_(p, 1 - lr * weight_decay)
69+
else:
70+
update = flag_gems.add_(update, p, alpha=weight_decay)
9171

92-
# Compute update with weight decay added (matches CUDA exactly)
93-
denom = flag_gems.add(flag_gems.sqrt(v_corr), epsilon)
94-
update = flag_gems.add(flag_gems.true_divide(m_corr, denom), p_float, alpha=weight_decay)
95-
96-
# Update parameter
97-
p.add_(update, alpha=-lr)
72+
p = flag_gems.add_(p, update, alpha=-lr)
9873

9974
if p_master is not None:
10075
flag_gems.copy_(p_master, p)
10176
out_dtype = p_master.dtype if out_dtype is None else out_dtype
10277
p.data = p.data.to(out_dtype)
10378

79+
10480
def multi_tensor_adam_param_remainder_fl(
10581
chunk_size: int,
10682
noop_flag: torch.Tensor,
10783
tensor_lists: List[List[torch.Tensor]],
10884
lr: float,
10985
beta1: float,
11086
beta2: float,
111-
epsilon: float,
87+
eps: float,
11288
step: int,
11389
mode: int,
11490
bias_correction: int,
11591
weight_decay: float,
92+
inv_scale: Optional[float] = 1.0,
11693
) -> None:
11794
"""
11895
Adam optimizer with parameter remainders for BF16 precision (FlagOS implementation).
96+
97+
This variant stores BF16 parameters + int16 remainders to reconstruct FP32 master weights.
98+
Used when you have BF16 params and need FP32 master params without storing full FP32 copies.
99+
100+
Args:
101+
chunk_size: Chunk size for processing (unused in this implementation)
102+
noop_flag: If non-zero, skip computation
103+
tensor_lists: [grads, params (bf16), exp_avgs (fp32), exp_avg_sqs (fp32), param_remainders (int16)]
104+
lr: Learning rate
105+
beta1: First moment decay rate
106+
beta2: Second moment decay rate
107+
eps: Epsilon for numerical stability
108+
step: Current optimization step
109+
mode: 0 = L2 regularization, 1 = AdamW (decoupled weight decay)
110+
bias_correction: Whether to apply bias correction (1 = yes, 0 = no)
111+
weight_decay: Weight decay coefficient
112+
inv_scale: Inverse gradient scale for mixed precision training
119113
"""
120114
if noop_flag.item() != 0:
121115
return
@@ -139,78 +133,58 @@ def multi_tensor_adam_param_remainder_fl(
139133

140134
for i in range(num_tensors):
141135
g = tensor_lists[0][i]
142-
p = tensor_lists[1][i] # int16 parameter (high 16 bits of FP32)
136+
p = tensor_lists[1][i] # BF16 parameter
143137
m = tensor_lists[2][i] # FP32 first moment
144138
v = tensor_lists[3][i] # FP32 second moment
145-
p_remainder = tensor_lists[4][i] # int16 remainder (low 16 bits of FP32)
139+
p_remainder = tensor_lists[4][i] # int16 remainder
146140

147141
if not g.is_contiguous():
148142
g = g.contiguous()
149143

150-
# Convert gradient to float
151-
g_float = g.float()
152-
153-
# Reconstruct FP32 master weight from int16 param + int16 remainder using bit manipulation
154-
# This matches the CUDA implementation exactly:
155-
# 1. If p_remainder < 0, decrement p (undo rounding)
156-
# 2. Combine high 16 bits (p) and low 16 bits (p_remainder) into FP32
157-
# Note: Use PyTorch native ops for bit manipulation (int16/int32 operations)
158-
159-
local_p = p.view(torch.int16).clone()
160-
local_p_rem = p_remainder.clone()
161-
162-
# Undo rounding: if remainder < 0, decrement p
163-
local_p = torch.where(local_p_rem < 0, local_p - 1, local_p)
164-
165-
# Combine into FP32 using bit shift operations
166-
# local_p is high 16 bits, local_p_rem is low 16 bits
167-
high_bits = local_p.to(torch.int32) << 16
168-
low_bits = local_p_rem.to(torch.int32) & 0xFFFF # Mask off sign extension
169-
param_int32 = high_bits | low_bits
170-
param_master = param_int32.view(torch.float32)
144+
# Apply gradient unscaling if needed
145+
if inv_scale is not None and inv_scale != 1.0:
146+
g = flag_gems.mul(g, inv_scale)
171147

172-
# L2 mode: add weight decay to gradient before updating moments
173-
if not is_adamw and weight_decay != 0:
174-
g_float = flag_gems.add(g_float, param_master, alpha=weight_decay)
148+
# Reconstruct FP32 master weight from BF16 param + int16 remainder
149+
# The remainder represents the lower 16 bits lost in BF16 conversion
150+
param_fp32 = p.float()
151+
param_master = flag_gems.add(param_fp32, flag_gems.mul(p_remainder.float(), 2.0 ** -16))
175152

176-
# Update first moment: m = beta1 * m + (1 - beta1) * g
177-
flag_gems.add_(flag_gems.mul_(m, beta1), g_float, alpha=1 - beta1)
153+
# Compute gradient with weight decay (if L2 mode)
154+
grad_with_decay = g.float()
155+
if not is_adamw: # L2 regularization mode
156+
grad_with_decay = flag_gems.add(grad_with_decay, flag_gems.mul(param_master, weight_decay))
178157

179-
# Update second moment: v = beta2 * v + (1 - beta2) * g^2
180-
flag_gems.add_(flag_gems.mul_(v, beta2), flag_gems.mul(g_float, g_float), alpha=1 - beta2)
158+
# Update moments
159+
m = flag_gems.add_(flag_gems.mul_(m, beta1), grad_with_decay, alpha=1 - beta1)
160+
v = flag_gems.add_(flag_gems.mul_(v, beta2), flag_gems.mul_(flag_gems.mul_(grad_with_decay, grad_with_decay), 1 - beta2))
181161

182162
# Apply bias correction
183-
m_corr = flag_gems.true_divide(m, bias_correction1)
184-
v_corr = flag_gems.true_divide(v, bias_correction2)
185-
186-
# Compute denominator: sqrt(v_corr) + epsilon
187-
denom = flag_gems.add(flag_gems.sqrt(v_corr), epsilon)
163+
m_corr = m.clone()
164+
v_corr = v.clone()
165+
if bias_correction == 1:
166+
m_corr = flag_gems.true_divide(m_corr, bias_correction1)
167+
v_corr = flag_gems.true_divide(v_corr, bias_correction2)
188168

189169
# Compute update
190-
update = flag_gems.true_divide(m_corr, denom)
191-
192-
# AdamW mode: add decoupled weight decay to update
193-
if is_adamw and weight_decay != 0:
194-
update = flag_gems.add(update, param_master, alpha=weight_decay)
170+
update = flag_gems.true_divide(m_corr, flag_gems.add(flag_gems.sqrt(v_corr), eps))
195171

196-
# Update master weight: p = p - lr * update
197-
param_master = flag_gems.sub(param_master, flag_gems.mul(update, lr))
172+
# Apply weight decay (if AdamW mode)
173+
if is_adamw:
174+
param_master = flag_gems.mul_(param_master, 1 - lr * weight_decay)
198175

199-
# Split FP32 back into int16 param + int16 remainder using bit manipulation
200-
# This matches the CUDA implementation exactly:
201-
# 1. Extract high 16 bits as p
202-
# 2. Extract low 16 bits as p_remainder
203-
# 3. If p_remainder < 0, increment p (round up)
204-
# Note: Use PyTorch native ops for bit manipulation (int32 operations)
176+
# Update master weight
177+
param_master = flag_gems.add_(param_master, update, alpha=-lr)
205178

206-
param_int32 = param_master.view(torch.int32)
207-
# Extract low 16 bits (remainder) and high 16 bits (param)
208-
new_p_rem = (param_int32 & 0xFFFF).to(torch.int16)
209-
new_p = ((param_int32 >> 16) & 0xFFFF).to(torch.int16)
179+
# Split back into BF16 param + int16 remainder
180+
# Convert to BF16 (this is the rounded version)
181+
param_bf16 = param_master.to(dtype=p.dtype)
210182

211-
# Round up: if remainder < 0, increment p
212-
new_p = torch.where(new_p_rem < 0, new_p + 1, new_p)
183+
# Compute remainder: difference between FP32 master and BF16 representation
184+
# Scale and quantize to int16 range
185+
remainder_fp32 = flag_gems.mul(flag_gems.sub(param_master, param_bf16.float()), 2.0 ** 16)
186+
remainder_int16 = flag_gems.clamp(torch.round(remainder_fp32), -32768, 32767).to(dtype=torch.int16)
213187

214188
# Write back
215-
flag_gems.copy_(p, new_p.view(torch.bfloat16))
216-
flag_gems.copy_(p_remainder, new_p_rem)
189+
flag_gems.copy_(p, param_bf16)
190+
flag_gems.copy_(p_remainder, remainder_int16)

transformer_engine/plugin/core/backends/flagos/impl/multi_tensor.py

Lines changed: 9 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,25 @@
22
#
33
# See LICENSE for license information.
44

5-
from typing import List, Tuple
65
import torch
6+
from torch.distributed._tensor import DTensor
77
import flag_gems
88

99

10-
def multi_tensor_l2_norm_fl(
11-
_chunk_size: int,
12-
noop_flag: torch.Tensor,
13-
tensor_lists: List[List[torch.Tensor]],
14-
per_tensor: bool = False,
15-
) -> Tuple[torch.Tensor, torch.Tensor]:
16-
"""
17-
Compute L2 norm of tensors using flag_gems.
18-
19-
Returns:
20-
Tuple of (total_norm, per_tensor_norms_or_dummy)
21-
- total_norm: The combined L2 norm of all tensors
22-
- per_tensor_norms_or_dummy: Per-tensor norms stacked if per_tensor=True, else dummy tensor
23-
"""
24-
device = tensor_lists[0][0].device if tensor_lists and tensor_lists[0] else 'cpu'
25-
26-
if noop_flag.item() != 0:
27-
return torch.tensor(0.0, device=device), torch.tensor(0.0, device=device)
10+
def multi_tensor_l2_norm_fl(chunk_size, noop_flag, tensor_lists, per_tensor, *args):
2811

2912
tensors = tensor_lists[0]
3013

31-
# Compute per-tensor norms
32-
per_tensor_norms = []
33-
total_norm_sq = torch.tensor(0.0, device=device)
34-
35-
for tensor in tensors:
36-
norm_sq = flag_gems.sum(tensor.float() ** 2)
37-
# Check for inf/nan (matches CUDA behavior)
38-
if not torch.isfinite(norm_sq):
39-
noop_flag.fill_(1)
40-
total_norm_sq = total_norm_sq + norm_sq
41-
if per_tensor:
42-
per_tensor_norms.append(flag_gems.sqrt(norm_sq))
43-
44-
total_norm = flag_gems.sqrt(total_norm_sq)
45-
4614
if per_tensor:
47-
per_tensor_result = torch.stack(per_tensor_norms)
15+
norms = [torch.norm(t.float(), p=2) for t in tensors]
16+
return norms, None
4817
else:
49-
per_tensor_result = torch.tensor(0.0, device=device)
18+
total_norm_sq = sum(flag_gems.sum(flag_gems.pow_func(t.float(), 2)) for t in tensors)
19+
total_norm = flag_gems.sqrt(total_norm_sq)
20+
return total_norm, None
5021

51-
return total_norm, per_tensor_result
5222

53-
def multi_tensor_scale_fl(
54-
_chunk_size: int,
55-
noop_flag: torch.Tensor,
56-
tensor_lists: List[List[torch.Tensor]],
57-
scale: float,
58-
) -> None:
59-
if noop_flag.item() != 0:
60-
return
23+
def multi_tensor_scale_fl(chunk_size, noop_flag, tensor_lists, scale):
6124

6225
for src, dst in zip(tensor_lists[0], tensor_lists[1]):
63-
# Check for inf/nan (matches CUDA behavior for AMP gradient scaling)
64-
if not torch.isfinite(src).all():
65-
noop_flag.fill_(1)
66-
flag_gems.copy_(dst, src * scale)
26+
flag_gems.copy_(dst, src * scale)

0 commit comments

Comments
 (0)