Skip to content

Commit a27f872

Browse files
committed
Split benchmark for reduction operators
1 parent 27531e7 commit a27f872

36 files changed

Lines changed: 1027 additions & 710 deletions

benchmark/performance_utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import yaml
1111

1212
import flag_gems
13+
from flag_gems.utils import shape_utils
1314

1415
from .attri_util import (
1516
BOOL_DTYPES,
@@ -552,6 +553,33 @@ def set_more_shapes(self):
552553
return [shape for shape in shapes if len(shape) == 2]
553554

554555

556+
class UnaryReductionBenchmark(Benchmark):
557+
def set_more_metrics(self):
558+
return ["gbps"]
559+
560+
def get_gbps(self, args, latency):
561+
inp = args[0]
562+
io_amount = sum([shape_utils.size_in_bytes(item) for item in [inp, inp]])
563+
return io_amount * 1e-9 / (latency * 1e-3)
564+
565+
def set_more_shapes(self):
566+
more_shapes_1d = [
567+
(1025 * 1024,),
568+
(1024 * 1024 * 1024,),
569+
]
570+
more_shapes_2d = [(1024, 2**i) for i in range(0, 21, 4)]
571+
more_shapes_3d = [(64, 2**i, 64) for i in range(0, 15, 4)]
572+
return more_shapes_1d + more_shapes_2d + more_shapes_3d
573+
574+
def get_input_iter(self, cur_dtype) -> Generator:
575+
for shape in self.shapes:
576+
inp = generate_tensor_input(shape, cur_dtype, self.device)
577+
if inp.ndim > 1:
578+
yield inp, 1
579+
else:
580+
yield inp,
581+
582+
555583
def generate_tensor_input(shape, dtype, device):
556584
if dtype in FLOAT_DTYPES:
557585
return torch.randn(shape, dtype=dtype, device=device)

benchmark/test_all.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
import torch
3+
4+
from . import attri_util as attr_utils
5+
from . import performance_utils as utils
6+
7+
8+
@pytest.mark.all
9+
def test_all():
10+
bench = utils.UnaryReductionBenchmark(
11+
op_name="all", torch_op=torch.all, dtypes=attr_utils.FLOAT_DTYPES
12+
)
13+
bench.run()

benchmark/test_amax.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
import torch
3+
4+
from . import attri_util as attr_utils
5+
from . import performance_utils as utils
6+
7+
8+
@pytest.mark.test_amax
9+
def test_amax():
10+
bench = utils.UnaryReductionBenchmark(
11+
op_name="amax", torch_op=torch.amax, dtypes=attr_utils.FLOAT_DTYPES
12+
)
13+
bench.run()

benchmark/test_aminmax.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
import torch
3+
4+
from . import attri_util as attr_utils
5+
from . import performance_utils as utils
6+
7+
8+
def aminmax_input_fn(shape, cur_dtype, device):
9+
inp = utils.generate_tensor_input(shape, cur_dtype, device)
10+
# Test dim=None (whole tensor reduction)
11+
yield inp,
12+
# Test dim=-1 (last dimension)
13+
yield inp, {"dim": -1}
14+
# Test dim=0 (first dimension)
15+
if len(shape) > 1:
16+
yield inp, {"dim": 0}
17+
18+
19+
class AminmaxBenchmark(utils.UnaryReductionBenchmark):
20+
def get_input_iter(self, cur_dtype):
21+
for shape in self.shapes:
22+
yield from aminmax_input_fn(shape, cur_dtype, self.device)
23+
24+
25+
@pytest.mark.aminmax
26+
def test_aminmax():
27+
bench = AminmaxBenchmark(
28+
op_name="aminmax",
29+
torch_op=torch.aminmax,
30+
dtypes=attr_utils.FLOAT_DTYPES,
31+
)
32+
33+
bench.run()

benchmark/test_any.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
import torch
3+
4+
from . import attri_util as attr_utils
5+
from . import performance_utils as utils
6+
7+
8+
@pytest.mark.any
9+
def test_any():
10+
bench = utils.UnaryReductionBenchmark(
11+
op_name="any", torch_op=torch.any, dtypes=attr_utils.FLOAT_DTYPES
12+
)
13+
bench.run()

benchmark/test_argmax.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
import torch
3+
4+
from . import attri_util as attr_utils
5+
from . import performance_utils as utils
6+
7+
8+
@pytest.mark.argmax
9+
def test_argmax():
10+
bench = utils.UnaryReductionBenchmark(
11+
op_name="argmax", torch_op=torch.argmax, dtypes=attr_utils.FLOAT_DTYPES
12+
)
13+
bench.run()

benchmark/test_argmin.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
import torch
3+
4+
from . import attri_util as attr_utils
5+
from . import performance_utils as utils
6+
7+
8+
@pytest.mark.argmin
9+
def test_argmin():
10+
bench = utils.UnaryReductionBenchmark(
11+
op_name="argmin", torch_op=torch.argmin, dtypes=attr_utils.FLOAT_DTYPES
12+
)
13+
bench.run()

benchmark/test_avg_pool2d.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from typing import Generator
2+
3+
import pytest
4+
import torch
5+
6+
import flag_gems
7+
8+
from . import attri_util as attr_utils
9+
from . import performance_utils as utils
10+
11+
12+
class AvgPool2dBenchmark(utils.GenericBenchmark):
13+
def get_input_iter(self, cur_dtype) -> Generator:
14+
shapes_4d = [
15+
(4, 3, 224, 224), # Typical input image size
16+
(16, 64, 56, 56), # Early ResNet layer output
17+
(32, 128, 28, 28), # Mid ResNet layer output
18+
(64, 256, 14, 14), # Later ResNet layer output
19+
(128, 512, 7, 7), # Final ResNet layer output
20+
]
21+
22+
for shape in shapes_4d:
23+
yield from self.input_fn(shape, cur_dtype, self.device)
24+
25+
26+
def avg_pool2d_input_fn(shape, dtype, device):
27+
inp = utils.generate_tensor_input(shape, dtype, device)
28+
29+
# Common case
30+
yield inp, {
31+
"kernel_size": 3,
32+
"stride": 2,
33+
"padding": 1,
34+
"ceil_mode": False,
35+
"count_include_pad": True,
36+
"divisor_override": None,
37+
}
38+
39+
if utils.Config.bench_level == utils.BenchLevel.COMPREHENSIVE:
40+
# With count_include_pad=False
41+
yield inp, {
42+
"kernel_size": 3,
43+
"stride": 2,
44+
"padding": 1,
45+
"ceil_mode": False,
46+
"count_include_pad": False,
47+
"divisor_override": None,
48+
}
49+
50+
# With ceil_mode
51+
yield inp, {
52+
"kernel_size": 3,
53+
"stride": 2,
54+
"padding": 1,
55+
"ceil_mode": True,
56+
"count_include_pad": True,
57+
"divisor_override": None,
58+
}
59+
60+
# With divisor_override
61+
if shape[-2] >= 2 and shape[-1] >= 2:
62+
yield inp, {
63+
"kernel_size": 2,
64+
"stride": 1,
65+
"padding": 0,
66+
"ceil_mode": False,
67+
"count_include_pad": True,
68+
"divisor_override": 3,
69+
}
70+
71+
72+
@pytest.mark.avg_pool2d
73+
def test_avg_pool2d():
74+
bench = AvgPool2dBenchmark(
75+
input_fn=avg_pool2d_input_fn,
76+
op_name="avg_pool2d",
77+
torch_op=torch.ops.aten.avg_pool2d,
78+
dtypes=attr_utils.FLOAT_DTYPES,
79+
)
80+
bench.run()
81+
82+
83+
@pytest.mark.skip(reason="Test case fails due to missing parameter self.")
84+
@pytest.mark.avg_pool2d_backward
85+
def test_avg_pool2d_backward():
86+
if flag_gems.vendor_name == "mthreads":
87+
dtypes = [torch.float32]
88+
else:
89+
dtypes = (attr_utils.FLOAT_DTYPES,)
90+
91+
bench = AvgPool2dBenchmark(
92+
input_fn=avg_pool2d_input_fn,
93+
op_name="avg_pool2d_backward",
94+
torch_op=torch.ops.aten.avg_pool2d_backward,
95+
dtypes=dtypes,
96+
is_backward=True,
97+
)
98+
bench.run()

benchmark/test_bincount.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import pytest
2+
import torch
3+
4+
import flag_gems
5+
6+
from . import attri_util as attr_utils
7+
from . import performance_utils as utils
8+
9+
10+
def bincount_input_fn(shape, dtype, device):
11+
if shape[0] > 1_000_000:
12+
return
13+
14+
n = shape[0]
15+
for num_classes in [10, 256, 4096]:
16+
inp = torch.randint(0, num_classes, (n,), dtype=torch.int64, device=device)
17+
18+
yield inp, {}
19+
20+
yield inp, {"minlength": max(512, num_classes * 2)}
21+
22+
23+
@pytest.mark.bincount
24+
def test_bincount():
25+
bench = utils.GenericBenchmark(
26+
input_fn=bincount_input_fn,
27+
op_name="bincount",
28+
torch_op=torch.bincount,
29+
dtypes=[torch.float32],
30+
)
31+
bench.set_gems(flag_gems.bincount)
32+
bench.run()
33+
34+
35+
def bincount_weighted_input_fn(shape, dtype, device):
36+
if shape[0] > 1_000_000:
37+
return
38+
39+
n = shape[0]
40+
for num_classes in [10, 256, 4096]:
41+
inp = torch.randint(0, num_classes, (n,), dtype=torch.int64, device=device)
42+
weights = torch.randn((n,), dtype=dtype, device=device)
43+
44+
yield inp, {"weights": weights}
45+
46+
yield inp, {"weights": weights, "minlength": max(512, num_classes * 2)}
47+
48+
49+
@pytest.mark.bincount
50+
@pytest.mark.parametrize("dtype", attr_utils.FLOAT_DTYPES)
51+
def test_bincount_weighted(dtype):
52+
bench = utils.GenericBenchmark(
53+
input_fn=bincount_weighted_input_fn,
54+
op_name=f"bincount_weighted_{str(dtype).split('.')[-1]}",
55+
torch_op=torch.bincount,
56+
dtypes=[dtype],
57+
)
58+
bench.set_gems(flag_gems.bincount)
59+
bench.run()

benchmark/test_count_nonzero.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import random
2+
3+
import pytest
4+
import torch
5+
6+
from . import attri_util as attr_utils
7+
from . import performance_utils as utils
8+
9+
10+
@pytest.mark.count_nonzero
11+
def test_count_nonzero():
12+
def count_nonzero_input_fn(shape, dtype, device):
13+
inp = torch.randn(shape, dtype=dtype, device=device)
14+
dim = random.choice([None, 0, 1])
15+
16+
yield inp, dim
17+
18+
bench = utils.GenericBenchmark2DOnly(
19+
input_fn=count_nonzero_input_fn,
20+
op_name="count_nonzero",
21+
torch_op=torch.count_nonzero,
22+
dtypes=attr_utils.FLOAT_DTYPES,
23+
)
24+
bench.run()

0 commit comments

Comments
 (0)