1# Copyright © 2020 Arm Ltd. All rights reserved. 2# SPDX-License-Identifier: MIT 3""" 4This file contains functions relating to the use of the Arm NN profiler within PyArmNN. 5""" 6import json 7from collections import namedtuple 8 9ProfilerData = namedtuple('ProfilerData', ['inference_data', 'per_workload_execution_data']) 10ProfilerData.__doc__ = """Container to hold the profiling inference data, and the profiling data per workload. 11 12Contains: 13 inference_data (dict): holds end-to-end inference performance data. Keys: 14 'time_unit' - timer units. 15 'execution_time' - list of total inference execution times for each inference run. 16 per_workload_execution_data (dict): holds per operation performance data, key is a operation name 17 Each operation has 18 'time_unit' - timer units. 19 'execution_time' - list of total execution times for each inference run. 20 'backend' - backend used for this operation. 21 22Examples: 23 24 >>> data = get_profiling_data(profiler) 25 >>> print(data) 26 >>> ProfilerData(inference_data={'time_unit': 'us', 27 'execution_time': [8901372.972]}, 28 per_workload_execution_data={'CopyMemGeneric_Execute_#3': {'time_unit': 'us', 29 'execution_time': [28.941], 30 'backend': 'Unknown'}, 31 'RefConvolution2dWorkload_Execute_#5': {'time_unit': 'us', 32 'execution_time': [126838.071], 33 'backend': 'CpuRef'}, 34 'RefDepthwiseConvolution2dWorkload_Execute_#6': {'time_unit': 'us', 35 'execution_time': [49886.208], 36 'backend': 'CpuRef'} 37 ...etc 38 } 39 ) 40""" 41 42 43def get_profiling_data(profiler: 'IProfiler') -> ProfilerData: 44 """Reads IProfiler object passed in, extracts the relevant data 45 and returns it in a ProfilerData container. 46 47 Args: 48 profiler (IProfiler): The IProfiler object to be parsed. 49 50 Returns: 51 ProfilerData: A container containing the relevant data extracted from the Profiler output. 52 """ 53 54 top_level_dict = json.loads(profiler.as_json()) 55 armnn_data = top_level_dict["ArmNN"] 56 #Get the inference measurements dict, this will be just one value for key starting with "inference_measurements" 57 inference_measurements = [v for k, v in armnn_data.items() if k.startswith("inference_measurements_")][0] 58 59 #Get the execution data dict, this will be just one value for key starting with "Execute_" 60 execution_data = [v for k, v in inference_measurements.items() if k.startswith("Execute_")][0] 61 62 workload_data = {} 63 inference_data = {} 64 for exec_key, exec_value in execution_data.items(): 65 # Check all items with a type. 66 if "type" in exec_value and exec_value["type"] == "Event": 67 for event_key, event_value in exec_value.items(): 68 if event_key.startswith("Wall clock time_#") and event_value["type"] == "Measurement": 69 time_data = __get_wall_clock_times__(event_value) 70 time_data["backend"] = __get_backend(exec_key) 71 workload_data[exec_key] = time_data 72 # This is the total inference time map 73 if exec_key.startswith("Wall clock time_#") and exec_value["type"] == "Measurement": 74 time_data = __get_wall_clock_times__(exec_value) 75 inference_data.update(time_data) 76 return ProfilerData(inference_data=inference_data, per_workload_execution_data=workload_data) 77 78 79def __get_wall_clock_times__(wall_clock_item): 80 execution_times = wall_clock_item["raw"] 81 time_data = {} 82 raw_data = [] 83 for time in execution_times: 84 raw_data.append(time) 85 time_data["time_unit"] = wall_clock_item["unit"] 86 time_data["execution_time"] = raw_data 87 return time_data 88 89 90def __get_backend(exec_key): 91 if "ref" in exec_key.lower(): 92 return "CpuRef" 93 elif "neon" in exec_key.lower(): 94 return "CpuAcc" 95 elif "cl" in exec_key.lower(): 96 return "GpuAcc" 97 elif "ethos" in exec_key.lower(): 98 return "EthosNAcc" 99 else: 100 return "Unknown"