Skip to content

Commit 95aeb5f

Browse files
huydhnpobin6
authored and
pobin6
committed
Record PR time benchmark results in JSON format (pytorch#140493)
I'm trying to make this benchmark results available on OSS benchmark database, so that people can query it from outside. The first step is to also record the results in the JSON format compatible with the database schema defined in pytorch/test-infra#5839. Existing CSV files remain unchanged. ### Testing The JSON results are uploaded as artifacts to S3 https://github.com/pytorch/pytorch/actions/runs/11809725848/job/32901411180#step:26:13, for example https://gha-artifacts.s3.amazonaws.com/pytorch/pytorch/11809725848/1/artifact/test-jsons-test-pr_time_benchmarks-1-1-linux.g4dn.metal.nvidia.gpu_32901411180.zip Pull Request resolved: pytorch#140493 Approved by: https://github.com/laithsakka
1 parent d88949d commit 95aeb5f

File tree

9 files changed

+150
-31
lines changed

9 files changed

+150
-31
lines changed

.github/workflows/_linux-test.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,14 @@ jobs:
330330
test_config: ${{ matrix.config }}
331331
job_identifier: ${{ github.workflow }}_${{ inputs.build-environment }}
332332

333+
- name: Upload the benchmark results
334+
uses: pytorch/test-infra/.github/actions/upload-benchmark-results@main
335+
with:
336+
benchmark-results-dir: test/test-reports
337+
dry-run: false
338+
schema-version: v3
339+
github-token: ${{ secrets.GITHUB_TOKEN }}
340+
333341
- name: Print remaining test logs
334342
shell: bash
335343
if: always() && steps.test.conclusion

benchmarks/dynamo/pr_time_benchmarks/benchmark_base.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import csv
22
import gc
3+
import json
4+
import os
35
from abc import ABC, abstractmethod
46

57
from fbscribelogger import make_scribe_logger
@@ -65,6 +67,22 @@ class BenchmarkBase(ABC):
6567
# number of iterations used to run when collecting instruction_count or compile_time_instruction_count.
6668
_num_iterations = 5
6769

70+
def __init__(
71+
self,
72+
category: str,
73+
device: str,
74+
backend: str = "",
75+
mode: str = "",
76+
dynamic=None,
77+
):
78+
# These individual attributes are used to support different filters on the
79+
# dashboard later
80+
self._category = category
81+
self._device = device
82+
self._backend = backend
83+
self._mode = mode # Training or inference
84+
self._dynamic = dynamic
85+
6886
def with_iterations(self, value):
6987
self._num_iterations = value
7088
return self
@@ -80,6 +98,21 @@ def enable_compile_time_instruction_count(self):
8098
def name(self):
8199
return ""
82100

101+
def backend(self):
102+
return self._backend
103+
104+
def mode(self):
105+
return self._mode
106+
107+
def category(self):
108+
return self._category
109+
110+
def device(self):
111+
return self._device
112+
113+
def is_dynamic(self):
114+
return self._dynamic
115+
83116
def description(self):
84117
return ""
85118

@@ -134,6 +167,46 @@ def _count_compile_time_instructions(self):
134167
finally:
135168
gc.enable()
136169

170+
def _write_to_json(self, output_dir: str):
171+
"""
172+
Write the result into JSON format, so that it can be uploaded to the benchmark database
173+
to be displayed on OSS dashboard. The JSON format is defined at
174+
https://github.com/pytorch/pytorch/wiki/How-to-integrate-with-PyTorch-OSS-benchmark-database
175+
"""
176+
records = []
177+
for entry in self.results:
178+
metric_name = entry[1]
179+
value = entry[2]
180+
181+
if not metric_name or value is None:
182+
continue
183+
184+
records.append(
185+
{
186+
"benchmark": {
187+
"name": "pr_time_benchmarks",
188+
"mode": self.mode(),
189+
"extra_info": {
190+
"is_dynamic": self.is_dynamic(),
191+
"device": self.device(),
192+
"description": self.description(),
193+
},
194+
},
195+
"model": {
196+
"name": self.name(),
197+
"type": self.category(),
198+
"backend": self.backend(),
199+
},
200+
"metric": {
201+
"name": metric_name,
202+
"benchmark_values": [value],
203+
},
204+
}
205+
)
206+
207+
with open(os.path.join(output_dir, f"{self.name()}.json"), "w") as f:
208+
json.dump(records, f)
209+
137210
def append_results(self, path):
138211
with open(path, "a", newline="") as csvfile:
139212
# Create a writer object
@@ -142,6 +215,10 @@ def append_results(self, path):
142215
for entry in self.results:
143216
writer.writerow(entry)
144217

218+
# TODO (huydhn) This requires the path to write to, so it needs to be in the same place
219+
# as the CSV writer for now
220+
self._write_to_json(os.path.dirname(os.path.abspath(path)))
221+
145222
def print(self):
146223
for entry in self.results:
147224
print(f"{entry[0]},{entry[1]},{entry[2]}")

benchmarks/dynamo/pr_time_benchmarks/benchmarks/add_loop.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,37 @@
88

99
class Benchmark(BenchmarkBase):
1010
def __init__(self, backend, dynamic=False, is_gpu=False):
11-
self._backend = backend
12-
self._dynamic = dynamic
13-
self._device = "cuda" if is_gpu else "cpu"
11+
super().__init__(
12+
category="add_loop",
13+
backend=backend,
14+
device="cuda" if is_gpu else "cpu",
15+
dynamic=dynamic,
16+
)
1417

1518
def name(self):
16-
prefix = f"add_loop_{self._backend}"
17-
if self._dynamic:
19+
prefix = f"{self.category()}_{self.backend()}"
20+
if self.is_dynamic():
1821
prefix += "_dynamic"
19-
if self._device == "cuda":
22+
if self.device() == "cuda":
2023
prefix += "_gpu"
2124
return prefix
2225

2326
def description(self):
2427
return "a loop over 100 add node"
2528

2629
def _prepare_once(self):
27-
self.a = torch.ones(1000, device=self._device)
28-
self.b = torch.torch.ones(1000, device=self._device)
30+
self.a = torch.ones(1000, device=self.device())
31+
self.b = torch.torch.ones(1000, device=self.device())
2932

3033
def _prepare(self):
3134
torch._dynamo.reset()
3235

3336
def _work(self):
34-
@torch.compile(backend=self._backend, fullgraph=True, dynamic=self._dynamic)
37+
@torch.compile(
38+
backend=self.backend(),
39+
fullgraph=True,
40+
dynamic=self.is_dynamic(),
41+
)
3542
def f(a, b):
3643
result = a.clone()
3744
for i in range(1000):

benchmarks/dynamo/pr_time_benchmarks/benchmarks/aotdispatcher.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,20 @@ class Benchmark(BenchmarkBase):
1010
def __init__(self, *, training, subclass):
1111
self._training = training
1212
self._subclass = subclass
13-
self._device = "cpu"
13+
super().__init__(
14+
category="aotdispatcher",
15+
backend="aot_eager_decomp_partition",
16+
device="cpu",
17+
mode="training" if self._training else "inference",
18+
)
1419

1520
def name(self):
16-
prefix = "aotdispatcher"
17-
if self._training:
18-
prefix += "_training"
19-
else:
20-
prefix += "_inference"
21+
prefix = f"{self.category()}_{self.mode()}"
2122
if self._subclass:
2223
prefix += "_subclass"
2324
else:
2425
prefix += "_nosubclass"
25-
if self._device == "cpu":
26+
if self.device() == "cpu":
2627
prefix += "_cpu"
2728
return prefix
2829

@@ -31,7 +32,7 @@ def description(self):
3132

3233
def _prepare_once(self):
3334
_args = [
34-
torch.ones(100, requires_grad=self._training, device=self._device)
35+
torch.ones(100, requires_grad=self._training, device=self.device())
3536
for _ in range(100)
3637
]
3738
if self._subclass:
@@ -45,7 +46,7 @@ def _prepare(self):
4546
torch._dynamo.reset()
4647

4748
def _work(self):
48-
@torch.compile(backend="aot_eager_decomp_partition", fullgraph=True)
49+
@torch.compile(backend=self.backend(), fullgraph=True)
4950
def f(*args):
5051
outs = [torch.add(x, x) for x in args]
5152
return outs

benchmarks/dynamo/pr_time_benchmarks/benchmarks/aotdispatcher_partitioner.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,15 @@
66

77

88
class Benchmark(BenchmarkBase):
9+
def __init__(self):
10+
super().__init__(
11+
category="aotdispatcher_partitioner",
12+
backend="aot_eager_decomp_partition",
13+
device="cpu",
14+
)
15+
916
def name(self):
10-
return "aotdispatcher_partitioner_cpu"
17+
return f"{self.category()}_{self.device()}"
1118

1219
def description(self):
1320
return "partitioner benchmark 1 input and 100 weights, mix of recompute and non-recompute ops"
@@ -20,7 +27,7 @@ def _prepare(self):
2027
torch._dynamo.reset()
2128

2229
def _work(self):
23-
@torch.compile(backend="aot_eager_decomp_partition", fullgraph=True)
30+
@torch.compile(backend=self.backend(), fullgraph=True)
2431
def f(inp, *weights):
2532
x = inp
2633
for w in weights:

benchmarks/dynamo/pr_time_benchmarks/benchmarks/basic_modules_benchmarks.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,20 @@ def __init__(
2424
self, ModuleClass, backend, is_gpu=False, dynamic=False, force_shape_pad=False
2525
):
2626
self.ModuleClass = ModuleClass
27-
self.backend = backend
2827
self._name = ModuleClass.__name__
2928
self._is_gpu = is_gpu
30-
self._dynamic = dynamic
3129
self._force_shape_pad = force_shape_pad
3230

31+
super().__init__(
32+
category="basic_modules",
33+
backend=backend,
34+
device="cuda" if self._is_gpu else "cpu",
35+
dynamic=dynamic,
36+
)
37+
3338
def name(self):
34-
prefix = f"basic_modules_{self._name}_{self.backend}"
35-
if self._dynamic:
39+
prefix = f"{self.category()}_{self._name}_{self.backend()}"
40+
if self.is_dynamic():
3641
prefix += "_dynamic"
3742
if self._is_gpu:
3843
prefix += "_gpu"
@@ -43,7 +48,7 @@ def name(self):
4348
def _prepare_once(self):
4449
self.m = self.ModuleClass()
4550
torch.set_float32_matmul_precision("high")
46-
self.input = torch.ones(10, device="cuda" if self._is_gpu else "cpu")
51+
self.input = torch.ones(10, device=self.device())
4752

4853
def _prepare(self):
4954
torch._dynamo.reset()
@@ -52,7 +57,7 @@ def _work(self):
5257
with fresh_inductor_cache(), torch._inductor.config.patch(
5358
force_shape_pad=self._force_shape_pad
5459
):
55-
opt_m = torch.compile(backend=self.backend, dynamic=self._dynamic)(
60+
opt_m = torch.compile(backend=self.backend(), dynamic=self.is_dynamic())(
5661
self.m.cuda() if self._is_gpu else self.m
5762
)
5863
opt_m(self.input)

benchmarks/dynamo/pr_time_benchmarks/benchmarks/sum_floordiv.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
class Benchmark(BenchmarkBase):
99
N = 100
1010

11+
def __init__(self):
12+
super().__init__(category="sum_floordiv", backend="export", device="cpu")
13+
1114
def name(self):
12-
return "sum_floordiv_regression"
15+
return f"{self.category()}_regression"
1316

1417
def description(self):
1518
return "information at https://github.com/pytorch/pytorch/issues/134133"

benchmarks/dynamo/pr_time_benchmarks/benchmarks/symint_sum.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ class Benchmark(BenchmarkBase):
99
N = 200
1010

1111
def __init__(self, use_loop=False):
12-
super().__init__()
1312
self.use_loop = use_loop
13+
super().__init__(
14+
category="symint_sum",
15+
backend="inductor",
16+
device="cpu",
17+
)
1418

1519
def name(self):
1620
if self.use_loop:
17-
return "symint_sum_loop"
21+
return f"{self.category()}_loop"
1822

19-
return "symint_sum"
23+
return self.category()
2024

2125
def description(self):
2226
return "see https://docs.google.com/document/d/11xJXl1etSmefUxPiVyk885e0Dl-4o7QwxYcPiMIo2iY/edit"

benchmarks/dynamo/pr_time_benchmarks/benchmarks/update_hint_benchmark.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@
88
class Benchmark(BenchmarkBase):
99
N = 20
1010

11+
def __init__(self):
12+
super().__init__(
13+
category="update_hint",
14+
backend="inductor",
15+
device="cpu",
16+
)
17+
1118
def name(self):
12-
return "update_hint_regression"
19+
return f"{self.category()}_regression"
1320

1421
def description(self):
1522
return "information at https://github.com/pytorch/pytorch/pull/129893"

0 commit comments

Comments
 (0)