1// 2// model_event_logger_impl.mm 3// 4// Copyright © 2024 Apple Inc. All rights reserved. 5// 6// Please refer to the license found in the LICENSE file in the root directory of the source tree. 7 8#import "model_event_logger_impl.h" 9 10#import "ETCoreMLModelStructurePath.h" 11#import "ETCoreMLOperationProfilingInfo.h" 12#import <executorch/runtime/core/event_tracer.h> 13#import "objc_array_util.h" 14#import <mach/mach_time.h> 15#import <numeric> 16#import "MLMultiArray_Copy.h" 17 18namespace { 19 20using namespace executorch::runtime; 21 22using executorch::aten::ScalarType; 23using executorch::aten::Tensor; 24using executorch::aten::TensorImpl; 25 26uint64_t time_units_to_nano_seconds(uint64_t time_units) { 27 static mach_timebase_info_data_t info; 28 static dispatch_once_t onceToken; 29 dispatch_once(&onceToken, ^{ 30 NSCAssert(mach_timebase_info(&info) == KERN_SUCCESS, @"ModelEventLogger: Failed to get time base."); 31 }); 32 33 return time_units * info.numer / info.denom; 34} 35 36std::optional<ScalarType> to_scalar_type(MLMultiArrayDataType data_type) { 37 switch (data_type) { 38 case MLMultiArrayDataTypeFloat16: { 39 return ScalarType::Half; 40 } 41 case MLMultiArrayDataTypeFloat32: { 42 return ScalarType::Float; 43 } 44 case MLMultiArrayDataTypeDouble: { 45 return ScalarType::Double; 46 } 47 case MLMultiArrayDataTypeInt32: { 48 return ScalarType::Int; 49 } 50 default: { 51 return std::nullopt; 52 } 53 } 54} 55 56MLMultiArrayDataType get_supported_data_type(MLMultiArrayDataType data_type) { 57 switch (data_type) { 58 case MLMultiArrayDataTypeFloat16: { 59 return MLMultiArrayDataTypeFloat32; 60 } 61 default: { 62 return data_type; 63 } 64 } 65} 66 67bool is_packed(NSArray<NSNumber *> *shape, NSArray<NSNumber *> *strides) { 68 if (shape.count == 0) { 69 return true; 70 } 71 size_t product = 1; 72 for (size_t i = shape.count; i > 0; i--) { 73 if (![strides[i - 1] isEqual:@(product)]) { 74 return false; 75 } 76 product *= shape[i - 1].unsignedLongValue; 77 } 78 79 return true; 80} 81} 82 83namespace executorchcoreml { 84 85void ModelEventLoggerImpl::log_profiling_infos(NSDictionary<ETCoreMLModelStructurePath *, ETCoreMLOperationProfilingInfo *> *op_path_to_profiling_info_map, 86 NSDictionary<ETCoreMLModelStructurePath *, NSString *> *op_path_to_debug_symbol_name_map) const noexcept { 87 [op_path_to_profiling_info_map enumerateKeysAndObjectsUsingBlock:^(ETCoreMLModelStructurePath *path, 88 ETCoreMLOperationProfilingInfo *profiling_info, 89 BOOL * _Nonnull __unused stop) { 90 if (profiling_info.estimatedExecutionEndTime == profiling_info.estimatedExecutionStartTime) { 91 return; 92 } 93 uint64_t estimated_execution_start_time_in_ns = time_units_to_nano_seconds(profiling_info.estimatedExecutionStartTime); 94 uint64_t estimated_execution_end_time_in_ns = time_units_to_nano_seconds(profiling_info.estimatedExecutionEndTime); 95 NSString *symbol_name = op_path_to_debug_symbol_name_map[path]; 96 if (symbol_name == nil) { 97 // We will use the operation output name as a placeholder for now. 98 symbol_name = [NSString stringWithFormat:@"%@:%@", profiling_info.outputNames.firstObject, profiling_info.operatorName]; 99 } 100 NSData *metadata = profiling_info.metadata; 101 tracer_->log_profiling_delegate(symbol_name.UTF8String, 102 -1, 103 estimated_execution_start_time_in_ns, 104 estimated_execution_end_time_in_ns, 105 metadata.bytes, 106 metadata.length); 107 108 }]; 109} 110 111void ModelEventLoggerImpl::log_intermediate_tensors(NSDictionary<ETCoreMLModelStructurePath *, MLMultiArray *> *op_path_to_value_map, 112 NSDictionary<ETCoreMLModelStructurePath*, NSString*> *op_path_to_debug_symbol_name_map) const noexcept { 113 [op_path_to_value_map enumerateKeysAndObjectsUsingBlock:^(ETCoreMLModelStructurePath *path, 114 MLMultiArray *intermediate_value, 115 BOOL * _Nonnull __unused stop) { 116 using namespace executorch::runtime; 117 118 @autoreleasepool { 119 NSString *debug_symbol = op_path_to_debug_symbol_name_map[path]; 120 if (debug_symbol == nil) { 121 return; 122 } 123 124 MLMultiArray *value = op_path_to_value_map[path]; 125 if (value == nil || value.count == 0) { 126 return; 127 } 128 129 MLMultiArray *supported_value = value; 130 NSArray<NSNumber *> *shape = supported_value.shape; 131 NSError *local_error = nil; 132 MLMultiArrayDataType data_type = get_supported_data_type(value.dataType); 133 134 if (!is_packed(shape, value.strides) || (supported_value.dataType != data_type)) { 135 supported_value = [[MLMultiArray alloc] initWithShape:shape 136 dataType:data_type 137 error:&local_error]; 138 NSCAssert(supported_value != nil, 139 @"ModelEventLoggerImpl: Failed to create packed multiarray with shape=%@, dataType=%ld, error=%@.", 140 shape, 141 static_cast<long>(value.dataType), 142 local_error); 143 [value copyInto:supported_value]; 144 } 145 146 147 [supported_value getBytesWithHandler:^(const void * _Nonnull bytes, NSInteger size) { 148 auto sizes = to_vector<TensorImpl::SizesType>(shape); 149 auto strides = to_vector<TensorImpl::StridesType>(supported_value.strides); 150 auto scalar_type = to_scalar_type(data_type); 151 auto dim_order = std::vector<TensorImpl::DimOrderType>(shape.count); 152 std::iota(std::begin(dim_order), std::end(dim_order), 0); 153 154 NSCAssert(scalar_type.has_value(), @"ModelEventLoggerImpl: MultiArray dataType=%ld is not supported.", static_cast<long>(data_type)); 155 auto tensor_impl = TensorImpl( 156 scalar_type.value(), 157 static_cast<ssize_t>(sizes.size()), 158 sizes.data(), 159 const_cast<void *>(bytes), 160 dim_order.data(), 161 strides.data()); 162 auto tensor = Tensor(&tensor_impl); 163 tracer_->log_intermediate_output_delegate(debug_symbol.UTF8String, -1, tensor); 164 }]; 165 } 166 }]; 167} 168} // namespace executorchcoreml 169