Skip to content

Commit 0fb6911

Browse files
q10meta-codesync[bot]
authored andcommitted
Normalize group_index_select_2d_bench param names (#5901)
Summary: X-link: https://github.com/facebookresearch/FBGEMM/pull/2819 Pull Request resolved: #5901 Normalizes the group-index-select-2d-bench command in sparse_ops_benchmark.py so its parameters match what the operator actually does: --row-size->--num-cols, --batch-size->--num-rows, --unique-batch-size->--num-unique-indices, --index-dim->--num-indices. Decouples num_rows from num_indices, renames the two input generators to _gen_group_inputs_uniform / _gen_group_inputs_jagged ("ragged"->"jagged"), and adds mutual-exclusivity validation between the uniform (--num-groups/--num-rows/ --num-cols) and jagged (--input-dims/--input-strides) shape modes. (First of a stack normalizing the benchmark + reporting nomenclature.) Reviewed By: spcyppt Differential Revision: D108357540 fbshipit-source-id: 3df1494ed871e0660a18077a72ba5a0a5528e289
1 parent 967d11e commit 0fb6911

1 file changed

Lines changed: 93 additions & 65 deletions

File tree

fbgemm_gpu/bench/sparse_ops_benchmark.py

Lines changed: 93 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -455,36 +455,37 @@ def _export_kineto_trace(
455455
logging.info(f"backward: fbgemm {time * 1e3:.3f} ms{ref_str}")
456456

457457

458-
def _gen_group_inputs(
459-
row_size: int,
460-
batch_size: int,
461-
unique_batch_size: int,
458+
def _gen_group_inputs_uniform(
459+
num_cols: int,
460+
num_rows: int,
461+
num_indices: int,
462+
num_unique_indices: int,
462463
num_groups: int,
463464
dtype: torch.dtype,
464465
sort_indices: bool,
465466
device: str,
466467
) -> tuple[list[torch.Tensor], list[torch.Tensor]]:
467468
"""Generate homogeneous synthetic inputs for group_index_select_2d_bench.
468469
469-
Every group shares the same shape: a ``(batch_size, row_size)`` slice of a
470-
single ``(num_groups * batch_size, row_size)`` tensor, with ``batch_size``
471-
indices drawn from ``unique_batch_size`` distinct rows. Used by
470+
Every group shares the same shape: a ``(num_rows, num_cols)`` slice of a
471+
single ``(num_groups * num_rows, num_cols)`` tensor, with ``num_indices``
472+
indices drawn from ``num_unique_indices`` distinct rows. Used by
472473
``group-index-select-2d-bench`` when ``--input-dims`` is NOT provided.
473474
"""
474475

475-
def gen_inverse_index(curr_size: int, final_size: int) -> np.ndarray:
476-
inverse_index = list(range(curr_size))
476+
def gen_inverse_index(num_unique: int, num_indices: int) -> np.ndarray:
477+
inverse_index = list(range(num_unique))
477478
np_arr = np.array(inverse_index)
478-
for _ in range(final_size - curr_size):
479-
inverse_index.append(np.random.randint(0, curr_size))
479+
for _ in range(num_indices - num_unique):
480+
inverse_index.append(np.random.randint(0, num_unique))
480481
np_arr = np.array(inverse_index)
481482
np.random.shuffle(np_arr)
482483
return np_arr
483484

484485
indices_group = []
485486
for _ in range(num_groups):
486487
indices = torch.tensor(
487-
gen_inverse_index(unique_batch_size, batch_size),
488+
gen_inverse_index(num_unique_indices, num_indices),
488489
dtype=torch.int32,
489490
device=device,
490491
)
@@ -493,28 +494,28 @@ def gen_inverse_index(curr_size: int, final_size: int) -> np.ndarray:
493494
indices_group.append(indices)
494495

495496
input = torch.rand(
496-
num_groups * batch_size,
497-
row_size,
497+
num_groups * num_rows,
498+
num_cols,
498499
dtype=dtype,
499500
device=device,
500501
)
501502
input.requires_grad = True
502-
input_group = list(input.split(batch_size, 0))
503+
input_group = list(input.split(num_rows, 0))
503504
return input_group, indices_group
504505

505506

506-
def _gen_group_inputs_with_spec(
507+
def _gen_group_inputs_jagged(
507508
input_dims: str,
508509
input_strides: str | None,
509-
index_dim: int,
510+
num_indices: int,
510511
dtype: torch.dtype,
511512
sort_indices: bool,
512513
device: str,
513514
) -> tuple[list[torch.Tensor], list[torch.Tensor]]:
514-
"""Generate ragged inputs from explicit per-group shapes.
515+
"""Generate jagged inputs from explicit per-group shapes.
515516
516-
Each group gets its own ``[num_rows, row_size]`` tensor (groups may differ
517-
in both row count and column width) with ``index_dim`` indices each, matching
517+
Each group gets its own ``[num_rows, num_cols]`` tensor (groups may differ
518+
in both row count and column width) with ``num_indices`` indices each, matching
518519
real-world / production shapes. Used by ``group-index-select-2d-bench`` when
519520
``--input-dims`` is provided; ``--input-strides`` is optional and defaults to
520521
contiguous strides per group.
@@ -523,7 +524,7 @@ def _gen_group_inputs_with_spec(
523524
if input_strides is not None and input_strides.strip() != "":
524525
strides = json.loads(input_strides)
525526
else:
526-
strides = [[rs, 1] for (_, rs) in dims]
527+
strides = [[num_cols, 1] for (_, num_cols) in dims]
527528
if len(strides) != len(dims):
528529
raise ValueError(
529530
f"--input-strides length ({len(strides)}) must match "
@@ -541,7 +542,7 @@ def _gen_group_inputs_with_spec(
541542
idx = torch.randint(
542543
low=0,
543544
high=dim[0],
544-
size=(index_dim,),
545+
size=(num_indices,),
545546
dtype=torch.int32,
546547
device=device,
547548
)
@@ -553,12 +554,12 @@ def _gen_group_inputs_with_spec(
553554

554555

555556
@cli.command()
556-
@click.option("--row-size", default=512)
557-
@click.option("--batch-size", default=4096)
558-
@click.option("--unique-batch-size", default=1024)
557+
@click.option("--num-cols", type=int, default=None)
558+
@click.option("--num-rows", type=int, default=None)
559+
@click.option("--num-unique-indices", type=int, default=None)
559560
@click.option("--input-precision", type=str, default="fp32")
560561
@click.option("--sort-indices", type=bool, default=True)
561-
@click.option("--num-groups", default=32)
562+
@click.option("--num-groups", type=int, default=None)
562563
@click.option(
563564
"--device",
564565
type=str,
@@ -580,8 +581,8 @@ def _gen_group_inputs_with_spec(
580581
"--input-dims",
581582
type=str,
582583
default=None,
583-
help="JSON list of [num_rows, row_size] per group, e.g. '[[27330,96],[4914,96]]'. "
584-
"When provided, --row-size, --batch-size, --unique-batch-size, and --num-groups are ignored.",
584+
help="JSON list of [num_rows, num_cols] per group, e.g. '[[27330,96],[4914,96]]'. "
585+
"When provided, --num-cols, --num-rows, --num-unique-indices, and --num-groups are ignored.",
585586
)
586587
@click.option(
587588
"--input-strides",
@@ -590,35 +591,31 @@ def _gen_group_inputs_with_spec(
590591
help="JSON list of [stride0, stride1] per group. Defaults to contiguous strides if not provided.",
591592
)
592593
@click.option(
593-
"--index-dim", type=int, default=None, help="Number of indices per group."
594+
"--num-indices",
595+
type=int,
596+
default=None,
597+
help="Number of indices per group (= output rows / effective batch L).",
594598
)
595599
@click.option(
596600
"--manual-seed/--skip-manual-seed",
597601
default=True,
598602
help="Use manual seed for reproduction.",
599603
)
600604
def group_index_select_2d_bench(
601-
row_size: int,
602-
batch_size: int,
603-
unique_batch_size: int,
605+
num_cols: int | None,
606+
num_rows: int | None,
607+
num_unique_indices: int | None,
604608
input_precision: str,
605609
sort_indices: bool,
606-
num_groups: int,
610+
num_groups: int | None,
607611
device: str,
608612
export_trace: bool,
609613
trace_url: str,
610614
input_dims: str | None,
611615
input_strides: str | None,
612-
index_dim: int | None,
616+
num_indices: int | None,
613617
manual_seed: bool,
614618
) -> None:
615-
# Input validation (backported from tritonbench implementation)
616-
if unique_batch_size > batch_size:
617-
raise ValueError(
618-
f"unique_batch_size ({unique_batch_size}) must be <= batch_size "
619-
f"({batch_size})"
620-
)
621-
622619
# set manual seed for reproducibility
623620
if manual_seed:
624621
torch.manual_seed(42)
@@ -632,34 +629,56 @@ def group_index_select_2d_bench(
632629
else:
633630
raise RuntimeError(f"Does not support data type {input_precision}")
634631

635-
bench_kwargs = {"num_warmups": 10, "iters": 100}
636-
637-
def _kineto_trace_handler(p: profile, phase: str) -> None:
638-
p.export_chrome_trace(trace_url.format(phase=phase, ospid=os.getpid()))
639-
640-
# pyre-ignore[3]
641-
def context_factory(on_trace_ready: Callable[[profile], None]):
642-
return profile(on_trace_ready=on_trace_ready) if export_trace else nullcontext()
643-
644-
# An empty/whitespace --input-dims is treated as NOT provided, so it falls
645-
# back to the synthetic path. Checking input_dims directly (rather than via a
646-
# bool) lets the type checker narrow it to a non-None str inside the branch.
632+
# Two mutually-exclusive shape-specification modes:
633+
# uniform (synthetic): --num-groups + --num-rows + --num-cols
634+
# jagged (prod/real): --input-dims + --input-strides
635+
# --num-indices / --num-unique-indices / --input-precision / --sort-indices
636+
# apply to both. The uniform scalar flags default to None so we can detect
637+
# which mode the user selected and reject mixing the two. Checking input_dims
638+
# directly (rather than via a bool) lets the type checker narrow it.
647639
if input_dims is not None and input_dims.strip() != "":
648-
if index_dim is None:
649-
raise ValueError("--index-dim is required when --input-dims is provided")
650-
input_group, indices_group = _gen_group_inputs_with_spec(
651-
input_dims, input_strides, index_dim, dtype, sort_indices, device
640+
for name, val in [
641+
("--num-groups", num_groups),
642+
("--num-rows", num_rows),
643+
("--num-cols", num_cols),
644+
("--num-unique-indices", num_unique_indices),
645+
]:
646+
if val is not None:
647+
raise ValueError(
648+
f"{name} cannot be combined with --input-dims (jagged mode)"
649+
)
650+
if num_indices is None:
651+
raise ValueError(
652+
"--num-indices is required with --input-dims (jagged mode)"
653+
)
654+
input_group, indices_group = _gen_group_inputs_jagged(
655+
input_dims, input_strides, num_indices, dtype, sort_indices, device
652656
)
653657
logging.info(
654-
f"ragged: {len(input_group)} groups, "
658+
f"jagged: {len(input_group)} groups, "
655659
f"cols={[t.shape[1] for t in input_group]}, "
656-
f"index_dim={index_dim}, sort_indices={sort_indices}"
660+
f"num_indices={num_indices}, sort_indices={sort_indices}"
657661
)
658662
else:
659-
input_group, indices_group = _gen_group_inputs(
660-
row_size,
661-
batch_size,
662-
unique_batch_size,
663+
if input_strides is not None and input_strides.strip() != "":
664+
raise ValueError("--input-strides requires --input-dims (jagged mode)")
665+
# Apply uniform-mode effective defaults (preserve today's numbers).
666+
num_groups = 32 if num_groups is None else num_groups
667+
num_rows = 4096 if num_rows is None else num_rows
668+
num_cols = 512 if num_cols is None else num_cols
669+
num_unique_indices = 1024 if num_unique_indices is None else num_unique_indices
670+
# Decoupled default: indices count falls back to the row count.
671+
num_indices = num_rows if num_indices is None else num_indices
672+
if num_unique_indices > min(num_rows, num_indices):
673+
raise ValueError(
674+
f"num_unique_indices ({num_unique_indices}) must be <= "
675+
f"min(num_rows={num_rows}, num_indices={num_indices})"
676+
)
677+
input_group, indices_group = _gen_group_inputs_uniform(
678+
num_cols,
679+
num_rows,
680+
num_indices,
681+
num_unique_indices,
663682
num_groups,
664683
dtype,
665684
sort_indices,
@@ -670,9 +689,18 @@ def context_factory(on_trace_ready: Callable[[profile], None]):
670689
# reference fwd/bwd below to compare torch.index_select vs fbgemm.
671690
# input = torch.cat(input_group, dim=0)
672691
# offset_indices = torch.cat(
673-
# [idx + batch_size * i for i, idx in enumerate(indices_group)]
692+
# [idx + num_rows * i for i, idx in enumerate(indices_group)]
674693
# )
675-
# num_bytes = 2 * batch_size * row_size * input.element_size() * num_groups
694+
# num_bytes = 2 * num_rows * num_cols * input.element_size() * num_groups
695+
696+
bench_kwargs = {"num_warmups": 10, "iters": 100}
697+
698+
def _kineto_trace_handler(p: profile, phase: str) -> None:
699+
p.export_chrome_trace(trace_url.format(phase=phase, ospid=os.getpid()))
700+
701+
# pyre-ignore[3]
702+
def context_factory(on_trace_ready: Callable[[profile], None]):
703+
return profile(on_trace_ready=on_trace_ready) if export_trace else nullcontext()
676704

677705
# Benchmark forward
678706
with context_factory(lambda p: _kineto_trace_handler(p, "fwd")):

0 commit comments

Comments
 (0)