Skip to content

Commit 4025495

Browse files
committed
Improved compatiblity with T4 format, added path support to filenames
1 parent 4e0a80e commit 4025495

File tree

1 file changed

+78
-52
lines changed

1 file changed

+78
-52
lines changed

kernel_tuner/file_utils.py

Lines changed: 78 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import json
55
import subprocess
66
import xmltodict
7+
from sys import platform
8+
from pathlib import Path
79

810
from importlib.metadata import requires, version, PackageNotFoundError
911
from packaging.requirements import Requirement
@@ -14,7 +16,7 @@
1416

1517

1618
def output_file_schema(target):
17-
""" Get the requested JSON schema and the version number
19+
"""Get the requested JSON schema and the version number
1820
1921
:param target: Name of the T4 schema to return, should be any of ['output', 'metadata']
2022
:type target: string
@@ -25,13 +27,13 @@ def output_file_schema(target):
2527
"""
2628
current_version = "1.0.0"
2729
output_file = schema_dir + f"/T4/{current_version}/{target}-schema.json"
28-
with open(output_file, 'r') as fh:
30+
with open(output_file, "r") as fh:
2931
json_string = json.load(fh)
3032
return current_version, json_string
3133

3234

3335
def get_configuration_validity(objective) -> str:
34-
""" Convert internal Kernel Tuner error to string """
36+
"""Convert internal Kernel Tuner error to string"""
3537
errorstring: str
3638
if not isinstance(objective, util.ErrorConfig):
3739
errorstring = "correct"
@@ -40,24 +42,33 @@ def get_configuration_validity(objective) -> str:
4042
errorstring = "compile"
4143
elif isinstance(objective, util.RuntimeFailedConfig):
4244
errorstring = "runtime"
43-
else:
45+
elif isinstance(objective, util.InvalidConfig):
4446
errorstring = "constraints"
47+
else:
48+
raise ValueError(f"Unkown objective type {type(objective)}, value {objective}")
4549
return errorstring
4650

4751

4852
def filename_ensure_json_extension(filename: str) -> str:
49-
""" Check if the filename has a .json extension, if not, add it """
53+
"""Check if the filename has a .json extension, if not, add it"""
5054
if filename[-5:] != ".json":
5155
filename += ".json"
5256
return filename
5357

5458

55-
def store_output_file(output_filename, results, tune_params, objective="time"):
56-
""" Store the obtained auto-tuning results in a JSON output file
59+
def make_filenamepath(filenamepath: Path):
60+
"""Create the given path to a filename if the path does not yet exist"""
61+
filepath = filenamepath.parents[0]
62+
if not filepath.exists():
63+
filepath.mkdir()
64+
65+
66+
def store_output_file(output_filename: str, results, tune_params, objective="time"):
67+
"""Store the obtained auto-tuning results in a JSON output file
5768
5869
This function produces a JSON file that adheres to the T4 auto-tuning output JSON schema.
5970
60-
:param output_filename: Name of the to be created output file
71+
:param output_filename: Name or 'path / name' of the to be created output file
6172
:type output_filename: string
6273
6374
:param results: Results list as return by tune_kernel
@@ -70,26 +81,20 @@ def store_output_file(output_filename, results, tune_params, objective="time"):
7081
:type objective: string
7182
7283
"""
73-
output_filename = filename_ensure_json_extension(output_filename)
84+
output_filenamepath = Path(filename_ensure_json_extension(output_filename))
85+
make_filenamepath(output_filenamepath)
7486

75-
timing_keys = [
76-
"compile_time", "benchmark_time", "framework_time", "strategy_time",
77-
"verification_time"
78-
]
79-
not_measurement_keys = list(
80-
tune_params.keys()) + timing_keys + ["timestamp"] + ["times"]
87+
timing_keys = ["compile_time", "benchmark_time", "framework_time", "strategy_time", "verification_time"]
88+
not_measurement_keys = list(tune_params.keys()) + timing_keys + ["timestamp"] + ["times"]
8189

