Skip to content

Commit 1715519

Browse files
Move definitions of BenchResult and downstream dependencies to separate file
Similar for JsonCache, and ProcessRunner.
1 parent 12652bc commit 1715519

File tree

7 files changed

+276
-260
lines changed

7 files changed

+276
-260
lines changed

benchmarks/scripts/cccl/bench/bench.py

Lines changed: 14 additions & 239 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
import itertools
2-
import json
32
import os
43
import signal
54
import subprocess
65
import time
76

87
import fpzip
9-
import numpy as np
108

9+
from .bench_result import BenchResult, get_axis_name
1110
from .cmake import CMake
1211
from .config import BasePoint, Config
12+
from .json_cache import device_json, json_benches
1313
from .logger import Logger
14+
from .process_runner import ProcessRunner
1415
from .score import compute_axes_ids, compute_weight_matrices, get_workload_weight
1516
from .storage import Storage, get_bench_table_name
1617

@@ -29,45 +30,6 @@ def first_val(my_dict):
2930
return first_value
3031

3132

32-
class JsonCache:
33-
_instance = None
34-
35-
def __new__(cls):
36-
if cls._instance is None:
37-
cls._instance = super().__new__(cls)
38-
cls._instance.bench_cache = {}
39-
cls._instance.device_cache = {}
40-
return cls._instance
41-
42-
def get_bench(self, algname):
43-
if algname not in self.bench_cache:
44-
result = subprocess.check_output(
45-
[os.path.join(".", "bin", algname + ".base"), "--jsonlist-benches"]
46-
)
47-
self.bench_cache[algname] = json.loads(result)
48-
return self.bench_cache[algname]
49-
50-
def get_device(self, algname):
51-
if algname not in self.device_cache:
52-
result = subprocess.check_output(
53-
[os.path.join(".", "bin", algname + ".base"), "--jsonlist-devices"]
54-
)
55-
devices = json.loads(result)["devices"]
56-
57-
if len(devices) != 1:
58-
raise Exception(
59-
"NVBench doesn't work well with multiple GPUs, use `CUDA_VISIBLE_DEVICES`"
60-
)
61-
62-
self.device_cache[algname] = devices[0]
63-
64-
return self.device_cache[algname]
65-
66-
67-
def json_benches(algname):
68-
return JsonCache().get_bench(algname)
69-
70-
7133
def create_benches_tables(conn, subbench, bench_axes):
7234
with conn:
7335
conn.execute("""
@@ -117,163 +79,12 @@ def create_benches_tables(conn, subbench, bench_axes):
11779
)
11880

11981

120-
def read_json(filename):
121-
with open(filename, "r") as f:
122-
file_root = json.load(f)
123-
return file_root
124-
125-
126-
def extract_filename(summary):
127-
summary_data = summary["data"]
128-
value_data = next(filter(lambda v: v["name"] == "filename", summary_data))
129-
assert value_data["type"] == "string"
130-
return value_data["value"]
131-
132-
133-
def extract_size(summary):
134-
summary_data = summary["data"]
135-
value_data = next(filter(lambda v: v["name"] == "size", summary_data))
136-
assert value_data["type"] == "int64"
137-
return int(value_data["value"])
138-
139-
140-
def extract_bw(summary):
141-
summary_data = summary["data"]
142-
value_data = next(filter(lambda v: v["name"] == "value", summary_data))
143-
assert value_data["type"] == "float64"
144-
return float(value_data["value"])
145-
146-
147-
def parse_samples_meta(state):
148-
summaries = state["summaries"]
149-
if not summaries:
150-
return None, None
151-
152-
summary = next(
153-
filter(lambda s: s["tag"] == "nv/json/bin:nv/cold/sample_times", summaries),
154-
None,
155-
)
156-
if not summary:
157-
return None, None
158-
159-
sample_filename = extract_filename(summary)
160-
sample_count = extract_size(summary)
161-
return sample_count, sample_filename
162-
163-
164-
def parse_samples(state):
165-
sample_count, samples_filename = parse_samples_meta(state)
166-
if not sample_count or not samples_filename:
167-
return np.array([], dtype=np.float32)
168-
169-
with open(samples_filename, "rb") as f:
170-
samples = np.fromfile(f, "<f4")
171-
172-
samples.sort()
173-
174-
assert sample_count == len(samples)
175-
return samples
176-
177-
178-
def parse_bw(state):
179-
bwutil = next(
180-
filter(
181-
lambda s: s["tag"] == "nv/cold/bw/global/utilization", state["summaries"]
182-
),
183-
None,
184-
)
185-
if not bwutil:
186-
return None
187-
188-
return extract_bw(bwutil)
189-
190-
191-
class SubBenchState:
192-
def __init__(self, state, axes_names, axes_values):
193-
self.samples = parse_samples(state)
194-
self.bw = parse_bw(state)
195-
196-
self.point = {}
197-
for axis in state["axis_values"]:
198-
name = axes_names[axis["name"]]
199-
value = axes_values[axis["name"]][axis["value"]]
200-
self.point[name] = value
201-
202-
def __repr__(self):
203-
return str(self.__dict__)
204-
205-
def name(self):
206-
return " ".join(f"{k}={v}" for k, v in self.point.items())
207-
208-
def center(self, estimator):
209-
return estimator(self.samples)
210-
211-
212-
class SubBenchResult:
213-
def __init__(self, bench):
214-
axes_names = {}
215-
axes_values = {}
216-
for axis in bench["axes"]:
217-
short_name = axis["name"]
218-
full_name = get_axis_name(axis)
219-
axes_names[short_name] = full_name
220-
axes_values[short_name] = {}
221-
for value in axis["values"]:
222-
if "value" in value:
223-
axes_values[axis["name"]][str(value["value"])] = value[
224-
"input_string"
225-
]
226-
else:
227-
axes_values[axis["name"]][value["input_string"]] = value[
228-
"input_string"
229-
]
230-
231-
self.states = []
232-
for state in bench["states"]:
233-
if not state["is_skipped"]:
234-
self.states.append(SubBenchState(state, axes_names, axes_values))
235-
236-
def __repr__(self):
237-
return str(self.__dict__)
238-
239-
def centers(self, estimator):
240-
result = {}
241-
for state in self.states:
242-
result[state.name()] = state.center(estimator)
243-
return result
244-
245-
246-
class BenchResult:
247-
def __init__(self, json_path, code, elapsed):
248-
self.code = code
249-
self.elapsed = elapsed
250-
251-
if json_path:
252-
self.subbenches = {}
253-
if code == 0:
254-
for bench in read_json(json_path)["benchmarks"]:
255-
self.subbenches[bench["name"]] = SubBenchResult(bench)
256-
257-
def __repr__(self):
258-
return str(self.__dict__)
259-
260-
def centers(self, estimator):
261-
result = {}
262-
for subbench in self.subbenches:
263-
result[subbench] = self.subbenches[subbench].centers(estimator)
264-
return result
265-
266-
267-
def device_json(algname):
268-
return JsonCache().get_device(algname)
269-
270-
27182
def get_device_name(device):
27283
gpu_name = device["name"]
27384
bus_width = device["global_memory_bus_width"]
27485
sms = device["number_of_sms"]
27586
ecc = "eccon" if device["ecc_state"] else "eccoff"
276-
name = "{} ({}, {}, {})".format(gpu_name, bus_width, sms, ecc)
87+
name = f"{gpu_name} ({bus_width}, {sms}, {ecc})"
27788
return name.replace("NVIDIA ", "")
27889

27990

@@ -287,7 +98,7 @@ def state_to_rt_workload(bench, state):
28798
name, value = param.split("=")
28899
if is_ct_axis(name):
289100
continue
290-
rt_workload.append("{}={}".format(name, value))
101+
rt_workload.append(f"{name}={value}")
291102
return rt_workload
292103

293104

@@ -352,7 +163,7 @@ def __new__(cls, *args, **kwargs):
352163

353164
return cls._instance
354165

355-
def create_table_if_not_exists(self, conn, bench):
166+
def ensure_table_exists(self, conn, bench):
356167
bench_base = bench.get_base()
357168
alg_name = bench_base.algorithm_name()
358169

@@ -368,10 +179,10 @@ def push_bench_centers(self, bench, result, estimator):
368179
config = Config()
369180
ctk = config.ctk
370181
cccl = config.cccl
371-
gpu = get_device_name(device_json(bench.algname))
182+
gpu = get_device_name(device_json(bench.algorithm_name()))
372183
conn = Storage().connection()
373184

374-
self.create_table_if_not_exists(conn, bench)
185+
self.ensure_table_exists(conn, bench)
375186

376187
centers = {}
377188
with conn:
@@ -385,7 +196,7 @@ def push_bench_centers(self, bench, result, estimator):
385196

386197
for name in state.point:
387198
value = state.point[name]
388-
columns = columns + ', "{}"'.format(name)
199+
columns = columns + f', "{name}"'
389200
placeholders = placeholders + ", ?"
390201
values.append(value)
391202

@@ -421,7 +232,7 @@ def pull_bench_centers(self, bench, ct_workload_point, rt_values):
421232
gpu = get_device_name(device_json(bench.algname))
422233
conn = Storage().connection()
423234

424-
self.create_table_if_not_exists(conn, bench)
235+
self.ensure_table_exists(conn, bench)
425236

426237
centers = {}
427238

@@ -457,13 +268,6 @@ def pull_bench_centers(self, bench, ct_workload_point, rt_values):
457268
return centers
458269

459270

460-
def get_axis_name(axis):
461-
name = axis["name"]
462-
if axis["flags"]:
463-
name = name + "[{}]".format(axis["flags"])
464-
return name
465-
466-
467271
def speedup(base, variant):
468272
# If one of the runs failed, dict is empty
469273
if not base or not variant:
@@ -496,37 +300,6 @@ def values_to_space(axes):
496300
return list(itertools.product(*result))
497301

498302

499-
class ProcessRunner:
500-
_instance = None
501-
502-
def __new__(cls, *args, **kwargs):
503-
if not isinstance(cls._instance, cls):
504-
cls._instance = super(ProcessRunner, cls).__new__(cls, *args, **kwargs)
505-
return cls._instance
506-
507-
def __init__(self):
508-
self.process = None
509-
signal.signal(signal.SIGINT, self.signal_handler)
510-
signal.signal(signal.SIGTERM, self.signal_handler)
511-
512-
def new_process(self, cmd):
513-
self.process = subprocess.Popen(
514-
cmd,
515-
start_new_session=True,
516-
stdout=subprocess.DEVNULL,
517-
stderr=subprocess.DEVNULL,
518-
)
519-
return self.process
520-
521-
def signal_handler(self, signum, frame):
522-
self.kill_process()
523-
raise SystemExit("search was interrupted")
524-
525-
def kill_process(self):
526-
if self.process is not None:
527-
self.process.kill()
528-
529-
530303
class Bench:
531304
def __init__(self, algorithm_name, variant, ct_workload):
532305
self.algname = algorithm_name
@@ -603,9 +376,11 @@ def ct_axes_value_descriptions(self):
603376
continue
604377
if axis["flags"]:
605378
name = name + "[{}]".format(axis["flags"])
606-
descriptions[name] = {}
379+
axis_descr = {}
607380
for value in axis["values"]:
608-
descriptions[name][value["input_string"]] = value["description"]
381+
k, v = value["input_string"], value["description"]
382+
axis_descr[k] = v
383+
descriptions[name] = axis_descr
609384

610385
subbench_descriptions[bench["name"]] = descriptions
611386
return first_val(subbench_descriptions)

0 commit comments

Comments
 (0)