11import itertools
2- import json
32import os
43import signal
54import subprocess
65import time
76
87import fpzip
9- import numpy as np
108
9+ from .bench_result import BenchResult , get_axis_name
1110from .cmake import CMake
1211from .config import BasePoint , Config
12+ from .json_cache import device_json , json_benches
1313from .logger import Logger
14+ from .process_runner import ProcessRunner
1415from .score import compute_axes_ids , compute_weight_matrices , get_workload_weight
1516from .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-
7133def 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-
27182def 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-
467271def 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-
530303class 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