# Copyright (c) Microsoft Corporation. # Licensed under the MIT license. """Module of the cudnn functions benchmarks.""" import os import json import yaml import statistics from superbench.common.utils import logger from superbench.benchmarks import Platform, BenchmarkRegistry, ReturnCode from superbench.benchmarks.micro_benchmarks import MicroBenchmarkWithInvoke class CudnnBenchmark(MicroBenchmarkWithInvoke): """The cudnn performance benchmark class.""" def __init__(self, name, parameters=''): """Constructor. Args: name (str): benchmark name. parameters (str): benchmark parameters. """ super().__init__(name, parameters) self.__default_params_dict_list = [ { 'name': 'cudnnConvolutionBackwardFilter', 'algo': 0, 'inputDims': [32, 128, 14, 14], 'inputStride': [25088, 196, 14, 1], 'inputType': 0, 'outputDims': [32, 32, 14, 14], 'outputStride': [6272, 196, 14, 1], 'convType': 0, 'tensorOp': False, 'arrayLength': 2, 'padA': [1, 1], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'filterDims': [32, 128, 3, 3], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardFilter', 'algo': 1, 'inputDims': [32, 128, 14, 14], 'inputStride': [25088, 196, 14, 1], 'inputType': 2, 'outputDims': [32, 32, 14, 14], 'outputStride': [6272, 196, 14, 1], 'convType': 0, 'tensorOp': True, 'arrayLength': 2, 'padA': [1, 1], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'filterDims': [32, 128, 3, 3], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardFilter', 'algo': 1, 'inputDims': [32, 256, 14, 14], 'inputStride': [50176, 196, 14, 1], 'inputType': 0, 'outputDims': [32, 1024, 14, 14], 'outputStride': [200704, 196, 14, 1], 'convType': 0, 'tensorOp': False, 'arrayLength': 2, 'padA': [0, 0], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'filterDims': [1024, 256, 1, 1], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardFilter', 'algo': 1, 'inputDims': [32, 256, 14, 14], 'inputStride': [50176, 196, 14, 1], 'inputType': 2, 'outputDims': [32, 1024, 14, 14], 'outputStride': [200704, 196, 14, 1], 'convType': 0, 'tensorOp': True, 'arrayLength': 2, 'padA': [0, 0], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'filterDims': [1024, 256, 1, 1], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardFilter', 'algo': 1, 'inputDims': [32, 512, 14, 14], 'inputStride': [100352, 196, 14, 1], 'inputType': 0, 'outputDims': [32, 512, 14, 14], 'outputStride': [100352, 196, 14, 1], 'convType': 0, 'tensorOp': False, 'arrayLength': 2, 'padA': [1, 1], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'filterDims': [512, 512, 3, 3], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardFilter', 'algo': 1, 'inputDims': [32, 512, 14, 14], 'inputStride': [100352, 196, 14, 1], 'inputType': 2, 'outputDims': [32, 512, 14, 14], 'outputStride': [100352, 196, 14, 1], 'convType': 0, 'tensorOp': True, 'arrayLength': 2, 'padA': [1, 1], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'filterDims': [512, 512, 3, 3], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardData', 'algo': 1, 'filterDims': [32, 128, 3, 3], 'inputType': 2, 'inputDims': [32, 32, 14, 14], 'inputStride': [6272, 196, 14, 1], 'outputDims': [32, 128, 14, 14], 'outputStride': [25088, 196, 14, 1], 'convType': 0, 'tensorOp': True, 'arrayLength': 2, 'padA': [1, 1], 'dilationA': [1, 1], 'filterStrideA': [1, 1], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardData', 'algo': 4, 'filterDims': [32, 128, 3, 3], 'inputType': 0, 'inputDims': [32, 32, 14, 14], 'inputStride': [6272, 196, 14, 1], 'outputDims': [32, 128, 14, 14], 'outputStride': [25088, 196, 14, 1], 'convType': 0, 'tensorOp': False, 'arrayLength': 2, 'padA': [1, 1], 'dilationA': [1, 1], 'filterStrideA': [1, 1], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardData', 'algo': 1, 'filterDims': [1024, 256, 1, 1], 'inputType': 0, 'inputDims': [32, 1024, 14, 14], 'inputStride': [200704, 196, 14, 1], 'outputDims': [32, 256, 14, 14], 'outputStride': [50176, 196, 14, 1], 'convType': 0, 'tensorOp': False, 'arrayLength': 2, 'padA': [0, 0], 'dilationA': [1, 1], 'filterStrideA': [1, 1], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardData', 'algo': 1, 'filterDims': [1024, 256, 1, 1], 'inputType': 2, 'inputDims': [32, 1024, 14, 14], 'inputStride': [200704, 196, 14, 1], 'outputDims': [32, 256, 14, 14], 'outputStride': [50176, 196, 14, 1], 'convType': 0, 'tensorOp': True, 'arrayLength': 2, 'padA': [0, 0], 'dilationA': [1, 1], 'filterStrideA': [1, 1], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardData', 'algo': 1, 'filterDims': [512, 512, 3, 3], 'inputType': 0, 'inputDims': [32, 512, 14, 14], 'inputStride': [100352, 196, 14, 1], 'outputDims': [32, 512, 14, 14], 'outputStride': [100352, 196, 14, 1], 'convType': 0, 'tensorOp': False, 'arrayLength': 2, 'padA': [1, 1], 'dilationA': [1, 1], 'filterStrideA': [1, 1], 'mode': 1 }, { 'name': 'cudnnConvolutionBackwardData', 'algo': 1, 'filterDims': [512, 512, 3, 3], 'inputType': 2, 'inputDims': [32, 512, 14, 14], 'inputStride': [100352, 196, 14, 1], 'outputDims': [32, 512, 14, 14], 'outputStride': [100352, 196, 14, 1], 'convType': 0, 'tensorOp': True, 'arrayLength': 2, 'padA': [1, 1], 'dilationA': [1, 1], 'filterStrideA': [1, 1], 'mode': 1 }, { 'name': 'cudnnConvolutionForward', 'inputDims': [32, 128, 14, 14], 'inputStride': [25088, 196, 14, 1], 'filterDims': [32, 128, 3, 3], 'outputDims': [32, 32, 14, 14], 'outputStride': [6272, 196, 14, 1], 'arrayLength': 2, 'padA': [1, 1], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'mode': 1, 'tensorOp': False, 'inputType': 0, 'convType': 0, 'algo': 6 }, { 'name': 'cudnnConvolutionForward', 'inputDims': [32, 128, 14, 14], 'inputStride': [25088, 196, 14, 1], 'filterDims': [32, 128, 3, 3], 'outputDims': [32, 32, 14, 14], 'outputStride': [6272, 196, 14, 1], 'arrayLength': 2, 'padA': [1, 1], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'mode': 1, 'tensorOp': True, 'inputType': 2, 'convType': 0, 'algo': 1 }, { 'name': 'cudnnConvolutionForward', 'inputDims': [32, 256, 14, 14], 'inputStride': [50176, 196, 14, 1], 'filterDims': [1024, 256, 1, 1], 'outputDims': [32, 1024, 14, 14], 'outputStride': [200704, 196, 14, 1], 'arrayLength': 2, 'padA': [0, 0], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'mode': 1, 'tensorOp': False, 'inputType': 0, 'convType': 0, 'algo': 1 }, { 'name': 'cudnnConvolutionForward', 'inputDims': [32, 256, 14, 14], 'inputStride': [50176, 196, 14, 1], 'filterDims': [1024, 256, 1, 1], 'outputDims': [32, 1024, 14, 14], 'outputStride': [200704, 196, 14, 1], 'arrayLength': 2, 'padA': [0, 0], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'mode': 1, 'tensorOp': True, 'inputType': 2, 'convType': 0, 'algo': 1 }, { 'name': 'cudnnConvolutionForward', 'inputDims': [32, 512, 14, 14], 'inputStride': [100352, 196, 14, 1], 'filterDims': [512, 512, 3, 3], 'outputDims': [32, 512, 14, 14], 'outputStride': [100352, 196, 14, 1], 'arrayLength': 2, 'padA': [1, 1], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'mode': 1, 'tensorOp': False, 'inputType': 0, 'convType': 0, 'algo': 1 }, { 'name': 'cudnnConvolutionForward', 'inputDims': [32, 512, 14, 14], 'inputStride': [100352, 196, 14, 1], 'filterDims': [512, 512, 3, 3], 'outputDims': [32, 512, 14, 14], 'outputStride': [100352, 196, 14, 1], 'arrayLength': 2, 'padA': [1, 1], 'filterStrideA': [1, 1], 'dilationA': [1, 1], 'mode': 1, 'tensorOp': True, 'inputType': 2, 'convType': 0, 'algo': 1 } ] self._bin_name = 'cudnn_benchmark' def add_parser_arguments(self): """Add the specified arguments.""" super().add_parser_arguments() self._parser.add_argument( '--num_warmup', type=int, default=8, required=False, help='The number of warmup step.', ) self._parser.add_argument( '--num_steps', type=int, default=100, required=False, help='The number of test step.', ) self._parser.add_argument( '--num_in_step', type=int, default=1000, required=False, help='The number of functions in one step.', ) self._parser.add_argument( '--random_seed', type=int, default=33931, required=False, help='The random seed to fill in the data of the function.', ) self._parser.add_argument( '--config_json_str', type=str, nargs='+', default=None, required=False, help='The custom json string defining the params in a cudnn function.', ) def _preprocess(self): """Preprocess/preparation operations before the benchmarking. Return: True if _preprocess() succeed. """ if not super()._preprocess(): return False self._args.tolerant_fail = True command = os.path.join(self._args.bin_dir, self._bin_name) command += (' --num_test ' + str(self._args.num_steps)) command += (' --warm_up ' + str(self._args.num_warmup)) command += (' --num_in_step ' + str(self._args.num_in_step)) command += (' --random_seed ' + str(self._args.random_seed)) try: if not self._args.config_json_str: for config_dict in self.__default_params_dict_list: config_json_str = "\'" + json.dumps(config_dict).replace(' ', '') + "\'" complete_command = command + (' --config_json ') + config_json_str self._commands.append(complete_command) else: if not isinstance(self._args.config_json_str, list): self._args.config_json_str = [self._args.config_json_str] for config_json_str in self._args.config_json_str: custom_config_str = yaml.safe_load(config_json_str) config_json_str = "\'" + json.dumps(custom_config_str).replace(' ', '') + "\'" complete_command = command + (' --config_json ') + config_json_str self._commands.append(complete_command) except BaseException as e: logger.error('Invalid input params - benchmark: {}, message: {}'.format(self._name, str(e))) self._result.set_return_code(ReturnCode.INVALID_ARGUMENT) return False return True def _process_raw_result(self, cmd_idx, raw_output): """Function to process raw results and save the summarized results. self._result.add_raw_data() and self._result.add_result() need to be called to save the results. Args: cmd_idx (int): the index of command corresponding with the raw_output. raw_output (str): raw output string of the micro-benchmark. Return: True if the raw output string is valid and result can be extracted. """ self._result.add_raw_data('raw_output_' + str(cmd_idx), raw_output, self._args.log_raw_data) metric = '' try: lines = raw_output.splitlines() cmd_config = json.loads(self._commands[cmd_idx].split('--config_json')[-1].replace(' ', '')[1:-1]) for key in sorted(cmd_config.keys()): if 'name' in key: metric = key + '_' + str(cmd_config[key]) + metric else: metric = metric + '_' + key + '_' + str(cmd_config[key]) metric = metric.replace(' ', '').replace(',', '_') error = False raw_data = [] for line in lines: if '[raw_data]' in line: raw_data = line[line.index('[raw_data]: ') + len('[raw_data]: '):] raw_data = raw_data.split(',') raw_data.pop() raw_data = [float(item) for item in raw_data] self._result.add_result(metric.lower() + '_time', statistics.mean(raw_data) * 1000) self._result.add_raw_data(metric.lower() + '_time', raw_data, self._args.log_raw_data) if 'Error' in line: error = True except BaseException as e: logger.error( 'Cannot extract results from cudnn functions - round: {}, index of cmd: {}, \ benchmark: {}, raw data: {}, message: {}'.format( self._curr_run_index, cmd_idx, self._name, raw_output, str(e) ) ) error = True if error: logger.error( 'Error in running cudnn test - round: {}, index of cmd: {}, benchmark: {}, raw data: {}'.format( self._curr_run_index, cmd_idx, self._name, raw_output ) ) self._result.add_result(metric.lower() + '_time', -1) return False return True BenchmarkRegistry.register_benchmark('cudnn-function', CudnnBenchmark, platform=Platform.CUDA)