blob: 495f3ecdb6e0d32ed88fad2dba8a041252e8088e [file] [log] [blame]
Slava Shklyaev73ee79d2019-05-14 14:15:14 +01001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "GeneratedTestHarness.h"
18
19#include <android-base/logging.h>
20#include <android/hardware/neuralnetworks/1.0/IDevice.h>
21#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
22#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
23#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
24#include <android/hardware/neuralnetworks/1.0/types.h>
25#include <android/hardware/neuralnetworks/1.1/IDevice.h>
26#include <android/hardware/neuralnetworks/1.2/IDevice.h>
27#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
28#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
29#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
30#include <android/hidl/allocator/1.0/IAllocator.h>
31#include <android/hidl/memory/1.0/IMemory.h>
32#include <hidlmemory/mapping.h>
33
34#include <iostream>
35
36#include "1.0/Utils.h"
37#include "1.2/Callbacks.h"
38#include "ExecutionBurstController.h"
39#include "MemoryUtils.h"
40#include "TestHarness.h"
41#include "Utils.h"
42
43namespace android {
44namespace hardware {
45namespace neuralnetworks {
46namespace generated_tests {
47
48using ::android::hardware::neuralnetworks::V1_0::ErrorStatus;
49using ::android::hardware::neuralnetworks::V1_0::Request;
50using ::android::hardware::neuralnetworks::V1_0::RequestArgument;
51using ::android::hardware::neuralnetworks::V1_1::ExecutionPreference;
Michael Butler3835f612019-07-11 15:43:22 -070052using ::android::hardware::neuralnetworks::V1_2::Constant;
Slava Shklyaev73ee79d2019-05-14 14:15:14 +010053using ::android::hardware::neuralnetworks::V1_2::IDevice;
54using ::android::hardware::neuralnetworks::V1_2::IPreparedModel;
Michael Butler3835f612019-07-11 15:43:22 -070055using ::android::hardware::neuralnetworks::V1_2::MeasureTiming;
Slava Shklyaev73ee79d2019-05-14 14:15:14 +010056using ::android::hardware::neuralnetworks::V1_2::Model;
Michael Butler3835f612019-07-11 15:43:22 -070057using ::android::hardware::neuralnetworks::V1_2::OutputShape;
58using ::android::hardware::neuralnetworks::V1_2::Timing;
Slava Shklyaev73ee79d2019-05-14 14:15:14 +010059using ::android::hardware::neuralnetworks::V1_2::implementation::ExecutionCallback;
60using ::android::hardware::neuralnetworks::V1_2::implementation::PreparedModelCallback;
61using ::android::hidl::memory::V1_0::IMemory;
62using ::test_helper::compare;
63using ::test_helper::expectMultinomialDistributionWithinTolerance;
64using ::test_helper::filter;
65using ::test_helper::for_all;
66using ::test_helper::for_each;
67using ::test_helper::MixedTyped;
68using ::test_helper::MixedTypedExample;
69using ::test_helper::resize_accordingly;
70using HidlToken = hidl_array<uint8_t, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
71
72static bool isZeroSized(const MixedTyped& example, uint32_t index) {
73 for (auto i : example.operandDimensions.at(index)) {
74 if (i == 0) return true;
75 }
76 return false;
77}
78
79static Return<ErrorStatus> ExecutePreparedModel(sp<IPreparedModel>& preparedModel,
80 const Request& request, MeasureTiming measure,
81 sp<ExecutionCallback>& callback) {
82 return preparedModel->execute_1_2(request, measure, callback);
83}
84static Return<ErrorStatus> ExecutePreparedModel(sp<IPreparedModel>& preparedModel,
85 const Request& request, MeasureTiming measure,
86 hidl_vec<OutputShape>* outputShapes,
87 Timing* timing) {
88 ErrorStatus result;
89 Return<void> ret = preparedModel->executeSynchronously(
90 request, measure,
91 [&result, outputShapes, timing](ErrorStatus error, const hidl_vec<OutputShape>& shapes,
92 const Timing& time) {
93 result = error;
94 *outputShapes = shapes;
95 *timing = time;
96 });
97 if (!ret.isOk()) {
98 return ErrorStatus::GENERAL_FAILURE;
99 }
100 return result;
101}
102static std::shared_ptr<::android::nn::ExecutionBurstController> CreateBurst(
103 const sp<IPreparedModel>& preparedModel) {
104 return ::android::nn::ExecutionBurstController::create(preparedModel, /*blocking=*/true);
105}
106enum class Executor { ASYNC, SYNC, BURST };
107enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT };
108const float kDefaultAtol = 1e-5f;
109const float kDefaultRtol = 1e-5f;
110void EvaluatePreparedModel(sp<IPreparedModel>& preparedModel, std::function<bool(int)> is_ignored,
111 const std::vector<MixedTypedExample>& examples,
112 bool hasRelaxedFloat32Model, float fpAtol, float fpRtol,
113 Executor executor, MeasureTiming measure, OutputType outputType) {
114 const uint32_t INPUT = 0;
115 const uint32_t OUTPUT = 1;
116
117 int example_no = 1;
118 for (auto& example : examples) {
119 SCOPED_TRACE(example_no++);
120 const MixedTyped& inputs = example.operands.first;
121 const MixedTyped& golden = example.operands.second;
122
123 const bool hasFloat16Inputs = !inputs.float16Operands.empty();
124 if (hasRelaxedFloat32Model || hasFloat16Inputs) {
125 // TODO: Adjust the error limit based on testing.
126 // If in relaxed mode, set the absolute tolerance to be 5ULP of FP16.
127 fpAtol = 5.0f * 0.0009765625f;
128 // Set the relative tolerance to be 5ULP of the corresponding FP precision.
129 fpRtol = 5.0f * 0.0009765625f;
130 }
131
132 std::vector<RequestArgument> inputs_info, outputs_info;
133 uint32_t inputSize = 0, outputSize = 0;
134 // This function only partially specifies the metadata (vector of RequestArguments).
135 // The contents are copied over below.
136 for_all(inputs, [&inputs_info, &inputSize](int index, auto, auto s) {
137 if (inputs_info.size() <= static_cast<size_t>(index)) inputs_info.resize(index + 1);
138 RequestArgument arg = {
139 .location = {.poolIndex = INPUT,
140 .offset = 0,
141 .length = static_cast<uint32_t>(s)},
142 .dimensions = {},
143 };
144 RequestArgument arg_empty = {
145 .hasNoValue = true,
146 };
147 inputs_info[index] = s ? arg : arg_empty;
148 inputSize += s;
149 });
150 // Compute offset for inputs 1 and so on
151 {
152 size_t offset = 0;
153 for (auto& i : inputs_info) {
154 if (!i.hasNoValue) i.location.offset = offset;
155 offset += i.location.length;
156 }
157 }
158
159 MixedTyped test; // holding test results
160
161 // Go through all outputs, initialize RequestArgument descriptors
162 resize_accordingly(golden, test);
163 bool sizeLargerThanOne = true;
164 for_all(golden, [&golden, &outputs_info, &outputSize, &outputType, &sizeLargerThanOne](
165 int index, auto, auto s) {
166 if (outputs_info.size() <= static_cast<size_t>(index)) outputs_info.resize(index + 1);
167 if (index == 0) {
168 // On OutputType::INSUFFICIENT, set the output operand with index 0 with
169 // buffer size one byte less than needed.
170 if (outputType == OutputType::INSUFFICIENT) {
171 if (s > 1 && !isZeroSized(golden, index)) {
172 s -= 1;
173 } else {
174 sizeLargerThanOne = false;
175 }
176 }
177 }
178 RequestArgument arg = {
179 .location = {.poolIndex = OUTPUT,
180 .offset = 0,
181 .length = static_cast<uint32_t>(s)},
182 .dimensions = {},
183 };
184 outputs_info[index] = arg;
185 outputSize += s;
186 });
187 // If output0 does not have size larger than one byte,
188 // we can not provide an insufficient buffer
189 if (!sizeLargerThanOne && outputType == OutputType::INSUFFICIENT) return;
190 // Compute offset for outputs 1 and so on
191 {
192 size_t offset = 0;
193 for (auto& i : outputs_info) {
194 i.location.offset = offset;
195 offset += i.location.length;
196 }
197 }
198 std::vector<hidl_memory> pools = {nn::allocateSharedMemory(inputSize),
199 nn::allocateSharedMemory(outputSize)};
200 ASSERT_NE(0ull, pools[INPUT].size());
201 ASSERT_NE(0ull, pools[OUTPUT].size());
202
203 // load data
204 sp<IMemory> inputMemory = mapMemory(pools[INPUT]);
205 sp<IMemory> outputMemory = mapMemory(pools[OUTPUT]);
206 ASSERT_NE(nullptr, inputMemory.get());
207 ASSERT_NE(nullptr, outputMemory.get());
208 char* inputPtr = reinterpret_cast<char*>(static_cast<void*>(inputMemory->getPointer()));
209 char* outputPtr = reinterpret_cast<char*>(static_cast<void*>(outputMemory->getPointer()));
210 ASSERT_NE(nullptr, inputPtr);
211 ASSERT_NE(nullptr, outputPtr);
212 inputMemory->update();
213 outputMemory->update();
214
215 // Go through all inputs, copy the values
216 for_all(inputs, [&inputs_info, inputPtr](int index, auto p, auto s) {
217 char* begin = (char*)p;
218 char* end = begin + s;
219 // TODO: handle more than one input
220 std::copy(begin, end, inputPtr + inputs_info[index].location.offset);
221 });
222
223 inputMemory->commit();
224 outputMemory->commit();
225
226 const Request request = {.inputs = inputs_info, .outputs = outputs_info, .pools = pools};
227
228 ErrorStatus executionStatus;
229 hidl_vec<OutputShape> outputShapes;
230 Timing timing;
231 switch (executor) {
232 case Executor::ASYNC: {
233 SCOPED_TRACE("asynchronous");
234
235 // launch execution
236 sp<ExecutionCallback> executionCallback = new ExecutionCallback();
237 ASSERT_NE(nullptr, executionCallback.get());
238 Return<ErrorStatus> executionLaunchStatus =
239 ExecutePreparedModel(preparedModel, request, measure, executionCallback);
240 ASSERT_TRUE(executionLaunchStatus.isOk());
241 EXPECT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(executionLaunchStatus));
242
243 // retrieve execution status
244 executionCallback->wait();
245 executionStatus = executionCallback->getStatus();
246 outputShapes = executionCallback->getOutputShapes();
247 timing = executionCallback->getTiming();
248
249 break;
250 }
251 case Executor::SYNC: {
252 SCOPED_TRACE("synchronous");
253
254 // execute
255 Return<ErrorStatus> executionReturnStatus = ExecutePreparedModel(
256 preparedModel, request, measure, &outputShapes, &timing);
257 ASSERT_TRUE(executionReturnStatus.isOk());
258 executionStatus = static_cast<ErrorStatus>(executionReturnStatus);
259
260 break;
261 }
262 case Executor::BURST: {
263 SCOPED_TRACE("burst");
264
265 // create burst
266 const std::shared_ptr<::android::nn::ExecutionBurstController> controller =
267 CreateBurst(preparedModel);
268 ASSERT_NE(nullptr, controller.get());
269
270 // create memory keys
271 std::vector<intptr_t> keys(request.pools.size());
272 for (size_t i = 0; i < keys.size(); ++i) {
273 keys[i] = reinterpret_cast<intptr_t>(&request.pools[i]);
274 }
275
276 // execute burst
277 std::tie(executionStatus, outputShapes, timing) =
278 controller->compute(request, measure, keys);
279
280 break;
281 }
282 }
283
284 if (outputType != OutputType::FULLY_SPECIFIED &&
285 executionStatus == ErrorStatus::GENERAL_FAILURE) {
286 LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
287 "execute model that it does not support.";
288 std::cout << "[ ] Early termination of test because vendor service cannot "
289 "execute model that it does not support."
290 << std::endl;
291 GTEST_SKIP();
292 }
293 if (measure == MeasureTiming::NO) {
294 EXPECT_EQ(UINT64_MAX, timing.timeOnDevice);
295 EXPECT_EQ(UINT64_MAX, timing.timeInDriver);
296 } else {
297 if (timing.timeOnDevice != UINT64_MAX && timing.timeInDriver != UINT64_MAX) {
298 EXPECT_LE(timing.timeOnDevice, timing.timeInDriver);
299 }
300 }
301
302 switch (outputType) {
303 case OutputType::FULLY_SPECIFIED:
304 // If the model output operands are fully specified, outputShapes must be either
305 // either empty, or have the same number of elements as the number of outputs.
306 ASSERT_EQ(ErrorStatus::NONE, executionStatus);
307 ASSERT_TRUE(outputShapes.size() == 0 ||
308 outputShapes.size() == test.operandDimensions.size());
309 break;
310 case OutputType::UNSPECIFIED:
311 // If the model output operands are not fully specified, outputShapes must have
312 // the same number of elements as the number of outputs.
313 ASSERT_EQ(ErrorStatus::NONE, executionStatus);
314 ASSERT_EQ(outputShapes.size(), test.operandDimensions.size());
315 break;
316 case OutputType::INSUFFICIENT:
317 ASSERT_EQ(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, executionStatus);
318 ASSERT_EQ(outputShapes.size(), test.operandDimensions.size());
319 ASSERT_FALSE(outputShapes[0].isSufficient);
320 return;
321 }
322 // Go through all outputs, overwrite output dimensions with returned output shapes
323 if (outputShapes.size() > 0) {
324 for_each<uint32_t>(test.operandDimensions,
325 [&outputShapes](int idx, std::vector<uint32_t>& dim) {
326 dim = outputShapes[idx].dimensions;
327 });
328 }
329
330 // validate results
331 outputMemory->read();
332 copy_back(&test, outputs_info, outputPtr);
333 outputMemory->commit();
334 // Filter out don't cares
335 MixedTyped filtered_golden = filter(golden, is_ignored);
336 MixedTyped filtered_test = filter(test, is_ignored);
337
338 // We want "close-enough" results for float
339 compare(filtered_golden, filtered_test, fpAtol, fpRtol);
340
341 if (example.expectedMultinomialDistributionTolerance > 0) {
342 expectMultinomialDistributionWithinTolerance(test, example);
343 }
344 }
345}
346void EvaluatePreparedModel(sp<IPreparedModel>& preparedModel, std::function<bool(int)> is_ignored,
347 const std::vector<MixedTypedExample>& examples,
348 bool hasRelaxedFloat32Model, Executor executor, MeasureTiming measure,
349 OutputType outputType) {
350 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model, kDefaultAtol,
351 kDefaultRtol, executor, measure, outputType);
352}
353
354void EvaluatePreparedModel(sp<IPreparedModel>& preparedModel, std::function<bool(int)> is_ignored,
355 const std::vector<MixedTypedExample>& examples,
356 bool hasRelaxedFloat32Model, bool testDynamicOutputShape) {
357 if (testDynamicOutputShape) {
358 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
359 Executor::ASYNC, MeasureTiming::NO, OutputType::UNSPECIFIED);
360 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
361 Executor::SYNC, MeasureTiming::NO, OutputType::UNSPECIFIED);
362 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
363 Executor::BURST, MeasureTiming::NO, OutputType::UNSPECIFIED);
364 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
365 Executor::ASYNC, MeasureTiming::YES, OutputType::UNSPECIFIED);
366 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
367 Executor::SYNC, MeasureTiming::YES, OutputType::UNSPECIFIED);
368 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
369 Executor::BURST, MeasureTiming::YES, OutputType::UNSPECIFIED);
370 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
371 Executor::ASYNC, MeasureTiming::NO, OutputType::INSUFFICIENT);
372 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
373 Executor::SYNC, MeasureTiming::NO, OutputType::INSUFFICIENT);
374 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
375 Executor::BURST, MeasureTiming::NO, OutputType::INSUFFICIENT);
376 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
377 Executor::ASYNC, MeasureTiming::YES, OutputType::INSUFFICIENT);
378 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
379 Executor::SYNC, MeasureTiming::YES, OutputType::INSUFFICIENT);
380 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
381 Executor::BURST, MeasureTiming::YES, OutputType::INSUFFICIENT);
382 } else {
383 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
384 Executor::ASYNC, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
385 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
386 Executor::SYNC, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
387 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
388 Executor::BURST, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
389 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
390 Executor::ASYNC, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
391 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
392 Executor::SYNC, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
393 EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
394 Executor::BURST, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
395 }
396}
397
398void PrepareModel(const sp<IDevice>& device, const Model& model,
399 sp<IPreparedModel>* preparedModel) {
400 // see if service can handle model
401 bool fullySupportsModel = false;
402 Return<void> supportedCall = device->getSupportedOperations_1_2(
403 model, [&fullySupportsModel](ErrorStatus status, const hidl_vec<bool>& supported) {
404 ASSERT_EQ(ErrorStatus::NONE, status);
405 ASSERT_NE(0ul, supported.size());
406 fullySupportsModel = std::all_of(supported.begin(), supported.end(),
407 [](bool valid) { return valid; });
408 });
409 ASSERT_TRUE(supportedCall.isOk());
410
411 // launch prepare model
412 sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
413 ASSERT_NE(nullptr, preparedModelCallback.get());
414 Return<ErrorStatus> prepareLaunchStatus = device->prepareModel_1_2(
415 model, ExecutionPreference::FAST_SINGLE_ANSWER, hidl_vec<hidl_handle>(),
416 hidl_vec<hidl_handle>(), HidlToken(), preparedModelCallback);
417 ASSERT_TRUE(prepareLaunchStatus.isOk());
418 ASSERT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(prepareLaunchStatus));
419
420 // retrieve prepared model
421 preparedModelCallback->wait();
422 ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
423 sp<V1_0::IPreparedModel> preparedModelV1_0 = preparedModelCallback->getPreparedModel();
424 *preparedModel = IPreparedModel::castFrom(preparedModelV1_0).withDefault(nullptr);
425
426 // early termination if vendor service cannot fully prepare model
427 if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
428 ASSERT_EQ(nullptr, preparedModel->get());
429 LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
430 "prepare model that it does not support.";
431 std::cout << "[ ] Early termination of test because vendor service cannot "
432 "prepare model that it does not support."
433 << std::endl;
434 return;
435 }
436 EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
437 ASSERT_NE(nullptr, preparedModel->get());
438}
439
440void Execute(const sp<IDevice>& device, std::function<Model(void)> create_model,
441 std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples,
442 bool testDynamicOutputShape) {
443 Model model = create_model();
444 sp<IPreparedModel> preparedModel = nullptr;
445 PrepareModel(device, model, &preparedModel);
446 if (preparedModel == nullptr) {
447 GTEST_SKIP();
448 }
449 EvaluatePreparedModel(preparedModel, is_ignored, examples,
450 model.relaxComputationFloat32toFloat16, testDynamicOutputShape);
451}
452
453} // namespace generated_tests
454} // namespace neuralnetworks
455} // namespace hardware
456} // namespace android