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
401 lines
16 KiB
Python
Executable File
401 lines
16 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
|
|
|
|
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 cop-
|
|
ies 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 IM-
|
|
PLIED, 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 CONNE-
|
|
CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
|
|
# Add the module path for hipblaslt performance modules
|
|
# This script is installed in bin/, modules are in share/hipblaslt/performance/
|
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
install_prefix = os.path.dirname(script_dir)
|
|
module_dir = os.path.join(install_prefix, 'share', 'hipblaslt', 'performance')
|
|
if os.path.exists(module_dir):
|
|
sys.path.insert(0, module_dir)
|
|
else:
|
|
# Fallback: support invocation from src-tree for development
|
|
sys.path.insert(0, script_dir)
|
|
|
|
try:
|
|
import specs
|
|
except ImportError:
|
|
print(f"Error: Cannot find required modules: Set PYTHON_PATH={module_dir}")
|
|
sys.exit(1)
|
|
|
|
import pathlib
|
|
import argparse
|
|
import logging
|
|
import bench
|
|
from dataclasses import dataclass, field
|
|
from bench_sample import BenchSample, MeasurementKey
|
|
from generator import SuiteProblemGenerator, ShellScriptProblemGenerator
|
|
from pathlib import Path
|
|
from git_info import create_github_file
|
|
from specs import get_machine_specs
|
|
from typing import Dict, List
|
|
|
|
console = logging.StreamHandler()
|
|
|
|
# Output from bench exe msg:
|
|
matmul_paramList = ['function','transA','transB','grouped_gemm','batch_count','m','n','k','alpha','lda','stride_a','beta','ldb','stride_b',
|
|
'ldc','stride_c','ldd','stride_d','a_type','b_type','c_type','d_type','compute_type','scaleA','scaleB','scaleC',
|
|
'scaleD','amaxD','swizzle_a','swizzle_b','activation_type','bias_vector','bias_type','rotating_buffer','gflops','GB/s','us']
|
|
|
|
amax_paramList = ['function','m','n','type','dtype','us']
|
|
|
|
api_overhead_paramList = ['function','api_name','us/iter','best_us']
|
|
|
|
# maps of outputParamList
|
|
benchMsgOutParams = {'matmul' : matmul_paramList,
|
|
'amax' : amax_paramList,
|
|
'api_overhead' : api_overhead_paramList}
|
|
|
|
#
|
|
# The real key in csv file, we need to add sample-num, and corresponding mean_, median_ fields
|
|
#
|
|
matmul_probKey = ['function','transA','transB','grouped_gemm','batch_count','m','n','k','alpha','lda','stride_a','beta','ldb','stride_b',
|
|
'ldc','stride_c','ldd','stride_d','a_type','b_type','c_type','d_type','compute_type','scaleA','scaleB','scaleC',
|
|
'scaleD','amaxD','swizzle_a','swizzle_b','activation_type','bias_vector','bias_type','rotating_buffer']
|
|
|
|
amax_probKey = ['function','m','n','type','dtype']
|
|
|
|
api_overhead_probKey = ['function','api_name']
|
|
|
|
# perf-measure key fields of each function
|
|
benchProbDescKeys = {'matmul' : matmul_probKey,
|
|
'amax' : amax_probKey,
|
|
'api_overhead' : api_overhead_probKey}
|
|
|
|
# perf-measure key fields of each function
|
|
benchMeasuredKeys = {'matmul' : ['GB/s','gflops','us'],
|
|
'amax' : ['us'],
|
|
'api_overhead' : ['us/iter','best_us']}
|
|
|
|
sample_filed_name = 'sample_num'
|
|
|
|
#
|
|
# full csv-file key = benchProbDescKeys + 'sample_num' + <mean,median>-of-each-(benchMeasuredKeys) + benchMeasuredKeys
|
|
#
|
|
|
|
# maps of benchmark executable
|
|
benchExecs = {'matmul' : 'hipblaslt-bench',
|
|
'amax' : 'hipblaslt-bench-extop-amax',
|
|
'api_overhead' : 'hipblaslt-api-overhead'}
|
|
|
|
#d is default dict or contains a superset of msgOutputParams
|
|
def extractTrackedParams(d, trackedParam):
|
|
return [d[p] for p in trackedParam]
|
|
|
|
#d is default dict or contains a superset of ProbTokenKeys, returned string is a token of this problem
|
|
def extractProblemDescStr(d, probTokenKeys):
|
|
return ','.join([str(d[t]) for t in probTokenKeys])
|
|
|
|
def runBenchmark(benchType, probBenchResults:Dict[str, BenchSample], prob_args, executable_folder, probYamlFolder, out_csv_File, write_csv_header, total_samples, finalize = False):
|
|
|
|
# get the actual exec-path of this bench type
|
|
benchExec = benchExecs[benchType]
|
|
benchCmd = pathlib.Path(os.path.join(executable_folder, benchExec)).resolve()
|
|
|
|
csvKeys, benchResults, success = bench.run_bench(benchCmd, probYamlFolder, benchType, prob_args, True)
|
|
# get the actual params we want to track and show in csv of this bench type
|
|
# TODO- check csvKeys == msgOutputParams
|
|
msgOutputParams = benchMsgOutParams[benchType]
|
|
problemDescKeys = benchProbDescKeys[benchType]
|
|
measurementKeys = benchMeasuredKeys[benchType]
|
|
|
|
content = ''
|
|
# 1. Extract problem token
|
|
# 2. Extract perf measurement data and add single sample
|
|
# 3. Finalize- output mean, median and all data for each problem
|
|
for eachResult in benchResults:
|
|
# data in problem description
|
|
probDesc = extractProblemDescStr(eachResult, problemDescKeys)
|
|
if probDesc not in probBenchResults:
|
|
probBenchResults[probDesc] = BenchSample(probKey=probDesc)
|
|
benchSample = probBenchResults[probDesc]
|
|
for measuredKey in measurementKeys:
|
|
benchSample.addSampleOfKey(measuredKey, eachResult[measuredKey])
|
|
if finalize:
|
|
content += benchSample.finalize(total_samples, measurementKeys) + '\n'
|
|
|
|
header = ''
|
|
if write_csv_header is True:
|
|
measurements = []
|
|
for Mkey in measurementKeys:
|
|
measurements.append(MeasurementKey(Mkey))
|
|
header += ','.join([str(key) for key in problemDescKeys]) + ',' + sample_filed_name + ','
|
|
for measure in measurements:
|
|
header += measure.getMeanName() + ',' + measure.getMedianName() + ','
|
|
header += ','.join([m.name for m in measurements])+'\n'
|
|
|
|
if out_csv_File is not None:
|
|
if write_csv_header is True:
|
|
out_csv_File.write(header)
|
|
if finalize:
|
|
out_csv_File.write(content)
|
|
else:
|
|
if write_csv_header is True:
|
|
print(header)
|
|
if finalize:
|
|
print(content)
|
|
|
|
def run_sh_bench(arguments):
|
|
"""Run bench cmds from a .sh file"""
|
|
|
|
if arguments.workspace:
|
|
print(f'Output data to {arguments.workspace}')
|
|
else:
|
|
print("Workspace not set. use -w /path/of/workspace")
|
|
return
|
|
|
|
if arguments.execFolder:
|
|
print(f'Will call the hipblaslt-bench executable from folder {arguments.execFolder}')
|
|
else:
|
|
print("execFolder not set. use -e /path/of/execFolder")
|
|
return
|
|
|
|
sh_filepath = pathlib.Path(os.path.join(os.path.curdir, arguments.run_sh)).resolve()
|
|
if os.path.exists(sh_filepath):
|
|
print(f'Will run the bench commands from file: {sh_filepath}')
|
|
else:
|
|
print(f'Input sh filepath {sh_filepath} is not existing, abort. Please check')
|
|
return
|
|
|
|
out_folder = arguments.workspace
|
|
exec_folder = arguments.execFolder
|
|
bench_exec_path = os.path.join(exec_folder, "hipblaslt-bench")
|
|
generator = ShellScriptProblemGenerator(sh_filepath, bench_exec_path)
|
|
|
|
print(f'Note: When running with --run_sh, argument --suite, --tag, --samples and --pts will be ignored')
|
|
|
|
subDirectory = os.path.join(out_folder, "bench_output_csv")
|
|
Path(subDirectory).mkdir(parents=True, exist_ok=True)
|
|
filename, _ = os.path.splitext(os.path.basename(arguments.run_sh))
|
|
|
|
if arguments.csvfile is not None:
|
|
out_csv_filename = os.path.join(subDirectory, arguments.csvfile + '.csv')
|
|
else:
|
|
out_csv_filename = os.path.join(subDirectory, filename + '.csv')
|
|
print(f'Info: --csvfile not set, use sh-filename as csv-filename')
|
|
# print(f'Info: run_sh = {arguments.run_sh}')
|
|
# print(f'Info: sh filename = {filename}')
|
|
print(f'Info: Output csv file = {out_csv_filename}')
|
|
|
|
logging.info("Start bench.")
|
|
|
|
print("\n==================================\n|| Running bench sh file {}\n==================================".format(arguments.run_sh))
|
|
|
|
content = ''
|
|
csvKeys = ''
|
|
writeHeader = True
|
|
csv_file = open(out_csv_filename, 'w')
|
|
|
|
for singleCMD in generator.iterate_cmd():
|
|
# print("singleCMD: " + singleCMD)
|
|
csvKeys, benchResults, success = bench.run_sh_cmd(singleCMD, True)
|
|
for eachResult in benchResults:
|
|
# data in problem description
|
|
content = extractProblemDescStr(eachResult, csvKeys) + '\n'
|
|
if writeHeader:
|
|
header = ','.join([str(key) for key in csvKeys]) + '\n'
|
|
csv_file.write(header)
|
|
writeHeader = False
|
|
csv_file.write(content)
|
|
|
|
print(f'\nResults written to {csv_file.name}')
|
|
csv_file.close()
|
|
|
|
logging.info("Finish bench.")
|
|
|
|
def run_suite_bench(arguments, probYaml_folder):
|
|
"""Run suite bench"""
|
|
|
|
if arguments.workspace:
|
|
print(f'Output data to {arguments.workspace}')
|
|
else:
|
|
print("Workspace not set. use -w /path/of/workspace")
|
|
return
|
|
|
|
if arguments.execFolder:
|
|
print(f'Will call cpp bench executable from folder {arguments.execFolder}')
|
|
else:
|
|
print("execFolder not set. use -e /path/of/execFolder")
|
|
return
|
|
|
|
if arguments.suite is None:
|
|
print("test problems not set, use --suite for testing problems")
|
|
return
|
|
else:
|
|
# Pass problems_dir if using custom directory with --suite all
|
|
if arguments.problems_dir and 'all' in arguments.suite:
|
|
generator = SuiteProblemGenerator(arguments.suite, problems_dir=probYaml_folder)
|
|
else:
|
|
generator = SuiteProblemGenerator(arguments.suite)
|
|
|
|
out_folder = arguments.workspace
|
|
exec_folder = arguments.execFolder
|
|
num_samples = arguments.samples
|
|
|
|
needExportCSV = (arguments.csv == True) or (arguments.pts == True)
|
|
|
|
logging.info("Start bench.")
|
|
|
|
for problemSet in generator.generate_problemSet():
|
|
pSetName = problemSet.get_problemset_name()
|
|
print("\n==================================\n|| Running benchmarks suite {}\n==================================".format(pSetName))
|
|
pTypeName = problemSet.benchType
|
|
subDirectory = os.path.join(out_folder, "hipBLASLt_PTS_Benchmarks_"+pTypeName, arguments.tag)
|
|
Path(subDirectory).mkdir(parents=True, exist_ok=True)
|
|
|
|
# check if we need to output csv files
|
|
# outputName, _ = os.path.splitext(os.path.basename(filename))
|
|
out_csv_file = os.path.join(subDirectory, pSetName+'_benchmark.csv') if needExportCSV else ""
|
|
csv_file = None if out_csv_file == "" else open(out_csv_file, 'w')
|
|
|
|
# only the first time we need to write header
|
|
writeCSVHeader = True
|
|
curProbSet:Dict[str, BenchSample] = {}
|
|
for iter in range(num_samples):
|
|
print(f"\nSampling Iteration: {iter}")
|
|
finalize = (iter == num_samples - 1)
|
|
for p in problemSet.generate_problems():
|
|
runBenchmark(pTypeName, curProbSet, p.args, exec_folder, probYaml_folder, csv_file, writeCSVHeader, num_samples, finalize)
|
|
writeCSVHeader = False
|
|
|
|
if csv_file is not None:
|
|
print(f'\nResults written to {csv_file.name}')
|
|
csv_file.close()
|
|
|
|
# check if we need to output other files for pts
|
|
if arguments.pts == True:
|
|
# Will only be correct if script is run from directory of the git repo associated
|
|
# with the hipblaslt-bench executable
|
|
create_github_file(os.path.join(subDirectory, 'hipBLASLt-commit-hash.txt'))
|
|
get_machine_specs(os.path.join(subDirectory, 'specs.txt'))
|
|
|
|
logging.info("Finish bench.")
|
|
|
|
|
|
def main():
|
|
|
|
parser = argparse.ArgumentParser(prog='hipblaslt-perf')
|
|
|
|
dir_of_this_script = pathlib.Path(os.path.dirname(os.path.realpath(__file__))).resolve()
|
|
dir_of_this_repo = pathlib.Path(os.path.join(dir_of_this_script, '../../../')).resolve()
|
|
probYaml_folder = pathlib.Path(os.path.join(dir_of_this_script, 'problems/')).resolve()
|
|
executable_folder = pathlib.Path(os.path.join(dir_of_this_repo, "build/release/clients/")).resolve()
|
|
print(f'Info: path of this script = {dir_of_this_script}')
|
|
print(f'Info: path of this repo = {dir_of_this_repo}')
|
|
|
|
parser.add_argument('-w',
|
|
'--workspace',
|
|
type=str,
|
|
help='workspace folder keeping the perf data',
|
|
default=os.path.join(dir_of_this_repo, "hipBLASLt_benchmark/"))
|
|
|
|
# this argument is mainly used in jenkins/pts. For local dev, just use the default
|
|
parser.add_argument('-e',
|
|
'--execFolder',
|
|
type=str,
|
|
help='folder where the cpp bench executables are located',
|
|
default=executable_folder)
|
|
|
|
parser.add_argument('--run_sh',
|
|
type=str,
|
|
help='run a sh file calling a set of ./hipblaslt-bench commands',
|
|
default=None)
|
|
|
|
parser.add_argument('--csvfile',
|
|
type=str,
|
|
help='specify the output csv filename (without ext) when --run_sh, if not set, use [sh-filename].csv',
|
|
default=None)
|
|
|
|
parser.add_argument('--suite',
|
|
type=str,
|
|
action='append')
|
|
|
|
parser.add_argument('--targetArch',
|
|
type=str,
|
|
help='specify the target arch, mainly used when --suite all for PTS. If None, will use rocm_agent_enumerator to query',
|
|
default=None)
|
|
|
|
parser.add_argument('--tag',
|
|
type=str,
|
|
help='subfolder under workspace, (optional, useful for comparing two commits)',
|
|
default='')
|
|
|
|
parser.add_argument('--samples',
|
|
type=int,
|
|
help='number of runs of all benchmarks, used to calculate the significance value, default is 5',
|
|
default=5)
|
|
|
|
parser.add_argument('--csv',
|
|
help='dump result to csv files, default is True',
|
|
action='store_true',
|
|
default=True)
|
|
|
|
parser.add_argument('--pts',
|
|
help='dump several required files for pts system: csv, commit-hash, spec, default is False',
|
|
action='store_true',
|
|
default=False)
|
|
|
|
parser.add_argument('-p',
|
|
'--problems_dir',
|
|
type=str,
|
|
help='custom directory containing problem YAML files (only works with --suite all)',
|
|
default=None)
|
|
|
|
arguments = parser.parse_args()
|
|
|
|
SuiteProblemGenerator.target_arch_static = arguments.targetArch
|
|
|
|
# Handle custom problems directory
|
|
if arguments.problems_dir:
|
|
custom_prob_dir = pathlib.Path(arguments.problems_dir).resolve()
|
|
if not custom_prob_dir.exists():
|
|
print(f'Error: Problems directory {custom_prob_dir} does not exist')
|
|
sys.exit(1)
|
|
if not custom_prob_dir.is_dir():
|
|
print(f'Error: {custom_prob_dir} is not a directory')
|
|
sys.exit(1)
|
|
probYaml_folder = custom_prob_dir
|
|
print(f'Info: Using custom problems directory = {probYaml_folder}')
|
|
else:
|
|
print(f'Info: path of bench yaml problems = {probYaml_folder}')
|
|
|
|
if arguments.run_sh is not None:
|
|
run_sh_bench(arguments)
|
|
else:
|
|
run_suite_bench(arguments, probYaml_folder)
|
|
|
|
sys.exit(0)
|
|
|
|
if __name__=='__main__':
|
|
logging.basicConfig(filename='hipblaslt-perf.log',
|
|
format='%(asctime)s %(levelname)s: %(message)s',
|
|
level=logging.DEBUG)
|
|
|
|
console.setLevel(logging.WARNING)
|
|
console.setFormatter(logging.Formatter('%(levelname)-8s: %(message)s'))
|
|
logging.getLogger('').addHandler(console)
|
|
|
|
main()
|