################################################################################ # # Copyright (C) 2021-2022 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 # 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. # ################################################################################ import os import copy import yaml from Tensile.Utilities.ConditionalImports import yamlDumper, yamlLoader import math class BenchmarkSplitter(object): """ Benchmark splitter class Loads in a benchmark yaml file and splits it into several smaller benchmarks limited by a number of problem sizes entries. """ @staticmethod def __readConfigFile(benchmarkConfigFile): with open(benchmarkConfigFile) as f: data = yaml.load(f, yamlLoader) return data # data: a loaded .yaml file # returns: a list of yaml files that # are differentiated by the benchmark problem @staticmethod def __splitByProblem(data): rv = [] problemKey = "BenchmarkProblems" for i in range(len(data[problemKey])): result = {} for k in data.keys(): if k == problemKey: result[k] = [copy.deepcopy(data[k][i])] else: result[k] = copy.deepcopy(data[k]) rv.append(result) return rv # data: a loaded .yaml file, containing one benchmark problem section # returns: a list of yaml files that are differentiated by the # benchmark groupings @staticmethod def __splitByBenchmarkGroup(data): rv = [] problemKey = "BenchmarkProblems" assert len(data[problemKey]) == 1, "Config file must have one BenchmarkProblems group" benchmarkProblems = data[problemKey][0] # Find the index of the problem type group problemIdx = -1 for i in range(len(benchmarkProblems)): if("OperationType" in benchmarkProblems[i].keys()): problemIdx = i break assert problemIdx != -1, "Could not find problem type group" # Split files on the benchmark group for i in range(len(benchmarkProblems)): if i != problemIdx: result = {} for k in data.keys(): if k == problemKey: # Take only the problem group and one benchmarkgroup result[k] = [[copy.deepcopy(benchmarkProblems[problemIdx]), \ copy.deepcopy(benchmarkProblems[i])]] else: # copy other sections verbatim result[k] = copy.deepcopy(data[k]) rv.append(result) return rv # data: a loaded .yaml file, containing one benchmark problem section # and one benchmark group section # returns: a list of yaml files that are differentiated by the # benchmark sizes @staticmethod def __splitByBenchmarkSizes(data, numSizes=1): rv = [] problemKey = "BenchmarkProblems" assert len(data[problemKey]) == 1, "Config file must have one BenchmarkProblems group" benchmarkProblems = data[problemKey][0] # Find the index of the problem type group # and the benchmark group problemIdx = -1 benchmarkIdx = -1 for i in range(len(benchmarkProblems)): groupKeys = benchmarkProblems[i].keys() if("OperationType" in groupKeys): problemIdx = i elif("BenchmarkFinalParameters" in groupKeys): benchmarkIdx = i assert len(benchmarkProblems) == 2 \ and problemIdx != -1 \ and benchmarkIdx != -1, \ "Config file must have one ProblemType group and one Benchmark group" # Grab the problem sizes from the Benchmark group benchmarkGroup = benchmarkProblems[benchmarkIdx] assert "ProblemSizes" in benchmarkGroup["BenchmarkFinalParameters"][0] \ and len(benchmarkGroup["BenchmarkFinalParameters"][0]["ProblemSizes"]), \ "Benchmark group must have non-empty ProblemSizes" problemSizesGroup = benchmarkGroup["BenchmarkFinalParameters"][0]["ProblemSizes"] problemSizesCount = len(problemSizesGroup) numFiles = math.ceil(problemSizesCount / numSizes) # Split files on the benchmark sizes for i in range(numFiles): result = {} for k in data.keys(): if k == problemKey: # Create a new benchmark group that has all the old keys but split the sizes. newBenchmarkGroup = {} for bk in benchmarkGroup.keys(): if bk == "BenchmarkFinalParameters": newBenchmarkGroup[bk] = [ {"ProblemSizes": [] } ] for j in range(i*numSizes, min((i+1)*numSizes, problemSizesCount)): newBenchmarkGroup[bk][0]["ProblemSizes"].append(copy.deepcopy(problemSizesGroup[j])) else: newBenchmarkGroup[bk] = copy.deepcopy(benchmarkGroup[bk]) result[k] = [[copy.deepcopy(benchmarkProblems[problemIdx]), copy.deepcopy(newBenchmarkGroup)]] else: result[k] = copy.deepcopy(data[k]) rv.append(result) return rv # filePath: Name of the file (can be a path) # suffix: Add a suffix of _## to fileName by default # formatting: How the stuffix string is to be treated. # If more indices are needed, you can increase them on the formatting. @staticmethod def __appendFileNameSuffix(filePath, suffix, separator="_", formatting="{:02}"): root, ext = os.path.splitext(filePath) suffixString = (separator + formatting).format(suffix) return root + suffixString + ext @staticmethod def splitBenchmarkBySizes(configFile, outputDir, numSizes=1, baseFileName="", separator="_", suffixFormat="{:02}"): # Use the configFile as base name if none provided if baseFileName == "": baseFileName = os.path.basename(configFile) # If we want to split by sizes, then we must first split files # into separate problems, then split those files into separate # benchmark groups, then split those files into sizes data = BenchmarkSplitter.__readConfigFile(configFile) benchmarksByProblem = BenchmarkSplitter.__splitByProblem(data) benchmarksByGroup = [] benchmarksBySize = [] for problem in benchmarksByProblem: benchmarksByGroup += BenchmarkSplitter.__splitByBenchmarkGroup(problem) for group in benchmarksByGroup: benchmarksBySize += BenchmarkSplitter.__splitByBenchmarkSizes(group, numSizes) # outputDir/basefileName_XX.ext outputFileBase = os.path.join(outputDir, baseFileName) for i in range(len(benchmarksBySize)): outFileName = BenchmarkSplitter.__appendFileNameSuffix(outputFileBase, i, separator, suffixFormat) with open(outFileName, "w") as f: yaml.dump(benchmarksBySize[i], f, yamlDumper)