diff options
Diffstat (limited to 'runtimes/nn/runtime/ExecutionBuilder.cpp')
-rw-r--r-- | runtimes/nn/runtime/ExecutionBuilder.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/runtimes/nn/runtime/ExecutionBuilder.cpp b/runtimes/nn/runtime/ExecutionBuilder.cpp new file mode 100644 index 000000000..5cf2485c0 --- /dev/null +++ b/runtimes/nn/runtime/ExecutionBuilder.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ExecutionBuilder.h" +#include "CompilationBuilder.h" +#include "CpuExecutor.h" +#include "HalInterfaces.h" +#include "ModelBuilder.h" + +namespace nnfw { +namespace rt { + +// TODO-NNRT: Consider removing ModelArgumentInfo completely if it's not necessary +int ModelArgumentInfo::setFromPointer(const Operand& operand, + const ANeuralNetworksOperandType* type, void* data, + uint32_t length) { + if ((data == nullptr) != (length == 0)) { + LOG(ERROR) << "Data pointer must be nullptr if and only if length is zero (data = " + << data << ", length = " << length << ")"; + return ANEURALNETWORKS_BAD_DATA; + } + if (data == nullptr) { + state = ModelArgumentInfo::HAS_NO_VALUE; + } else { + int n = updateDimensionInfo(operand, type); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + uint32_t neededLength = sizeOfData(operand.type, dimensions); + if (operand.type != OperandType::OEM && neededLength != length) { + LOG(ERROR) << "Setting argument with invalid length: " << length + << ", expected length: " << neededLength; + return ANEURALNETWORKS_BAD_DATA; + } + state = ModelArgumentInfo::POINTER; + } + buffer = data; + locationAndLength = {.poolIndex = 0, .offset = 0, .length = length}; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelArgumentInfo::setFromMemory(const Operand& operand, const ANeuralNetworksOperandType* type, + uint32_t poolIndex, uint32_t offset, uint32_t length) { + int n = updateDimensionInfo(operand, type); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + state = ModelArgumentInfo::MEMORY; + locationAndLength = {.poolIndex = poolIndex, .offset = offset, .length = length}; + buffer = nullptr; + return ANEURALNETWORKS_NO_ERROR; +} + +int ModelArgumentInfo::updateDimensionInfo(const Operand& operand, + const ANeuralNetworksOperandType* newType) { + nnAssert(dimensions.empty()); + if (newType == nullptr) { + for (auto i : operand.dimensions) { + if (i == 0) { + LOG(ERROR) << "Setting input/output with unspecified dimensions"; + return ANEURALNETWORKS_BAD_DATA; + } + } + dimensions = operand.dimensions; + } else { + uint32_t count = newType->dimensionCount; + if (static_cast<OperandType>(newType->type) != operand.type || + count != operand.dimensions.size()) { + LOG(ERROR) << "Setting input/output with incompatible types"; + return ANEURALNETWORKS_BAD_DATA; + } + dimensions = hidl_vec<uint32_t>(count); + for (uint32_t i = 0; i < count; i++) { + if (operand.dimensions[i] != 0 && operand.dimensions[i] != newType->dimensions[i]) { + LOG(ERROR) << "Overriding a fully specified dimension is disallowed"; + return ANEURALNETWORKS_BAD_DATA; + } else { + dimensions[i] = newType->dimensions[i]; + } + } + } + return ANEURALNETWORKS_NO_ERROR; +} + +ExecutionBuilder::ExecutionBuilder(const CompilationBuilder* compilation) : + mModel(compilation->mModel), + mInputs(mModel->inputCount()), + mOutputs(mModel->outputCount()) { + VLOG(EXECUTION) << "ExecutionBuilder::ExecutionBuilder"; +} + +int ExecutionBuilder::setInput(uint32_t index, const ANeuralNetworksOperandType* type, + const void* buffer, size_t length) { + uint32_t count = static_cast<uint32_t>(mInputs.size()); + if (index >= count) { + LOG(ERROR) << "ANeuralNetworksExecution_setInput bad index " << index << " " << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (type != nullptr) { + int n = validateOperandType(*type, "ANeuralNetworksExecution_setInput", false); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + } + if (length > 0xFFFFFFFF) { + LOG(ERROR) << "ANeuralNetworksExecution_setInput input exceeds max length " << length; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t l = static_cast<uint32_t>(length); + return mInputs[index].setFromPointer(mModel->getInputOperand(index), type, + const_cast<void*>(buffer), l); +} + +int ExecutionBuilder::setInputFromMemory(uint32_t index, const ANeuralNetworksOperandType* type, + const Memory* memory, size_t offset, size_t length) { + // Should be similar to StepExecutor::setInputOrOutputFromTemporaryMemory() + + uint32_t count = static_cast<uint32_t>(mInputs.size()); + if (index >= count) { + LOG(ERROR) << "ANeuralNetworksExecution_setInputFromMemory bad index " << index << " " + << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (!memory->validateSize(offset, length)) { + return ANEURALNETWORKS_BAD_DATA; + } + // TODO validate the rest + uint32_t poolIndex = mMemories.add(memory); + return mInputs[index].setFromMemory(mModel->getInputOperand(index), type, poolIndex, offset, + length); +} + +int ExecutionBuilder::setOutput(uint32_t index, const ANeuralNetworksOperandType* type, void* buffer, + size_t length) { + uint32_t count = static_cast<uint32_t>(mOutputs.size()); + if (index >= count) { + LOG(ERROR) << "ANeuralNetworksExecution_setOutput bad index " << index << " " << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (type != nullptr) { + int n = validateOperandType(*type, "ANeuralNetworksExecution_setOutput", false); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + } + if (length > 0xFFFFFFFF) { + LOG(ERROR) << "ANeuralNetworksExecution_setOutput input exceeds max length " << length; + return ANEURALNETWORKS_BAD_DATA; + } + uint32_t l = static_cast<uint32_t>(length); + return mOutputs[index].setFromPointer(mModel->getOutputOperand(index), type, buffer, l); +} + +int ExecutionBuilder::setOutputFromMemory(uint32_t index, const ANeuralNetworksOperandType* type, + const Memory* memory, size_t offset, size_t length) { + // Should be similar to StepExecutor::setInputOrOutputFromTemporaryMemory() + + uint32_t count = static_cast<uint32_t>(mOutputs.size()); + if (index >= count) { + LOG(ERROR) << "ANeuralNetworksExecution_setOutputFromMemory bad index " << index << " " + << count; + return ANEURALNETWORKS_BAD_DATA; + } + if (!memory->validateSize(offset, length)) { + return ANEURALNETWORKS_BAD_DATA; + } + // TODO validate the rest + uint32_t poolIndex = mMemories.add(memory); + return mOutputs[index].setFromMemory(mModel->getOutputOperand(index), type, poolIndex, offset, + length); +} + +int ExecutionBuilder::startCompute(sp<ExecutionCallback>* synchronizationCallback) { + // Run on the CPU. + VLOG(EXECUTION) << "ExecutionBuilder::startCompute (without plan) on CPU"; + StepExecutor executor(this, mModel); + executor.mapInputsAndOutputsTrivially(); + return executor.startCompute(synchronizationCallback); +} + +static void setRequestArgumentArray(const std::vector<ModelArgumentInfo>& argumentInfos, + hidl_vec<RequestArgument>* ioInfos) { + size_t count = argumentInfos.size(); + ioInfos->resize(count); + for (size_t i = 0; i < count; i++) { + const auto& info = argumentInfos[i]; + (*ioInfos)[i] = { .hasNoValue = info.state == ModelArgumentInfo::HAS_NO_VALUE, + .location = info.locationAndLength, + .dimensions = info.dimensions, + }; + } +} + + +StepExecutor::StepExecutor(const ExecutionBuilder* executionBuilder, const ModelBuilder* model) + : mExecutionBuilder(executionBuilder), mModel(model) {} + +void StepExecutor::mapInputsAndOutputsTrivially() { + mInputs = mExecutionBuilder->mInputs; + mOutputs = mExecutionBuilder->mOutputs; + mMemories = mExecutionBuilder->mMemories; +} + +int StepExecutor::startCompute(sp<ExecutionCallback>* synchronizationCallback) { + // Run on CPU only + return startComputeOnCpu(synchronizationCallback); +} + +static void asyncStartComputeOnCpu(const Model& model, const Request& request, + const std::vector<RunTimePoolInfo>& modelPoolInfos, + const std::vector<RunTimePoolInfo>& requestPoolInfos, + const sp<ExecutionCallback>& executionCallback) { + CpuExecutor executor; + int err = executor.run(model, request, modelPoolInfos, requestPoolInfos); + ErrorStatus status = err == ANEURALNETWORKS_NO_ERROR ? + ErrorStatus::NONE : ErrorStatus::GENERAL_FAILURE; + executionCallback->notify(status); +} +int StepExecutor::startComputeOnCpu(sp<ExecutionCallback>* synchronizationCallback) { + // TODO: use a thread pool + + Model model; + mModel->setHidlModel(&model); + + // Prepare the callback for asynchronous execution. sp<ExecutionCallback> + // object is returned when the execution has been successfully launched, + // otherwise a nullptr is returned. The executionCallback is abstracted in + // the NN API as an "event". + sp<ExecutionCallback> executionCallback = new ExecutionCallback(); + *synchronizationCallback = nullptr; + + std::vector<RunTimePoolInfo> modelPoolInfos; + if (!setRunTimePoolInfosFromHidlMemories(&modelPoolInfos, model.pools)) { + return ANEURALNETWORKS_UNMAPPABLE; + } + + std::vector<RunTimePoolInfo> requestPoolInfos; + uint32_t count = mMemories.size(); + requestPoolInfos.resize(count); + for (uint32_t i = 0; i < count; i++) { + const Memory* mem = mMemories[i]; + if (!requestPoolInfos[i].set(mem->getHidlMemory())) { + return ANEURALNETWORKS_UNMAPPABLE; + } + } + // Create as many pools as there are input / output. + auto fixPointerArguments = [&requestPoolInfos](std::vector<ModelArgumentInfo>& argumentInfos) { + for (ModelArgumentInfo& argumentInfo : argumentInfos) { + if (argumentInfo.state == ModelArgumentInfo::POINTER) { + //RunTimePoolInfo runTimeInfo = { + // .buffer = static_cast<uint8_t*>(argumentInfo.buffer)}; + RunTimePoolInfo runTimeInfo = {}; + runTimeInfo.buffer = static_cast<uint8_t*>(argumentInfo.buffer); + argumentInfo.locationAndLength.poolIndex = + static_cast<uint32_t>(requestPoolInfos.size()); + argumentInfo.locationAndLength.offset = 0; + requestPoolInfos.push_back(runTimeInfo); + } + } + }; + fixPointerArguments(mInputs); + fixPointerArguments(mOutputs); + + Request request; + setRequestArgumentArray(mInputs, &request.inputs); + setRequestArgumentArray(mOutputs, &request.outputs); + + // TODO: should model be moved with a std::cref? + std::thread thread(asyncStartComputeOnCpu, model, std::move(request), + std::move(modelPoolInfos), std::move(requestPoolInfos), + executionCallback); + executionCallback->bind_thread(std::move(thread)); + + *synchronizationCallback = executionCallback; + return ANEURALNETWORKS_NO_ERROR; +} + +} // namespace rt +} // namespace nnfw |