8290
output_data = []
8391

8492
for result in results:
85-
8693
out = {}
8794

88-
out["timestamp"] = result["timestamp"]
89-
out["configuration"] = {
90-
k: v
91-
for k, v in result.items() if k in tune_params
92-
}
95+
if "timestamp" in result:
96+
out["timestamp"] = result["timestamp"]
97+
out["configuration"] = {k: v for k, v in result.items() if k in tune_params}
9398

9499
# collect configuration specific timings
95100
timings = dict()
@@ -98,7 +103,8 @@ def store_output_file(output_filename, results, tune_params, objective="time"):
98103
timings["framework"] = result["framework_time"]
99104
timings["search_algorithm"] = result["strategy_time"]
100105
timings["validation"] = result["verification_time"]
101-
timings["runtimes"] = result["times"]
106+
if "times" in result:
107+
timings["runtimes"] = result["times"]
102108
out["times"] = timings
103109

104110
# encode the validity of the configuration
@@ -112,10 +118,7 @@ def store_output_file(output_filename, results, tune_params, objective="time"):
112118
measurements = []
113119
for key, value in result.items():
114120
if key not in not_measurement_keys:
115-
measurements.append(
116-
dict(name=key,
117-
value=value,
118-
unit="ms" if key.startswith("time") else ""))
121+
measurements.append(dict(name=key, value=value, unit="ms" if key.startswith("time") else ""))
119122
out["measurements"] = measurements
120123

121124
# objectives
@@ -130,12 +133,12 @@ def store_output_file(output_filename, results, tune_params, objective="time"):
130133
# write output_data to a JSON file
131134
version, _ = output_file_schema("results")
132135
output_json = dict(results=output_data, schema_version=version)
133-
with open(output_filename, 'w+') as fh:
136+
with open(output_filenamepath, "w+") as fh:
134137
json.dump(output_json, fh)
135138

136139

137-
def get_dependencies(package='kernel_tuner'):
138-
""" Get the Python dependencies of Kernel Tuner currently installed and their version numbers """
140+
def get_dependencies(package="kernel_tuner"):
141+
"""Get the Python dependencies of Kernel Tuner currently installed and their version numbers"""
139142
requirements = requires(package)
140143
deps = [Requirement(req).name for req in requirements]
141144
depends = []
@@ -150,10 +153,9 @@ def get_dependencies(package='kernel_tuner'):
150153

151154

152155
def get_device_query(target):
153-
""" Get the information about GPUs in the current system, target is any of ['nvidia', 'amd'] """
156+
"""Get the information about GPUs in the current system, target is any of ['nvidia', 'amd']"""
154157
if target == "nvidia":
155-
nvidia_smi_out = subprocess.run(["nvidia-smi", "--query", "-x"],
156-
capture_output=True)
158+
nvidia_smi_out = subprocess.run(["nvidia-smi", "--query", "-x"], capture_output=True)
157159
nvidia_smi = xmltodict.parse(nvidia_smi_out.stdout)
158160
gpu_info = nvidia_smi["nvidia_smi_log"]["gpu"]
159161
del_key = "processes"
@@ -162,57 +164,81 @@ def get_device_query(target):
162164
for gpu in gpu_info:
163165
del gpu[del_key]
164166
elif isinstance(gpu_info, dict) and del_key in gpu_info:
165-
del gpu_info[del_key]
167+
del gpu_info[del_key]
166168
return nvidia_smi
167169
elif target == "amd":
168-
rocm_smi_out = subprocess.run(["rocm-smi", "--showallinfo", "--json"],
169-
capture_output=True)
170+
rocm_smi_out = subprocess.run(["rocm-smi", "--showallinfo", "--json"], capture_output=True)
170171
return json.loads(rocm_smi_out.stdout)
171172
else:
172173
raise ValueError("get_device_query target not supported")
173174

174175

