1 /* 2 * Copyright (C) 2017 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 // Provides C++ classes to more easily use the Neural Networks API. 18 // TODO(b/117845862): this should be auto generated from NeuralNetworksWrapper.h. 19 20 #ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_NEURAL_NETWORKS_WRAPPER_H 21 #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_NEURAL_NETWORKS_WRAPPER_H 22 23 #include <math.h> 24 25 #include <algorithm> 26 #include <memory> 27 #include <optional> 28 #include <string> 29 #include <utility> 30 #include <vector> 31 32 #include "NeuralNetworks.h" 33 #include "NeuralNetworksWrapper.h" 34 #include "NeuralNetworksWrapperExtensions.h" 35 36 namespace android { 37 namespace nn { 38 namespace test_wrapper { 39 40 using wrapper::Event; 41 using wrapper::ExecutePreference; 42 using wrapper::ExecutePriority; 43 using wrapper::ExtensionModel; 44 using wrapper::ExtensionOperandParams; 45 using wrapper::ExtensionOperandType; 46 using wrapper::OperandType; 47 using wrapper::Result; 48 using wrapper::SymmPerChannelQuantParams; 49 using wrapper::Type; 50 51 class Memory { 52 public: 53 // Takes ownership of a ANeuralNetworksMemory Memory(ANeuralNetworksMemory * memory)54 Memory(ANeuralNetworksMemory* memory) : mMemory(memory) {} 55 Memory(size_t size,int protect,int fd,size_t offset)56 Memory(size_t size, int protect, int fd, size_t offset) { 57 mValid = ANeuralNetworksMemory_createFromFd(size, protect, fd, offset, &mMemory) == 58 ANEURALNETWORKS_NO_ERROR; 59 } 60 Memory(AHardwareBuffer * buffer)61 Memory(AHardwareBuffer* buffer) { 62 mValid = ANeuralNetworksMemory_createFromAHardwareBuffer(buffer, &mMemory) == 63 ANEURALNETWORKS_NO_ERROR; 64 } 65 ~Memory()66 virtual ~Memory() { ANeuralNetworksMemory_free(mMemory); } 67 68 // Disallow copy semantics to ensure the runtime object can only be freed 69 // once. Copy semantics could be enabled if some sort of reference counting 70 // or deep-copy system for runtime objects is added later. 71 Memory(const Memory&) = delete; 72 Memory& operator=(const Memory&) = delete; 73 74 // Move semantics to remove access to the runtime object from the wrapper 75 // object that is being moved. This ensures the runtime object will be 76 // freed only once. Memory(Memory && other)77 Memory(Memory&& other) { *this = std::move(other); } 78 Memory& operator=(Memory&& other) { 79 if (this != &other) { 80 ANeuralNetworksMemory_free(mMemory); 81 mMemory = other.mMemory; 82 mValid = other.mValid; 83 other.mMemory = nullptr; 84 other.mValid = false; 85 } 86 return *this; 87 } 88 get()89 ANeuralNetworksMemory* get() const { return mMemory; } isValid()90 bool isValid() const { return mValid; } 91 92 private: 93 ANeuralNetworksMemory* mMemory = nullptr; 94 bool mValid = true; 95 }; 96 97 class Model { 98 public: Model()99 Model() { 100 // TODO handle the value returned by this call 101 ANeuralNetworksModel_create(&mModel); 102 } ~Model()103 ~Model() { ANeuralNetworksModel_free(mModel); } 104 105 // Disallow copy semantics to ensure the runtime object can only be freed 106 // once. Copy semantics could be enabled if some sort of reference counting 107 // or deep-copy system for runtime objects is added later. 108 Model(const Model&) = delete; 109 Model& operator=(const Model&) = delete; 110 111 // Move semantics to remove access to the runtime object from the wrapper 112 // object that is being moved. This ensures the runtime object will be 113 // freed only once. Model(Model && other)114 Model(Model&& other) { *this = std::move(other); } 115 Model& operator=(Model&& other) { 116 if (this != &other) { 117 ANeuralNetworksModel_free(mModel); 118 mModel = other.mModel; 119 mNextOperandId = other.mNextOperandId; 120 mValid = other.mValid; 121 mRelaxed = other.mRelaxed; 122 mFinished = other.mFinished; 123 other.mModel = nullptr; 124 other.mNextOperandId = 0; 125 other.mValid = false; 126 other.mRelaxed = false; 127 other.mFinished = false; 128 } 129 return *this; 130 } 131 finish()132 Result finish() { 133 if (mValid) { 134 auto result = static_cast<Result>(ANeuralNetworksModel_finish(mModel)); 135 if (result != Result::NO_ERROR) { 136 mValid = false; 137 } 138 mFinished = true; 139 return result; 140 } else { 141 return Result::BAD_STATE; 142 } 143 } 144 addOperand(const OperandType * type)145 uint32_t addOperand(const OperandType* type) { 146 if (ANeuralNetworksModel_addOperand(mModel, &(type->operandType)) != 147 ANEURALNETWORKS_NO_ERROR) { 148 mValid = false; 149 } 150 if (type->channelQuant) { 151 if (ANeuralNetworksModel_setOperandSymmPerChannelQuantParams( 152 mModel, mNextOperandId, &type->channelQuant.value().params) != 153 ANEURALNETWORKS_NO_ERROR) { 154 mValid = false; 155 } 156 } 157 return mNextOperandId++; 158 } 159 160 template <typename T> addConstantOperand(const OperandType * type,const T & value)161 uint32_t addConstantOperand(const OperandType* type, const T& value) { 162 static_assert(sizeof(T) <= ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES, 163 "Values larger than ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES " 164 "not supported"); 165 uint32_t index = addOperand(type); 166 setOperandValue(index, &value); 167 return index; 168 } 169 addModelOperand(const Model * value)170 uint32_t addModelOperand(const Model* value) { 171 OperandType operandType(Type::MODEL, {}); 172 uint32_t operand = addOperand(&operandType); 173 setOperandValueFromModel(operand, value); 174 return operand; 175 } 176 setOperandValue(uint32_t index,const void * buffer,size_t length)177 void setOperandValue(uint32_t index, const void* buffer, size_t length) { 178 if (ANeuralNetworksModel_setOperandValue(mModel, index, buffer, length) != 179 ANEURALNETWORKS_NO_ERROR) { 180 mValid = false; 181 } 182 } 183 184 template <typename T> setOperandValue(uint32_t index,const T * value)185 void setOperandValue(uint32_t index, const T* value) { 186 static_assert(!std::is_pointer<T>(), "No operand may have a pointer as its value"); 187 return setOperandValue(index, value, sizeof(T)); 188 } 189 setOperandValueFromMemory(uint32_t index,const Memory * memory,uint32_t offset,size_t length)190 void setOperandValueFromMemory(uint32_t index, const Memory* memory, uint32_t offset, 191 size_t length) { 192 if (ANeuralNetworksModel_setOperandValueFromMemory(mModel, index, memory->get(), offset, 193 length) != ANEURALNETWORKS_NO_ERROR) { 194 mValid = false; 195 } 196 } 197 setOperandValueFromModel(uint32_t index,const Model * value)198 void setOperandValueFromModel(uint32_t index, const Model* value) { 199 if (ANeuralNetworksModel_setOperandValueFromModel(mModel, index, value->mModel) != 200 ANEURALNETWORKS_NO_ERROR) { 201 mValid = false; 202 } 203 } 204 addOperation(ANeuralNetworksOperationType type,const std::vector<uint32_t> & inputs,const std::vector<uint32_t> & outputs)205 void addOperation(ANeuralNetworksOperationType type, const std::vector<uint32_t>& inputs, 206 const std::vector<uint32_t>& outputs) { 207 if (ANeuralNetworksModel_addOperation(mModel, type, static_cast<uint32_t>(inputs.size()), 208 inputs.data(), static_cast<uint32_t>(outputs.size()), 209 outputs.data()) != ANEURALNETWORKS_NO_ERROR) { 210 mValid = false; 211 } 212 } identifyInputsAndOutputs(const std::vector<uint32_t> & inputs,const std::vector<uint32_t> & outputs)213 void identifyInputsAndOutputs(const std::vector<uint32_t>& inputs, 214 const std::vector<uint32_t>& outputs) { 215 if (ANeuralNetworksModel_identifyInputsAndOutputs( 216 mModel, static_cast<uint32_t>(inputs.size()), inputs.data(), 217 static_cast<uint32_t>(outputs.size()), 218 outputs.data()) != ANEURALNETWORKS_NO_ERROR) { 219 mValid = false; 220 } 221 } 222 relaxComputationFloat32toFloat16(bool isRelax)223 void relaxComputationFloat32toFloat16(bool isRelax) { 224 if (ANeuralNetworksModel_relaxComputationFloat32toFloat16(mModel, isRelax) == 225 ANEURALNETWORKS_NO_ERROR) { 226 mRelaxed = isRelax; 227 } 228 } 229 getHandle()230 ANeuralNetworksModel* getHandle() const { return mModel; } isValid()231 bool isValid() const { return mValid; } isRelaxed()232 bool isRelaxed() const { return mRelaxed; } isFinished()233 bool isFinished() const { return mFinished; } 234 235 protected: 236 ANeuralNetworksModel* mModel = nullptr; 237 // We keep track of the operand ID as a convenience to the caller. 238 uint32_t mNextOperandId = 0; 239 bool mValid = true; 240 bool mRelaxed = false; 241 bool mFinished = false; 242 }; 243 244 class Compilation { 245 public: 246 // On success, createForDevice(s) will return Result::NO_ERROR and the created compilation; 247 // otherwise, it will return the error code and Compilation object wrapping a nullptr handle. createForDevice(const Model * model,const ANeuralNetworksDevice * device)248 static std::pair<Result, Compilation> createForDevice(const Model* model, 249 const ANeuralNetworksDevice* device) { 250 return createForDevices(model, {device}); 251 } createForDevices(const Model * model,const std::vector<const ANeuralNetworksDevice * > & devices)252 static std::pair<Result, Compilation> createForDevices( 253 const Model* model, const std::vector<const ANeuralNetworksDevice*>& devices) { 254 ANeuralNetworksCompilation* compilation = nullptr; 255 const Result result = static_cast<Result>(ANeuralNetworksCompilation_createForDevices( 256 model->getHandle(), devices.empty() ? nullptr : devices.data(), devices.size(), 257 &compilation)); 258 return {result, Compilation(compilation)}; 259 } 260 Compilation(const Model * model)261 Compilation(const Model* model) { 262 int result = ANeuralNetworksCompilation_create(model->getHandle(), &mCompilation); 263 if (result != 0) { 264 // TODO Handle the error 265 } 266 } 267 Compilation()268 Compilation() {} 269 ~Compilation()270 ~Compilation() { ANeuralNetworksCompilation_free(mCompilation); } 271 272 // Disallow copy semantics to ensure the runtime object can only be freed 273 // once. Copy semantics could be enabled if some sort of reference counting 274 // or deep-copy system for runtime objects is added later. 275 Compilation(const Compilation&) = delete; 276 Compilation& operator=(const Compilation&) = delete; 277 278 // Move semantics to remove access to the runtime object from the wrapper 279 // object that is being moved. This ensures the runtime object will be 280 // freed only once. Compilation(Compilation && other)281 Compilation(Compilation&& other) { *this = std::move(other); } 282 Compilation& operator=(Compilation&& other) { 283 if (this != &other) { 284 ANeuralNetworksCompilation_free(mCompilation); 285 mCompilation = other.mCompilation; 286 other.mCompilation = nullptr; 287 } 288 return *this; 289 } 290 setPreference(ExecutePreference preference)291 Result setPreference(ExecutePreference preference) { 292 return static_cast<Result>(ANeuralNetworksCompilation_setPreference( 293 mCompilation, static_cast<int32_t>(preference))); 294 } 295 setPriority(ExecutePriority priority)296 Result setPriority(ExecutePriority priority) { 297 return static_cast<Result>(ANeuralNetworksCompilation_setPriority( 298 mCompilation, static_cast<int32_t>(priority))); 299 } 300 setCaching(const std::string & cacheDir,const std::vector<uint8_t> & token)301 Result setCaching(const std::string& cacheDir, const std::vector<uint8_t>& token) { 302 if (token.size() != ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN) { 303 return Result::BAD_DATA; 304 } 305 return static_cast<Result>(ANeuralNetworksCompilation_setCaching( 306 mCompilation, cacheDir.c_str(), token.data())); 307 } 308 finish()309 Result finish() { return static_cast<Result>(ANeuralNetworksCompilation_finish(mCompilation)); } 310 getHandle()311 ANeuralNetworksCompilation* getHandle() const { return mCompilation; } 312 313 protected: 314 // Takes the ownership of ANeuralNetworksCompilation. Compilation(ANeuralNetworksCompilation * compilation)315 Compilation(ANeuralNetworksCompilation* compilation) : mCompilation(compilation) {} 316 317 ANeuralNetworksCompilation* mCompilation = nullptr; 318 }; 319 320 class Execution { 321 public: Execution(const Compilation * compilation)322 Execution(const Compilation* compilation) : mCompilation(compilation->getHandle()) { 323 int result = ANeuralNetworksExecution_create(compilation->getHandle(), &mExecution); 324 if (result != 0) { 325 // TODO Handle the error 326 } 327 } 328 ~Execution()329 ~Execution() { ANeuralNetworksExecution_free(mExecution); } 330 331 // Disallow copy semantics to ensure the runtime object can only be freed 332 // once. Copy semantics could be enabled if some sort of reference counting 333 // or deep-copy system for runtime objects is added later. 334 Execution(const Execution&) = delete; 335 Execution& operator=(const Execution&) = delete; 336 337 // Move semantics to remove access to the runtime object from the wrapper 338 // object that is being moved. This ensures the runtime object will be 339 // freed only once. Execution(Execution && other)340 Execution(Execution&& other) { *this = std::move(other); } 341 Execution& operator=(Execution&& other) { 342 if (this != &other) { 343 ANeuralNetworksExecution_free(mExecution); 344 mCompilation = other.mCompilation; 345 other.mCompilation = nullptr; 346 mExecution = other.mExecution; 347 other.mExecution = nullptr; 348 } 349 return *this; 350 } 351 352 Result setInput(uint32_t index, const void* buffer, size_t length, 353 const ANeuralNetworksOperandType* type = nullptr) { 354 return static_cast<Result>( 355 ANeuralNetworksExecution_setInput(mExecution, index, type, buffer, length)); 356 } 357 358 template <typename T> 359 Result setInput(uint32_t index, const T* value, 360 const ANeuralNetworksOperandType* type = nullptr) { 361 static_assert(!std::is_pointer<T>(), "No operand may have a pointer as its value"); 362 return setInput(index, value, sizeof(T), type); 363 } 364 365 Result setInputFromMemory(uint32_t index, const Memory* memory, uint32_t offset, 366 uint32_t length, const ANeuralNetworksOperandType* type = nullptr) { 367 return static_cast<Result>(ANeuralNetworksExecution_setInputFromMemory( 368 mExecution, index, type, memory->get(), offset, length)); 369 } 370 371 Result setOutput(uint32_t index, void* buffer, size_t length, 372 const ANeuralNetworksOperandType* type = nullptr) { 373 return static_cast<Result>( 374 ANeuralNetworksExecution_setOutput(mExecution, index, type, buffer, length)); 375 } 376 377 template <typename T> 378 Result setOutput(uint32_t index, T* value, const ANeuralNetworksOperandType* type = nullptr) { 379 static_assert(!std::is_pointer<T>(), "No operand may have a pointer as its value"); 380 return setOutput(index, value, sizeof(T), type); 381 } 382 383 Result setOutputFromMemory(uint32_t index, const Memory* memory, uint32_t offset, 384 uint32_t length, const ANeuralNetworksOperandType* type = nullptr) { 385 return static_cast<Result>(ANeuralNetworksExecution_setOutputFromMemory( 386 mExecution, index, type, memory->get(), offset, length)); 387 } 388 setLoopTimeout(uint64_t duration)389 Result setLoopTimeout(uint64_t duration) { 390 return static_cast<Result>(ANeuralNetworksExecution_setLoopTimeout(mExecution, duration)); 391 } 392 startCompute(Event * event)393 Result startCompute(Event* event) { 394 ANeuralNetworksEvent* ev = nullptr; 395 Result result = static_cast<Result>(ANeuralNetworksExecution_startCompute(mExecution, &ev)); 396 event->set(ev); 397 return result; 398 } 399 startComputeWithDependencies(const std::vector<const Event * > & dependencies,uint64_t duration,Event * event)400 Result startComputeWithDependencies(const std::vector<const Event*>& dependencies, 401 uint64_t duration, Event* event) { 402 std::vector<const ANeuralNetworksEvent*> deps(dependencies.size()); 403 std::transform(dependencies.begin(), dependencies.end(), deps.begin(), 404 [](const Event* e) { return e->getHandle(); }); 405 ANeuralNetworksEvent* ev = nullptr; 406 Result result = static_cast<Result>(ANeuralNetworksExecution_startComputeWithDependencies( 407 mExecution, deps.data(), deps.size(), duration, &ev)); 408 event->set(ev); 409 return result; 410 } 411 compute()412 Result compute() { 413 switch (mComputeMode) { 414 case ComputeMode::SYNC: { 415 return static_cast<Result>(ANeuralNetworksExecution_compute(mExecution)); 416 } 417 case ComputeMode::ASYNC: { 418 ANeuralNetworksEvent* event = nullptr; 419 Result result = static_cast<Result>( 420 ANeuralNetworksExecution_startCompute(mExecution, &event)); 421 if (result != Result::NO_ERROR) { 422 return result; 423 } 424 // TODO how to manage the lifetime of events when multiple waiters is not 425 // clear. 426 result = static_cast<Result>(ANeuralNetworksEvent_wait(event)); 427 ANeuralNetworksEvent_free(event); 428 return result; 429 } 430 case ComputeMode::BURST: { 431 ANeuralNetworksBurst* burst = nullptr; 432 Result result = 433 static_cast<Result>(ANeuralNetworksBurst_create(mCompilation, &burst)); 434 if (result != Result::NO_ERROR) { 435 return result; 436 } 437 result = static_cast<Result>( 438 ANeuralNetworksExecution_burstCompute(mExecution, burst)); 439 ANeuralNetworksBurst_free(burst); 440 return result; 441 } 442 case ComputeMode::FENCED: { 443 ANeuralNetworksEvent* event = nullptr; 444 Result result = 445 static_cast<Result>(ANeuralNetworksExecution_startComputeWithDependencies( 446 mExecution, nullptr, 0, 0, &event)); 447 if (result != Result::NO_ERROR) { 448 return result; 449 } 450 result = static_cast<Result>(ANeuralNetworksEvent_wait(event)); 451 ANeuralNetworksEvent_free(event); 452 return result; 453 } 454 } 455 return Result::BAD_DATA; 456 } 457 458 // By default, compute() uses the synchronous API. setComputeMode() can be 459 // used to change the behavior of compute() to either: 460 // - use the asynchronous API and then wait for computation to complete 461 // or 462 // - use the burst API 463 // Returns the previous ComputeMode. 464 enum class ComputeMode { SYNC, ASYNC, BURST, FENCED }; setComputeMode(ComputeMode mode)465 static ComputeMode setComputeMode(ComputeMode mode) { 466 ComputeMode oldComputeMode = mComputeMode; 467 mComputeMode = mode; 468 return oldComputeMode; 469 } 470 getOutputOperandDimensions(uint32_t index,std::vector<uint32_t> * dimensions)471 Result getOutputOperandDimensions(uint32_t index, std::vector<uint32_t>* dimensions) { 472 uint32_t rank = 0; 473 Result result = static_cast<Result>( 474 ANeuralNetworksExecution_getOutputOperandRank(mExecution, index, &rank)); 475 dimensions->resize(rank); 476 if ((result != Result::NO_ERROR && result != Result::OUTPUT_INSUFFICIENT_SIZE) || 477 rank == 0) { 478 return result; 479 } 480 result = static_cast<Result>(ANeuralNetworksExecution_getOutputOperandDimensions( 481 mExecution, index, dimensions->data())); 482 return result; 483 } 484 getHandle()485 ANeuralNetworksExecution* getHandle() { return mExecution; }; 486 487 private: 488 ANeuralNetworksCompilation* mCompilation = nullptr; 489 ANeuralNetworksExecution* mExecution = nullptr; 490 491 // Initialized to ComputeMode::SYNC in TestNeuralNetworksWrapper.cpp. 492 static ComputeMode mComputeMode; 493 }; 494 495 } // namespace test_wrapper 496 } // namespace nn 497 } // namespace android 498 499 #endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_TEST_NEURAL_NETWORKS_WRAPPER_H 500