CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
387 lines
13 KiB
Python
Executable File
387 lines
13 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
###############################################################################
|
|
#
|
|
# MIT License
|
|
#
|
|
# Copyright (c) 2023 Advanced Micro Devices, Inc.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in all
|
|
# copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
#
|
|
#################################################################################
|
|
"""Performance tracking script"""
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import argparse
|
|
import csv
|
|
import time
|
|
from decimal import Decimal
|
|
import multiprocessing as mp
|
|
|
|
curr_path = os.path.abspath(os.getcwd())
|
|
default_results_path = curr_path + "/perf_results"
|
|
TOLERANCE = -10 # tolerance 10%
|
|
|
|
re_Elapsed = re.compile(r"(\d*\.*\d+)")
|
|
re_Solver = re.compile(r"^MIOpen .* Algorithm: (\d+), Solution: (\d+)/(\w+)")
|
|
re_GPU = re.compile(r"^GPU Kernel Time .* Elapsed: (\d+\.\d+) ms")
|
|
re_Key = re.compile(r"^.*Key match: ([\w\-]*)")
|
|
|
|
queue = mp.Queue() # gpu idx queue, used in HIP_VISIBLE_DEVICES
|
|
results_queue = mp.Queue()
|
|
|
|
|
|
class Manager(mp.Process):
|
|
"""Queue manager"""
|
|
|
|
def __init__(self, **kwargs):
|
|
allowed_keys = set(["filename", "install_path", "override", "results_path"])
|
|
self.filename = None
|
|
self.install_path = None
|
|
self.override = False
|
|
self.results_path = f"{default_results_path}"
|
|
self.__dict__.update(
|
|
(key, value) for key, value in kwargs.items() if key in allowed_keys
|
|
)
|
|
|
|
self.in_qs = {}
|
|
self.num_gpus = int(self.get_num_gpus())
|
|
self.resfile = f"{self.results_path}/{self.filename}"
|
|
print(self.resfile)
|
|
print("install_path: ", self.install_path)
|
|
self.model_path = (
|
|
f"{self.install_path}/share/miopen/perf_models/{self.filename}"
|
|
)
|
|
self.driver_cmds = []
|
|
self.set_driver_cmds()
|
|
self.writer = None
|
|
|
|
def get_num_gpus(self):
|
|
"""Get num_gpus"""
|
|
cmd = "/opt/rocm/bin/rocminfo | grep 'Device Type: *GPU' | wc -l"
|
|
proc = subprocess.Popen(
|
|
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
|
)
|
|
output = proc.communicate()[0]
|
|
return int(output.decode("utf-8").strip())
|
|
|
|
def set_driver_cmds(self):
|
|
if self.override:
|
|
var_list = self.override.split(",")
|
|
var_str = ""
|
|
for var in var_list:
|
|
var_str += var + " &&"
|
|
|
|
with open(os.path.expanduser(self.model_path), "r", encoding="utf-8") as infile:
|
|
for line in infile:
|
|
try:
|
|
if line.find("MIOpenDriver") == -1:
|
|
print(f"Skipping line '{line}'")
|
|
continue
|
|
idx = line.index("MIOpenDriver")
|
|
driver_cmd = line[idx:-1]
|
|
if self.override:
|
|
cmd = (
|
|
f"export LD_LIBRARY_PATH={self.install_path}/lib && export MIOPEN_LOG_LEVEL=6 && "
|
|
f"export MIOPEN_SYSTEM_DB_PATH={self.install_path}/share/miopen/db && "
|
|
f"export HIP_VISIBLE_DEVICES=GPU_ID && "
|
|
f"{var_str} "
|
|
f"{self.install_path}/bin/{driver_cmd} -V 0 -i 10 -w 1 -t 1 -G 1"
|
|
)
|
|
else:
|
|
cmd = (
|
|
f"export LD_LIBRARY_PATH={self.install_path}/lib && export MIOPEN_LOG_LEVEL=6 && "
|
|
f"export MIOPEN_SYSTEM_DB_PATH={self.install_path}/share/miopen/db && "
|
|
f"export HIP_VISIBLE_DEVICES=GPU_ID && "
|
|
f"{self.install_path}/bin/{driver_cmd} -V 0 -i 10 -w 1 -t 1 -G 1"
|
|
)
|
|
print(f"Appending cm: {cmd}")
|
|
self.driver_cmds.append(cmd)
|
|
|
|
except Exception as err:
|
|
print(f"Could not get driver commands: {err}")
|
|
|
|
print("#driver commands: ", len(self.driver_cmds))
|
|
|
|
def run(self):
|
|
"""Main function to launch worker pool"""
|
|
for gpu_id in range(self.num_gpus):
|
|
queue.put(gpu_id)
|
|
self.launch_pool()
|
|
|
|
def write_to_file(self, results):
|
|
"""Write results to csv file"""
|
|
field_names = [
|
|
"Driver",
|
|
"k_time",
|
|
"wall_time",
|
|
"solver_id",
|
|
"solver_name",
|
|
"fdb_key",
|
|
]
|
|
with open(os.path.expanduser(self.resfile), "w+", encoding="utf-8") as outfile:
|
|
self.writer = csv.DictWriter(outfile, fieldnames=field_names)
|
|
self.writer.writeheader()
|
|
try:
|
|
self.writer.writerows(results)
|
|
print(f"Perf results written to: {self.resfile}")
|
|
except Exception as exp:
|
|
print(exp)
|
|
|
|
def launch_pool(self):
|
|
"""Launch pool of driver cmds"""
|
|
pool = mp.Pool(processes=self.num_gpus)
|
|
for result in pool.imap_unordered(self.run_driver_cmd, self.driver_cmds):
|
|
self.parse_result(result)
|
|
pool.close()
|
|
print(f"Size of results Q: {results_queue.qsize()}")
|
|
results = []
|
|
while not results_queue.empty():
|
|
results.append(results_queue.get())
|
|
results.sort(key=lambda x: x["Driver"])
|
|
self.write_to_file(results)
|
|
|
|
def parse_result(self, result):
|
|
"""Potential to use result as it becomes available"""
|
|
# print(result)
|
|
|
|
def run_driver_cmd(self, driver_cmd):
|
|
"""Launch each driver cmd in subproc"""
|
|
while queue.empty():
|
|
time.sleep(2)
|
|
print("GPUs busy, sleeping")
|
|
gpu_id = queue.get()
|
|
cmd = driver_cmd.replace("GPU_ID", str(gpu_id))
|
|
try:
|
|
print("")
|
|
print(f"Starting process on GPU {gpu_id}")
|
|
print(cmd)
|
|
|
|
res_dict = {}
|
|
proc = subprocess.Popen(
|
|
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
|
)
|
|
p_out = proc.stdout.readlines()
|
|
res = None
|
|
e = Entry()
|
|
for line in p_out:
|
|
line = line.decode("utf-8")
|
|
line = line.strip()
|
|
if (line.find("MIOpenDriver") != -1) and (
|
|
line.find("MIOpen:") == -1
|
|
): # fragile solution
|
|
e.cmd = line
|
|
print(e.cmd)
|
|
continue
|
|
if line.find("Wall-clock Time") != -1:
|
|
res = re_Elapsed.findall(line)
|
|
print(res)
|
|
e.wall_elapsed = res[0]
|
|
e.wall_aux = res[1]
|
|
e.wall_gwss = res[2]
|
|
continue
|
|
if re_Solver.match(line):
|
|
res = re_Solver.findall(line)[0]
|
|
print(res)
|
|
e.algo = res[0]
|
|
e.sol_id = res[1]
|
|
e.sol_name = res[2]
|
|
continue
|
|
if re_Key.match(line):
|
|
e.fdb_key = re_Key.findall(line)[0]
|
|
if re_GPU.match(line):
|
|
res = re_GPU.findall(line)
|
|
e.sol_time = res[0]
|
|
print("k_time: ", e.sol_time)
|
|
if line.find("error") != -1:
|
|
raise ValueError(p_out)
|
|
|
|
res_dict = {
|
|
"Driver": e.cmd,
|
|
"k_time": e.sol_time,
|
|
"wall_time": e.wall_elapsed,
|
|
"solver_id": e.sol_id,
|
|
"solver_name": e.sol_name,
|
|
"fdb_key": e.fdb_key,
|
|
}
|
|
|
|
results_queue.put(res_dict)
|
|
ret = res_dict
|
|
finally:
|
|
queue.put(gpu_id)
|
|
return ret
|
|
|
|
|
|
class Entry:
|
|
"""Module to hold runtime values"""
|
|
|
|
def __init__(self):
|
|
self.cmd = ""
|
|
self.wall_elapsed = ""
|
|
self.wall_aux = ""
|
|
self.wall_gwss = ""
|
|
self.algo = ""
|
|
self.sol_id = ""
|
|
self.sol_name = ""
|
|
self.sol_time = ""
|
|
self.fdb_key = ""
|
|
|
|
def __str__(self):
|
|
atrs = [
|
|
self.cmd,
|
|
self.wall_elapsed,
|
|
self.wall_aux,
|
|
self.wall_gwss,
|
|
self.algo,
|
|
self.sol_id,
|
|
self.sol_name,
|
|
self.sol_time,
|
|
self.fdb_key,
|
|
]
|
|
return ",".join(atrs)
|
|
|
|
|
|
def parse_args():
|
|
"""Function to parse cmd line arguments"""
|
|
parser = argparse.ArgumentParser()
|
|
group1 = parser.add_mutually_exclusive_group()
|
|
|
|
parser.add_argument(
|
|
"--filename",
|
|
dest="filename",
|
|
type=str,
|
|
required=True,
|
|
help="Specify filename for model",
|
|
)
|
|
group1.add_argument(
|
|
"--install_path", dest="install_path", help="Specify MIOpenDriver install path"
|
|
)
|
|
group1.add_argument(
|
|
"--compare_results",
|
|
dest="compare_results",
|
|
action="store_true",
|
|
help="Compare current perf results to old perf results",
|
|
)
|
|
parser.add_argument(
|
|
"--old_results_path",
|
|
dest="old_results_path",
|
|
type=str,
|
|
help="Specify full path to old results directory",
|
|
)
|
|
parser.add_argument(
|
|
"--results_path",
|
|
dest="results_path",
|
|
default=default_results_path,
|
|
type=str,
|
|
help="Specify full path to output results directory",
|
|
)
|
|
parser.add_argument(
|
|
"--override", dest="override", type=str, help="Specify driver cmd env vars"
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if args.compare_results and not args.old_results_path:
|
|
parser.error("--old_results_path and --compare_results must both be specified")
|
|
|
|
return args
|
|
|
|
|
|
def compare_results(args):
|
|
"""Compare current results with previous results"""
|
|
if not os.path.exists(args.results_path):
|
|
raise ValueError(f"Results path does not exist {args.results_path}")
|
|
if not os.path.exists(args.old_results_path):
|
|
raise ValueError(f"Old results path does not exist {args.old_results_path}")
|
|
|
|
if not compare_file(
|
|
f"{args.results_path}/{args.filename}",
|
|
f"{args.old_results_path}/{args.filename}",
|
|
):
|
|
raise ValueError(f"FAILED: {args.filename}")
|
|
print(f"PASSED: {args.filename}")
|
|
|
|
|
|
def compare_file(new_results, old_results):
|
|
"""Compare kernel_time in new vs old results file"""
|
|
ret = True
|
|
print(f"Comparing results from: {new_results} to {old_results}")
|
|
with open(new_results, "r", encoding="utf-8") as new, open(
|
|
old_results, "r", encoding="utf-8"
|
|
) as old:
|
|
csv_new = list(csv.DictReader(new))
|
|
csv_new.sort(key=lambda x: x["Driver"])
|
|
csv_old = list(csv.DictReader(old))
|
|
csv_old.sort(key=lambda x: x["Driver"])
|
|
for line_new, line_old in zip(csv_new, csv_old):
|
|
if line_new["Driver"] != line_old["Driver"]:
|
|
print(f"New driver: {line_new['Driver']}")
|
|
print(f"Old driver: {line_old['Driver']}")
|
|
raise ValueError("Result files are out of sync")
|
|
if line_new["solver_name"] != line_old["solver_name"]:
|
|
print(
|
|
f"Winning solver changed from {line_new['solver_name']} to {line_old['solver_name']}"
|
|
)
|
|
speedup = (
|
|
(Decimal(line_old["k_time"]) - Decimal(line_new["k_time"]))
|
|
/ Decimal(line_old["k_time"])
|
|
* 100
|
|
)
|
|
if int(speedup) < TOLERANCE:
|
|
print(f"{line_new['Driver']}")
|
|
print(f"Speedup: {speedup}%")
|
|
print(
|
|
f"FAILED for new k_time: {line_new['k_time']} - old k_time: {line_old['k_time']}\n"
|
|
)
|
|
ret = False
|
|
|
|
return ret
|
|
|
|
|
|
def main():
|
|
"""Main function"""
|
|
args = parse_args()
|
|
|
|
if args.compare_results:
|
|
try:
|
|
compare_results(args)
|
|
except Exception as ex:
|
|
print(f"ERR: {ex}")
|
|
return False
|
|
else:
|
|
if not os.path.exists(args.results_path):
|
|
os.makedirs(args.results_path)
|
|
|
|
try:
|
|
manager = Manager(
|
|
filename=args.filename,
|
|
install_path=args.install_path,
|
|
override=args.override,
|
|
results_path=args.results_path,
|
|
)
|
|
manager.run()
|
|
except Exception as ex:
|
|
print(f"ERR: {ex}")
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|