xref: /aosp_15_r20/external/executorch/backends/apple/coreml/runtime/sdk/model_event_logger_impl.mm (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
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