Skip to content

Commit ff0407d

Browse files
progress
Signed-off-by: Jeremy Berchtold <jberchtold@nvidia.com>
1 parent 786fa1d commit ff0407d

6 files changed

Lines changed: 448 additions & 85 deletions

File tree

tests/jax/test_grouped_gemm_partitioning.py

Lines changed: 232 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ def _mesh():
2525
return Mesh(np.asarray(devices[:4]).reshape(2, 2), ("expert", "fsdp"))
2626

2727

28+
def _mesh_with_dp_tp():
29+
devices = jax.devices()
30+
if len(devices) < 4:
31+
pytest.skip("Grouped GEMM partitioning tests require at least 4 visible GPUs.")
32+
return Mesh(np.asarray(devices[:4]).reshape(2, 1, 2, 1), ("expert", "dp", "fsdp", "tp"))
33+
34+
35+
def _mesh_with_arbitrary_axis():
36+
devices = jax.devices()
37+
if len(devices) < 4:
38+
pytest.skip("Grouped GEMM partitioning tests require at least 4 visible GPUs.")
39+
return Mesh(
40+
np.asarray(devices[:4]).reshape(2, 1, 2, 1),
41+
("expert", "dp", "fsdp", "myaxis123"),
42+
)
43+
44+
2845
def _arg_info(mesh, shape, spec):
2946
return SimpleNamespace(
3047
shape=shape,
@@ -40,6 +57,14 @@ def _normalize_spec(spec):
4057
return spec
4158

4259

60+
def _spec_contains_axis(spec, axis):
61+
for axis_spec in spec:
62+
axis_tuple = axis_spec if isinstance(axis_spec, tuple) else (axis_spec,)
63+
if axis in axis_tuple:
64+
return True
65+
return False
66+
67+
4368
def _mxfp8_grouped_quantizer_set(n_groups):
4469
return QuantizerFactory.create_set(
4570
scaling_mode=ScalingMode.MXFP8_1D_SCALING,
@@ -50,10 +75,10 @@ def _mxfp8_grouped_quantizer_set(n_groups):
5075
)
5176

5277

53-
def test_grouped_quantize_specs_preserve_ep_and_fsdp_for_block_scales():
78+
def test_grouped_quantize_gathers_hidden_axis_for_block_scales():
5479
mesh = _mesh()
5580
with global_shard_guard(MeshResource(fsdp_resource="fsdp", ep_resource="expert")):
56-
_, _, out_shardings, _ = GroupedQuantizePrimitive.partition(
81+
_, _, out_shardings, arg_shardings = GroupedQuantizePrimitive.partition(
5782
jnp.float8_e4m3fn,
5883
ScalingMode.MXFP8_1D_SCALING.value,
5984
QuantizeLayout.ROWWISE,
@@ -68,16 +93,17 @@ def test_grouped_quantize_specs_preserve_ep_and_fsdp_for_block_scales():
6893
(),
6994
)
7095

96+
assert tuple(arg_shardings[0].spec) == ("expert", None, None)
7197
specs = tuple(tuple(sharding.spec) for sharding in out_shardings)
72-
assert _normalize_spec(specs[0]) == (("expert", "fsdp"),)
73-
assert _normalize_spec(specs[2]) == (("expert", "fsdp"),)
98+
assert _normalize_spec(specs[0]) == ("expert",)
99+
assert _normalize_spec(specs[2]) == ("expert",)
74100
assert _normalize_spec(specs[4]) == ("expert",)
75101

76102

77-
def test_grouped_quantize_mxfp8_colwise_specs_preserve_ep_and_fsdp():
103+
def test_grouped_quantize_mxfp8_colwise_specs_gather_hidden_axis():
78104
mesh = _mesh()
79105
with global_shard_guard(MeshResource(fsdp_resource="fsdp", ep_resource="expert")):
80-
_, _, out_shardings, _ = GroupedQuantizePrimitive.partition(
106+
_, _, out_shardings, arg_shardings = GroupedQuantizePrimitive.partition(
81107
jnp.float8_e4m3fn,
82108
ScalingMode.MXFP8_1D_SCALING.value,
83109
QuantizeLayout.ROWWISE_COLWISE,
@@ -92,14 +118,47 @@ def test_grouped_quantize_mxfp8_colwise_specs_preserve_ep_and_fsdp():
92118
(),
93119
)
94120

121+
assert tuple(arg_shardings[0].spec) == ("expert", None, None)
95122
specs = tuple(tuple(sharding.spec) for sharding in out_shardings)
96-
assert _normalize_spec(specs[0]) == (("expert", "fsdp"),)
97-
assert _normalize_spec(specs[1]) == (("expert", "fsdp"),)
98-
assert _normalize_spec(specs[2]) == (("expert", "fsdp"),)
99-
assert _normalize_spec(specs[3]) == (("expert", "fsdp"),)
123+
assert _normalize_spec(specs[0]) == ("expert",)
124+
assert _normalize_spec(specs[1]) == ("expert",)
125+
assert _normalize_spec(specs[2]) == ("expert",)
126+
assert _normalize_spec(specs[3]) == ("expert",)
100127
assert _normalize_spec(specs[4]) == ("expert",)
101128

102129

130+
def test_grouped_quantize_strips_unsupported_axes_and_gathers_hidden_axes():
131+
mesh = _mesh_with_dp_tp()
132+
with jax.set_mesh(mesh), global_shard_guard(
133+
MeshResource(dp_resource="dp", tp_resource="tp", fsdp_resource="fsdp", ep_resource="expert")
134+
):
135+
_, _, out_shardings, arg_shardings = GroupedQuantizePrimitive.partition(
136+
jnp.float8_e4m3fn,
137+
ScalingMode.MXFP8_1D_SCALING.value,
138+
QuantizeLayout.ROWWISE,
139+
-1,
140+
jnp.float8_e8m0fnu,
141+
mesh,
142+
(
143+
_arg_info(mesh, (8, 128, 128), ("expert", "dp", ("fsdp", "tp"))),
144+
_arg_info(mesh, (8,), (("expert", "tp"),)),
145+
_arg_info(mesh, (8,), (("expert", "tp"),)),
146+
),
147+
(),
148+
)
149+
150+
assert tuple(arg_shardings[0].spec) == ("expert", None, None)
151+
assert tuple(arg_shardings[1].spec) == ("expert",)
152+
assert tuple(arg_shardings[2].spec) == ("expert",)
153+
154+
out_specs = tuple(tuple(sharding.spec) for sharding in out_shardings)
155+
assert _normalize_spec(out_specs[0]) == ("expert",)
156+
assert _normalize_spec(out_specs[2]) == ("expert",)
157+
assert _normalize_spec(out_specs[4]) == ("expert",)
158+
for spec in (*out_specs, *(tuple(sharding.spec) for sharding in arg_shardings)):
159+
assert not _spec_contains_axis(spec, "tp")
160+
161+
103162
def test_grouped_gemm_rhs_weight_specs_gather_fsdp_but_preserve_ep():
104163
mesh = _mesh()
105164
arg_infos = (
@@ -143,6 +202,169 @@ def test_grouped_gemm_rhs_weight_specs_gather_fsdp_but_preserve_ep():
143202
assert tuple(out_sharding[0].spec) == (None, None, None)
144203

145204

205+
def test_grouped_gemm_strips_unsupported_axes_preserves_dp_and_gathers_rhs_fsdp():
206+
mesh = _mesh_with_dp_tp()
207+
arg_infos = (
208+
_arg_info(mesh, (8192,), (("dp", "tp"),)),
209+
_arg_info(mesh, (0,), (("tp",),)),
210+
_arg_info(mesh, (65536,), (("expert", "fsdp", "tp"),)),
211+
_arg_info(mesh, (2048,), (("expert", "fsdp", "tp"),)),
212+
_arg_info(mesh, (0,), (("fsdp", "tp"),)),
213+
_arg_info(mesh, (8,), (("expert", "tp"),)),
214+
_arg_info(mesh, (0,), (("tp",),)),
215+
_arg_info(mesh, (0,), (("tp",),)),
216+
_arg_info(mesh, (0,), (("tp",),)),
217+
_arg_info(mesh, (8,), (("expert", "tp"),)),
218+
_arg_info(mesh, (0,), (("tp",),)),
219+
_arg_info(mesh, (1,), (("tp",),)),
220+
_arg_info(mesh, (0,), (("tp",),)),
221+
)
222+
result_infos = (_arg_info(mesh, (1, 128, 64), ("expert", "tp", None)),)
223+
with jax.set_mesh(mesh), global_shard_guard(
224+
MeshResource(dp_resource="dp", tp_resource="tp", fsdp_resource="fsdp", ep_resource="expert")
225+
):
226+
_, _, out_sharding, arg_shardings = GroupedGemmPrimitive.partition(
227+
False,
228+
False,
229+
ScalingMode.NO_SCALING.value,
230+
jnp.bfloat16,
231+
False,
232+
False,
233+
False,
234+
1,
235+
1,
236+
(1, 128, 64),
237+
128,
238+
64,
239+
128,
240+
64,
241+
mesh,
242+
arg_infos,
243+
result_infos,
244+
)
245+
246+
assert tuple(arg_shardings[0].spec) == ("dp",)
247+
assert tuple(arg_shardings[2].spec) == ("expert",)
248+
assert tuple(arg_shardings[3].spec) == ("expert",)
249+
assert tuple(arg_shardings[5].spec) == ("expert",)
250+
assert tuple(out_sharding[0].spec) == ("expert", None, None)
251+
for spec in (
252+
*(tuple(sharding.spec) for sharding in arg_shardings),
253+
tuple(out_sharding[0].spec),
254+
):
255+
assert not _spec_contains_axis(spec, "tp")
256+
257+
258+
def test_grouped_gemm_reduce_axis_skips_ep_and_uses_dp():
259+
mesh = _mesh_with_dp_tp()
260+
arg_infos = (
261+
_arg_info(mesh, (8192,), (("expert", "dp"),)),
262+
_arg_info(mesh, (0,), (None,)),
263+
_arg_info(mesh, (8192,), (("expert", "dp"),)),
264+
_arg_info(mesh, (0,), (None,)),
265+
_arg_info(mesh, (0,), (None,)),
266+
_arg_info(mesh, (8,), ("expert",)),
267+
_arg_info(mesh, (0,), (None,)),
268+
_arg_info(mesh, (8,), ("expert",)),
269+
_arg_info(mesh, (0,), (None,)),
270+
_arg_info(mesh, (8,), ("expert",)),
271+
_arg_info(mesh, (0,), (None,)),
272+
_arg_info(mesh, (1,), (None,)),
273+
_arg_info(mesh, (0,), (None,)),
274+
)
275+
276+
with jax.set_mesh(mesh), global_shard_guard(
277+
MeshResource(dp_resource="dp", fsdp_resource="fsdp", ep_resource="expert")
278+
):
279+
_, _, reduce_axis = GroupedGemmPrimitive._parse_partition_specs(
280+
mesh,
281+
arg_infos,
282+
(),
283+
out_shape=(1, 128, 64),
284+
lhs_is_trans=False,
285+
lhs_axis_boundary=1,
286+
)
287+
288+
assert reduce_axis == "dp"
289+
290+
291+
def test_grouped_partitioning_strips_arbitrary_unsupported_axis():
292+
mesh = _mesh_with_arbitrary_axis()
293+
mesh_resource = MeshResource(dp_resource="dp", fsdp_resource="fsdp", ep_resource="expert")
294+
295+
with jax.set_mesh(mesh), global_shard_guard(mesh_resource):
296+
_, _, quantize_out_shardings, quantize_arg_shardings = GroupedQuantizePrimitive.partition(
297+
jnp.float8_e4m3fn,
298+
ScalingMode.MXFP8_1D_SCALING.value,
299+
QuantizeLayout.ROWWISE,
300+
-1,
301+
jnp.float8_e8m0fnu,
302+
mesh,
303+
(
304+
_arg_info(mesh, (8, 128, 128), ("expert", "myaxis123", ("dp", "fsdp"))),
305+
_arg_info(mesh, (8,), (("expert", "myaxis123"),)),
306+
_arg_info(mesh, (8,), (("expert", "myaxis123"),)),
307+
),
308+
(),
309+
)
310+
311+
gemm_arg_infos = (
312+
_arg_info(mesh, (8192,), (("dp", "myaxis123"),)),
313+
_arg_info(mesh, (0,), (("myaxis123",),)),
314+
_arg_info(mesh, (65536,), (("expert", "fsdp", "myaxis123"),)),
315+
_arg_info(mesh, (2048,), (("expert", "fsdp", "myaxis123"),)),
316+
_arg_info(mesh, (0,), (("fsdp", "myaxis123"),)),
317+
_arg_info(mesh, (8,), (("expert", "myaxis123"),)),
318+
_arg_info(mesh, (0,), (("myaxis123",),)),
319+
_arg_info(mesh, (0,), (("myaxis123",),)),
320+
_arg_info(mesh, (0,), (("myaxis123",),)),
321+
_arg_info(mesh, (8,), (("expert", "myaxis123"),)),
322+
_arg_info(mesh, (0,), (("myaxis123",),)),
323+
_arg_info(mesh, (1,), (("myaxis123",),)),
324+
_arg_info(mesh, (0,), (("myaxis123",),)),
325+
)
326+
gemm_result_infos = (_arg_info(mesh, (1, 128, 64), ("expert", "myaxis123", None)),)
327+
_, _, gemm_out_sharding, gemm_arg_shardings = GroupedGemmPrimitive.partition(
328+
False,
329+
False,
330+
ScalingMode.NO_SCALING.value,
331+
jnp.bfloat16,
332+
False,
333+
False,
334+
False,
335+
1,
336+
1,
337+
(1, 128, 64),
338+
128,
339+
64,
340+
128,
341+
64,
342+
mesh,
343+
gemm_arg_infos,
344+
gemm_result_infos,
345+
)
346+
347+
assert tuple(quantize_arg_shardings[0].spec) == ("expert", None, None)
348+
assert tuple(quantize_arg_shardings[1].spec) == ("expert",)
349+
quantize_out_specs = tuple(tuple(sharding.spec) for sharding in quantize_out_shardings)
350+
assert _normalize_spec(quantize_out_specs[0]) == ("expert",)
351+
assert _normalize_spec(quantize_out_specs[2]) == ("expert",)
352+
353+
assert tuple(gemm_arg_shardings[0].spec) == ("dp",)
354+
assert tuple(gemm_arg_shardings[2].spec) == ("expert",)
355+
assert tuple(gemm_arg_shardings[3].spec) == ("expert",)
356+
assert tuple(gemm_out_sharding[0].spec) == ("expert", None, None)
357+
358+
all_specs = (
359+
*quantize_out_specs,
360+
*(tuple(sharding.spec) for sharding in quantize_arg_shardings),
361+
*(tuple(sharding.spec) for sharding in gemm_arg_shardings),
362+
tuple(gemm_out_sharding[0].spec),
363+
)
364+
for spec in all_specs:
365+
assert not _spec_contains_axis(spec, "myaxis123")
366+
367+
146368
def test_grouped_partitioning_shardy_rules_smoke():
147369
mesh = _mesh()
148370
quantize_rule = GroupedQuantizePrimitive.shardy_sharding_rule(

0 commit comments

Comments
 (0)