175-
def store_metadata_file(metadata_filename):
176-
""" Store the metadata about the current hardware and software environment in a JSON output file
176+
def store_metadata_file(metadata_filename: str):
177+
"""Store the metadata about the current hardware and software environment in a JSON output file
177178
178179
This function produces a JSON file that adheres to the T4 auto-tuning metadata JSON schema.
179180
180-
:param metadata_filename: Name of the to be created metadata file
181+
:param metadata_filename: Name or 'path / name' of the to be created metadata file
181182
:type metadata_filename: string
182183
183184
"""
184-
metadata_filename = filename_ensure_json_extension(metadata_filename)
185+
metadata_filenamepath = Path(filename_ensure_json_extension(metadata_filename))
186+
make_filenamepath(metadata_filenamepath)
185187
metadata = {}
186188

187-
# lshw only works on Linux, this intentionally raises a FileNotFoundError when ran on systems that do not have it
188-
lshw_out = subprocess.run(["lshw", "-json"], capture_output=True)
189-
190-
# sometimes lshw outputs a list of length 1, sometimes just as a dict, schema wants a list
191-
lshw_string = lshw_out.stdout.decode('utf-8').strip()
192-
if lshw_string[0] == '{' and lshw_string[-1] == '}':
193-
lshw_string = '[' + lshw_string + ']'
189+
# differentiate between OSes, possible values: https://docs.python.org/3/library/sys.html#sys.platform
190+
if platform == "linux":
191+
os_string = "Linux"
192+
hardware_description_out = subprocess.run(["lshw", "-json"], capture_output=True)
193+
elif platform == "win32":
194+
os_string = "Windows"
195+
raise NotImplementedError(f"Hardware specification not yet implemented for Windows")
196+
elif platform == "darwin":
197+
os_string = "Mac"
198+
hardware_description_out = subprocess.run(
199+
[
200+
"system_profiler",
201+
"-json",
202+
"-detailLevel",
203+
"mini",
204+
"SPSoftwareDataType",
205+
"SPHardwareDataType",
206+
"SPiBridgeDataType",
207+
"SPPCIDataType",
208+
"SPMemoryDataType",
209+
"SPNVMeDataType",
210+
],
211+
capture_output=True,
212+
)
213+
else:
214+
raise ValueError(f"Platform {platform} not supported for metadata collection")
194215

195-
metadata["hardware"] = dict(lshw=json.loads(lshw_string))
216+
# process the hardware description output
217+
hardware_description_string = hardware_description_out.stdout.decode("utf-8").strip()
218+
if hardware_description_string[0] == "{" and hardware_description_string[-1] == "}":
219+
# sometimes lshw outputs a list of length 1, sometimes just as a dict, schema wants a list
220+
hardware_description_string = "[" + hardware_description_string + "]"
221+
metadata["hardware"] = dict(hardware_description=json.loads(hardware_description_string))
222+
metadata["operating_system"] = os_string
196223

197224
# attempts to use nvidia-smi or rocm-smi if present
198225
device_query = {}
199226
try:
200-
device_query['nvidia-smi'] = get_device_query("nvidia")
227+
device_query["nvidia-smi"] = get_device_query("nvidia")
201228
except FileNotFoundError:
202229
# ignore if nvidia-smi is not found
203230
pass
204231

205232
try:
206-
device_query['rocm-smi'] = get_device_query("amd")
233+
device_query["rocm-smi"] = get_device_query("amd")
207234
except FileNotFoundError:
208235
# ignore if rocm-smi is not found
209236
pass
210237

211-
metadata["environment"] = dict(device_query=device_query,
212-
requirements=get_dependencies())
238+
metadata["environment"] = dict(device_query=device_query, requirements=get_dependencies())
213239

214240
# write metadata to JSON file
215241
version, _ = output_file_schema("metadata")
216242
metadata_json = dict(metadata=metadata, schema_version=version)
217-
with open(metadata_filename, 'w+') as fh:
243+
with open(metadata_filenamepath, "w+") as fh:
218244
json.dump(metadata_json, fh, indent=" ")

0 commit comments

Comments
 (0)