1 /* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include <stdint.h>
17 
18 #include "tensorflow/lite/micro/all_ops_resolver.h"
19 #include "tensorflow/lite/micro/benchmarks/keyword_scrambled_model_data.h"
20 #include "tensorflow/lite/micro/micro_error_reporter.h"
21 #include "tensorflow/lite/micro/recording_micro_allocator.h"
22 #include "tensorflow/lite/micro/recording_micro_interpreter.h"
23 #include "tensorflow/lite/micro/testing/micro_test.h"
24 #include "tensorflow/lite/micro/testing/test_conv_model.h"
25 
26 /**
27  * Tests to ensure arena memory allocation does not regress by more than 3%.
28  */
29 
30 namespace {
31 
32 // Ensure memory doesn't expand more that 3%:
33 constexpr float kAllocationThreshold = 0.03;
34 
35 // TODO(b/160617245): Record persistent allocations to provide a more accurate
36 // number here.
37 constexpr float kAllocationTailMiscCeiling = 2 * 1024;
38 
39 const bool kIs64BitSystem = (sizeof(void*) == 8);
40 
41 constexpr int kKeywordModelTensorArenaSize = 22 * 1024;
42 uint8_t keyword_model_tensor_arena[kKeywordModelTensorArenaSize];
43 
44 constexpr int kKeywordModelTensorCount = 54;
45 constexpr int kKeywordModelNodeAndRegistrationCount = 15;
46 
47 // NOTE: These values are measured on x86-64:
48 // TODO(b/158651472): Consider auditing these values on non-64 bit systems.
49 //
50 // Run this test with '--copt=-DTF_LITE_STATIC_MEMORY' to get optimized memory
51 // runtime values:
52 #ifdef TF_LITE_STATIC_MEMORY
53 constexpr int kKeywordModelTotalSize = 14416;
54 constexpr int kKeywordModelTailSize = 13744;
55 constexpr int kKeywordModelPersistentTfLiteTensorDataSize = 128;
56 constexpr int kKeywordModelPersistentBufferDataSize = 564;
57 #else
58 constexpr int kKeywordModelTotalSize = 14992;
59 constexpr int kKeywordModelTailSize = 14320;
60 constexpr int kKeywordModelPersistentTfLiteTensorDataSize = 224;
61 constexpr int kKeywordModelPersistentBufferDataSize = 564;
62 #endif
63 constexpr int kKeywordModelHeadSize = 672;
64 constexpr int kKeywordModelTfLiteTensorVariableBufferDataSize = 10240;
65 constexpr int kKeywordModelPersistentTfLiteTensorQuantizationData = 64;
66 constexpr int kKeywordModelOpRuntimeDataSize = 148;
67 
68 constexpr int kTestConvModelArenaSize = 12 * 1024;
69 uint8_t test_conv_tensor_arena[kTestConvModelArenaSize];
70 
71 constexpr int kTestConvModelTensorCount = 15;
72 constexpr int kTestConvModelNodeAndRegistrationCount = 7;
73 
74 // NOTE: These values are measured on x86-64:
75 // TODO(b/158651472): Consider auditing these values on non-64 bit systems.
76 #ifdef TF_LITE_STATIC_MEMORY
77 constexpr int kTestConvModelTotalSize = 9792;
78 constexpr int kTestConvModelTailSize = 2048;
79 constexpr int kTestConvModelPersistentTfLiteTensorDataSize = 128;
80 constexpr int kTestConvModelPersistentBufferDataSize = 680;
81 #else
82 constexpr int kTestConvModelTotalSize = 10112;
83 constexpr int kTestConvModelTailSize = 2368;
84 constexpr int kTestConvModelPersistentTfLiteTensorDataSize = 224;
85 constexpr int kTestConvModelPersistentBufferDataSize = 680;
86 #endif
87 constexpr int kTestConvModelHeadSize = 7744;
88 constexpr int kTestConvModelOpRuntimeDataSize = 136;
89 constexpr int kTestConvModelPersistentTfLiteTensorQuantizationData = 0;
90 
91 struct ModelAllocationThresholds {
92   size_t tensor_count = 0;
93   size_t node_and_registration_count = 0;
94   size_t total_alloc_size = 0;
95   size_t head_alloc_size = 0;
96   size_t tail_alloc_size = 0;
97   size_t tensor_variable_buffer_data_size = 0;
98   size_t persistent_tflite_tensor_data_size = 0;
99   size_t persistent_tflite_tensor_quantization_data_size = 0;
100   size_t op_runtime_data_size = 0;
101   size_t persistent_buffer_data = 0;
102 };
103 
EnsureAllocatedSizeThreshold(const char * allocation_type,size_t actual,size_t expected)104 void EnsureAllocatedSizeThreshold(const char* allocation_type, size_t actual,
105                                   size_t expected) {
106   // TODO(b/158651472): Better auditing of non-64 bit systems:
107   if (kIs64BitSystem) {
108     // 64-bit systems should check floor and ceiling to catch memory savings:
109     TF_LITE_MICRO_EXPECT_NEAR(actual, expected,
110                               expected * kAllocationThreshold);
111     if (actual != expected) {
112       TF_LITE_REPORT_ERROR(tflite::GetMicroErrorReporter(),
113                            "%s threshold failed: %d != %d", allocation_type,
114                            actual, expected);
115     }
116   } else {
117     // Non-64 bit systems should just expect allocation does not exceed the
118     // ceiling:
119     TF_LITE_MICRO_EXPECT_LE(actual, expected + expected * kAllocationThreshold);
120   }
121 }
122 
ValidateModelAllocationThresholds(const tflite::RecordingMicroAllocator & allocator,const ModelAllocationThresholds & thresholds)123 void ValidateModelAllocationThresholds(
124     const tflite::RecordingMicroAllocator& allocator,
125     const ModelAllocationThresholds& thresholds) {
126   allocator.PrintAllocations();
127 
128   EnsureAllocatedSizeThreshold(
129       "Total", allocator.GetSimpleMemoryAllocator()->GetUsedBytes(),
130       thresholds.total_alloc_size);
131   EnsureAllocatedSizeThreshold(
132       "Head", allocator.GetSimpleMemoryAllocator()->GetHeadUsedBytes(),
133       thresholds.head_alloc_size);
134   EnsureAllocatedSizeThreshold(
135       "Tail", allocator.GetSimpleMemoryAllocator()->GetTailUsedBytes(),
136       thresholds.tail_alloc_size);
137   EnsureAllocatedSizeThreshold(
138       "TfLiteEvalTensor",
139       allocator
140           .GetRecordedAllocation(
141               tflite::RecordedAllocationType::kTfLiteEvalTensorData)
142           .used_bytes,
143       sizeof(TfLiteEvalTensor) * thresholds.tensor_count);
144   EnsureAllocatedSizeThreshold(
145       "VariableBufferData",
146       allocator
147           .GetRecordedAllocation(
148               tflite::RecordedAllocationType::kTfLiteTensorVariableBufferData)
149           .used_bytes,
150       thresholds.tensor_variable_buffer_data_size);
151   EnsureAllocatedSizeThreshold(
152       "PersistentTfLiteTensor",
153       allocator
154           .GetRecordedAllocation(
155               tflite::RecordedAllocationType::kPersistentTfLiteTensorData)
156           .used_bytes,
157       thresholds.persistent_tflite_tensor_data_size);
158   EnsureAllocatedSizeThreshold(
159       "PersistentTfliteTensorQuantizationData",
160       allocator
161           .GetRecordedAllocation(tflite::RecordedAllocationType::
162                                      kPersistentTfLiteTensorQuantizationData)
163           .used_bytes,
164       thresholds.persistent_tflite_tensor_quantization_data_size);
165   EnsureAllocatedSizeThreshold(
166       "PersistentBufferData",
167       allocator
168           .GetRecordedAllocation(
169               tflite::RecordedAllocationType::kPersistentBufferData)
170           .used_bytes,
171       thresholds.persistent_buffer_data);
172   EnsureAllocatedSizeThreshold(
173       "NodeAndRegistration",
174       allocator
175           .GetRecordedAllocation(
176               tflite::RecordedAllocationType::kNodeAndRegistrationArray)
177           .used_bytes,
178       sizeof(tflite::NodeAndRegistration) *
179           thresholds.node_and_registration_count);
180 
181   // Ensure tail allocation recording is not missing any large chunks:
182   size_t tail_est_length = sizeof(TfLiteEvalTensor) * thresholds.tensor_count +
183                            thresholds.tensor_variable_buffer_data_size +
184                            sizeof(tflite::NodeAndRegistration) *
185                                thresholds.node_and_registration_count +
186                            thresholds.op_runtime_data_size;
187   TF_LITE_MICRO_EXPECT_LE(thresholds.tail_alloc_size - tail_est_length,
188                           kAllocationTailMiscCeiling);
189 }
190 
191 }  // namespace
192 
193 TF_LITE_MICRO_TESTS_BEGIN
194 
TF_LITE_MICRO_TEST(TestKeywordModelMemoryThreshold)195 TF_LITE_MICRO_TEST(TestKeywordModelMemoryThreshold) {
196   tflite::AllOpsResolver all_ops_resolver;
197   tflite::RecordingMicroInterpreter interpreter(
198       tflite::GetModel(g_keyword_scrambled_model_data), all_ops_resolver,
199       keyword_model_tensor_arena, kKeywordModelTensorArenaSize,
200       tflite::GetMicroErrorReporter());
201 
202   interpreter.AllocateTensors();
203 
204   ModelAllocationThresholds thresholds;
205   thresholds.tensor_count = kKeywordModelTensorCount;
206   thresholds.node_and_registration_count =
207       kKeywordModelNodeAndRegistrationCount;
208   thresholds.total_alloc_size = kKeywordModelTotalSize;
209   thresholds.head_alloc_size = kKeywordModelHeadSize;
210   thresholds.tail_alloc_size = kKeywordModelTailSize;
211   thresholds.tensor_variable_buffer_data_size =
212       kKeywordModelTfLiteTensorVariableBufferDataSize;
213   thresholds.op_runtime_data_size = kKeywordModelOpRuntimeDataSize;
214   thresholds.persistent_buffer_data = kKeywordModelPersistentBufferDataSize;
215   thresholds.persistent_tflite_tensor_data_size =
216       kKeywordModelPersistentTfLiteTensorDataSize;
217   thresholds.persistent_tflite_tensor_quantization_data_size =
218       kKeywordModelPersistentTfLiteTensorQuantizationData;
219 
220   ValidateModelAllocationThresholds(interpreter.GetMicroAllocator(),
221                                     thresholds);
222 }
223 
TF_LITE_MICRO_TEST(TestConvModelMemoryThreshold)224 TF_LITE_MICRO_TEST(TestConvModelMemoryThreshold) {
225   tflite::AllOpsResolver all_ops_resolver;
226   tflite::RecordingMicroInterpreter interpreter(
227       tflite::GetModel(kTestConvModelData), all_ops_resolver,
228       test_conv_tensor_arena, kTestConvModelArenaSize,
229       tflite::GetMicroErrorReporter());
230 
231   interpreter.AllocateTensors();
232 
233   ModelAllocationThresholds thresholds;
234   thresholds.tensor_count = kTestConvModelTensorCount;
235   thresholds.node_and_registration_count =
236       kTestConvModelNodeAndRegistrationCount;
237   thresholds.total_alloc_size = kTestConvModelTotalSize;
238   thresholds.head_alloc_size = kTestConvModelHeadSize;
239   thresholds.tail_alloc_size = kTestConvModelTailSize;
240   thresholds.op_runtime_data_size = kTestConvModelOpRuntimeDataSize;
241   thresholds.persistent_buffer_data = kTestConvModelPersistentBufferDataSize;
242   thresholds.persistent_tflite_tensor_data_size =
243       kTestConvModelPersistentTfLiteTensorDataSize;
244   thresholds.persistent_tflite_tensor_quantization_data_size =
245       kTestConvModelPersistentTfLiteTensorQuantizationData;
246 
247   ValidateModelAllocationThresholds(interpreter.GetMicroAllocator(),
248                                     thresholds);
249 }
250 
251 TF_LITE_MICRO_TESTS_END
252