summaryrefslogtreecommitdiff
path: root/runtimes/nn/runtime/ExecutionBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'runtimes/nn/runtime/ExecutionBuilder.cpp')
-rw-r--r--runtimes/nn/runtime/ExecutionBuilder.cpp293
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