Kernels
ca1207 commited on
Commit
267e8a0
·
1 Parent(s): 99af74f

add doc strings + init self rank on init_assign_params

Browse files
Files changed (1) hide show
  1. torch-ext/optimizer/muon.py +48 -8
torch-ext/optimizer/muon.py CHANGED
@@ -73,7 +73,7 @@ class _muon_state:
73
  qk_clip_state = None
74
 
75
 
76
- def split_elems_for_src(param, state, src_rank, num_ranks) -> int:
77
  rows = param.shape[0]
78
  cols = int(param.numel() // rows)
79
  base, rem = divmod(rows, num_ranks)
@@ -83,6 +83,10 @@ def split_elems_for_src(param, state, src_rank, num_ranks) -> int:
83
 
84
  @torch.no_grad()
85
  def _alloc_gathered_grad(params, param_to_state, rank, compute_stream):
 
 
 
 
86
  with torch.cuda.stream(compute_stream):
87
  for p in params:
88
  state = param_to_state[id(p)]
@@ -102,13 +106,17 @@ def _alloc_gathered_grad(params, param_to_state, rank, compute_stream):
102
  @torch.no_grad()
103
  def _all2all_gather(params, param_to_state, rank, comm_stream, none_grad,
104
  alloc_event):
 
 
 
105
  with torch.cuda.stream(comm_stream):
106
  process_group = param_to_state[id(params[0])].process_group
107
  num_ranks = dist.get_world_size(group=process_group)
108
 
109
- # Calculate sending tensors
110
  per_dst = [[] for _ in range(num_ranks)]
111
  send_counts = [0] * num_ranks
 
112
  for p in params:
113
  state = param_to_state[id(p)]
114
  dst = state.worker_rank
@@ -129,8 +137,9 @@ def _all2all_gather(params, param_to_state, rank, comm_stream, none_grad,
129
  p for p in params if param_to_state[id(p)].worker_rank == rank
130
  ]
131
 
132
- # Calculate receiving tensors
133
  recv_counts = [0] * num_ranks
 
134
  for src in range(num_ranks):
135
  total = 0
136
  for p in owner_params:
@@ -141,6 +150,8 @@ def _all2all_gather(params, param_to_state, rank, comm_stream, none_grad,
141
 
142
  recv_total = sum(recv_counts)
143
  recv_buf = torch.empty(recv_total, dtype=COMM_DTYPE, device="cuda")
 
 
144
  dist.all_to_all_single(
145
  recv_buf,
146
  send_buf,
@@ -222,6 +233,10 @@ def _compute_u(p, state, steps, rank, compute_stream):
222
 
223
  @torch.no_grad()
224
  def _alloc_scattered_u(params, param_to_state, rank, compute_stream):
 
 
 
 
225
  with torch.cuda.stream(compute_stream):
226
  for p in params:
227
  state = param_to_state[id(p)]
@@ -234,6 +249,9 @@ def _alloc_scattered_u(params, param_to_state, rank, compute_stream):
234
 
235
 
236
  def _all2all_scatter(params, param_to_state, rank, comm_stream, alloc_event):
 
 
 
237
  with torch.cuda.stream(comm_stream):
238
  process_group = param_to_state[id(params[0])].process_group
239
  num_ranks = dist.get_world_size(group=process_group)
@@ -241,6 +259,7 @@ def _all2all_scatter(params, param_to_state, rank, comm_stream, alloc_event):
241
  p for p in params if param_to_state[id(p)].worker_rank == rank
242
  ]
243
 
 
244
  per_dst = [[] for _ in range(num_ranks)]
245
  send_counts = [0] * num_ranks
246
 
@@ -276,7 +295,9 @@ def _all2all_scatter(params, param_to_state, rank, comm_stream, alloc_event):
276
  # Even non-owner ranks must join the collective call
277
  send_buf = torch.empty(0, dtype=COMM_DTYPE, device="cuda")
278
 
 
279
  recv_counts = [0] * num_ranks
 
280
  for src in range(num_ranks):
281
  total = 0
282
  for p in params:
@@ -290,6 +311,7 @@ def _all2all_scatter(params, param_to_state, rank, comm_stream, alloc_event):
290
  assert recv_total > 0
291
  recv_buf = torch.empty(recv_total, dtype=COMM_DTYPE, device="cuda")
292
 
 
293
  dist.all_to_all_single(
294
  recv_buf,
295
  send_buf,
@@ -298,6 +320,21 @@ def _all2all_scatter(params, param_to_state, rank, comm_stream, alloc_event):
298
  group=process_group,
299
  )
300
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  comm_stream.wait_event(alloc_event)
302
 
303
  off = 0
@@ -521,10 +558,7 @@ class Muon(torch.optim.Optimizer):
521
 
522
  super().__init__(params, defaults)
523
 
524
- if dist.is_initialized():
525
- self.rank = dist.get_rank()
526
- else:
527
- self.rank = None
528
 
529
  self.comm_stream = torch.cuda.Stream()
530
  self.compute_stream = torch.cuda.Stream()
@@ -602,7 +636,11 @@ class Muon(torch.optim.Optimizer):
602
  if mesh is None:
603
  mesh = p.device_mesh
604
  shard_mesh, process_group = self.get_shard_mesh(p, self.rank)
605
- self.rank = dist.get_rank(group=process_group)
 
 
 
 
606
  elif mesh != p.device_mesh:
607
  raise ValueError("All parameters must be on the same mesh.")
608
 
@@ -743,6 +781,8 @@ class Muon(torch.optim.Optimizer):
743
  param_to_state, ordered_params = self.init_state_and_assign_params(
744
  names, params, group, qk_logits)
745
 
 
 
746
  def enqueue_all2all_gather(start_idx, chunk_size):
747
  target_params = ordered_params[start_idx:start_idx + chunk_size]
748
  if target_params:
 
73
  qk_clip_state = None
74
 
75
 
76
+ def split_elems_for_src(param, src_rank, num_ranks) -> int:
77
  rows = param.shape[0]
78
  cols = int(param.numel() // rows)
79
  base, rem = divmod(rows, num_ranks)
 
83
 
84
  @torch.no_grad()
85
  def _alloc_gathered_grad(params, param_to_state, rank, compute_stream):
86
+ """
87
+ Pre-allocate gathered_grad buffer on compute_stream
88
+ before launching all2all gather
89
+ """
90
  with torch.cuda.stream(compute_stream):
91
  for p in params:
92
  state = param_to_state[id(p)]
 
106
  @torch.no_grad()
107
  def _all2all_gather(params, param_to_state, rank, comm_stream, none_grad,
108
  alloc_event):
109
+ """
110
+ All2all gathers shards so each owner rank reconstructs its full gradient
111
+ """
112
  with torch.cuda.stream(comm_stream):
113
  process_group = param_to_state[id(params[0])].process_group
114
  num_ranks = dist.get_world_size(group=process_group)
115
 
116
+ # Construct sending buffers
117
  per_dst = [[] for _ in range(num_ranks)]
118
  send_counts = [0] * num_ranks
119
+
120
  for p in params:
121
  state = param_to_state[id(p)]
122
  dst = state.worker_rank
 
137
  p for p in params if param_to_state[id(p)].worker_rank == rank
138
  ]
139
 
140
+ # Compute receive sizes and allocate receiving buffers
141
  recv_counts = [0] * num_ranks
142
+
143
  for src in range(num_ranks):
144
  total = 0
145
  for p in owner_params:
 
150
 
151
  recv_total = sum(recv_counts)
152
  recv_buf = torch.empty(recv_total, dtype=COMM_DTYPE, device="cuda")
153
+
154
+ #All2All
155
  dist.all_to_all_single(
156
  recv_buf,
157
  send_buf,
 
233
 
234
  @torch.no_grad()
235
  def _alloc_scattered_u(params, param_to_state, rank, compute_stream):
236
+ """
237
+ Pre-allocate scattered_u buffer on compute_stream
238
+ before launching all2all gather
239
+ """
240
  with torch.cuda.stream(compute_stream):
241
  for p in params:
242
  state = param_to_state[id(p)]
 
249
 
250
 
251
  def _all2all_scatter(params, param_to_state, rank, comm_stream, alloc_event):
252
+ """
253
+ All2all scatters full gradients to all ranks
254
+ """
255
  with torch.cuda.stream(comm_stream):
256
  process_group = param_to_state[id(params[0])].process_group
257
  num_ranks = dist.get_world_size(group=process_group)
 
259
  p for p in params if param_to_state[id(p)].worker_rank == rank
260
  ]
261
 
262
+ # Construct sending buffer
263
  per_dst = [[] for _ in range(num_ranks)]
264
  send_counts = [0] * num_ranks
265
 
 
295
  # Even non-owner ranks must join the collective call
296
  send_buf = torch.empty(0, dtype=COMM_DTYPE, device="cuda")
297
 
298
+ # Compute receive sizes and allocate receiving buffers
299
  recv_counts = [0] * num_ranks
300
+
301
  for src in range(num_ranks):
302
  total = 0
303
  for p in params:
 
311
  assert recv_total > 0
312
  recv_buf = torch.empty(recv_total, dtype=COMM_DTYPE, device="cuda")
313
 
314
+ #All2All
315
  dist.all_to_all_single(
316
  recv_buf,
317
  send_buf,
 
320
  group=process_group,
321
  )
322
 
323
+ # Copy to pre-allocated scattered_u buffer from the received buffer
324
+ #
325
+ # recv_buf (num ranks = 3, local_rank = 0)
326
+ #
327
+ # From rank 0 From rank 1 From rank 2
328
+ # | p1_0, p2_0, p3_0 | p4_0 | p5_0, p6_0 |
329
+ #
330
+ # Outer loop:
331
+ # rank 0 -> rank 1 -> rank2
332
+ #
333
+ # Inner loop:
334
+ # src(0) : p1_0 -> p2_0 -> p3_0
335
+ # src(1) : p4_0
336
+ # src(2) : p5_0 -> p6_0
337
+
338
  comm_stream.wait_event(alloc_event)
339
 
340
  off = 0
 
558
 
559
  super().__init__(params, defaults)
560
 
561
+ self.rank = None
 
 
 
562
 
563
  self.comm_stream = torch.cuda.Stream()
564
  self.compute_stream = torch.cuda.Stream()
 
636
  if mesh is None:
637
  mesh = p.device_mesh
638
  shard_mesh, process_group = self.get_shard_mesh(p, self.rank)
639
+ local_rank = dist.get_rank(group=process_group)
640
+ if self.rank is None:
641
+ self.rank = dist.get_rank(group=process_group)
642
+ else:
643
+ assert self.rank == local_rank
644
  elif mesh != p.device_mesh:
645
  raise ValueError("All parameters must be on the same mesh.")
646
 
 
781
  param_to_state, ordered_params = self.init_state_and_assign_params(
782
  names, params, group, qk_logits)
783
 
784
+ assert self.rank is not None
785
+
786
  def enqueue_all2all_gather(start_idx, chunk_size):
787
  target_params = ordered_params[start_idx:start_idx + chunk_size]
788
  if target_params: