First implementation of the Neural Networks API. This first version can run a simple query on the CPU either via the fallback path or through a simulated driver. This code has many deficiencies: single threaded, not all validation are done, not going through HIDL, and not enough unit tests. Expect more changes! Test: Compiled and ran the unit tests Change-Id: I9f6a485a2e7207aeb5f91a2904dcb4b7fd8a6f65
diff --git a/common/Android.bp b/common/Android.bp new file mode 100644 index 0000000..8f00067 --- /dev/null +++ b/common/Android.bp
@@ -0,0 +1,33 @@ +/* + * Copyright 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. + */ + +cc_library_static { + name: "libneuralnetworks_common", + defaults: ["neuralnetworks_defaults"], + host_supported: true, + export_include_dirs: ["include"], + + srcs: [ + "CpuExecutor.cpp", + "Operations.cpp", + "OperationsUtils.cpp", + "Utils.cpp", + ], + + header_libs: [ + "libneuralnetworks_headers", + ], +}
diff --git a/common/CpuExecutor.cpp b/common/CpuExecutor.cpp new file mode 100644 index 0000000..3539edc --- /dev/null +++ b/common/CpuExecutor.cpp
@@ -0,0 +1,166 @@ +/* + * 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. + */ + +#define LOG_TAG "CpuExecutor" + +#include "CpuExecutor.h" + +#include "Model.h" +#include "NeuralNetworks.h" +#include "Operations.h" + +namespace android { +namespace nn { + +// If we don't have a buffer, allocate it. +static bool allocateIfNeeded(RunTimeOperandInfo* info) { + if (info->buffer == nullptr) { + uint32_t length = + sizeOfData(info->shape.type, + Range<uint32_t>(info->shape.numberOfDimensions, info->shape.dimensions)); + info->buffer = malloc(length); + } + return true; +} + +CpuExecutor::CpuExecutor(const IModel* model, const std::vector<InputOutputInfo>& modelInputs, + const std::vector<InputOutputInfo>& modelOutputs) + : mModel(model) { + mModel->copyDimensionStorage(&mDimensions); + + const Range<OperandEntry> modelOperands = model->getOperands(); + const size_t count = modelOperands.count(); + mOperands.resize(count); + for (size_t i = 0; i < count; i++) { + const OperandEntry& from = modelOperands[i]; + RunTimeOperandInfo& to = mOperands[i]; + to.shape.type = from.type; + to.shape.numberOfDimensions = from.dimensions.count; + // It's safe to take the address. The size of mDimensions won't change. + to.shape.dimensions = &mDimensions[from.dimensions.offset]; + if (from.location.pool == LOCATION_AT_RUN_TIME) { + to.buffer = nullptr; + to.numberOfUsesLeft = from.numberOfConsumers; + } else if (from.location.pool == LOCATION_SAME_BLOCK) { + to.buffer = const_cast<void*>(mModel->getDataPointer(from.location.offset)); + to.numberOfUsesLeft = 0; + } else { + // TODO: Revisit when we add support for multiple pools. + nnAssert(false); + } + to.length = from.length; + } + + for (uint32_t i = 0; i < modelInputs.size(); i++) { + overrideOperand(mModel->getInputOperandIndex(i), modelInputs[i]); + } + for (uint32_t i = 0; i < modelOutputs.size(); i++) { + overrideOperand(mModel->getOutputOperandIndex(i), modelOutputs[i]); + } +} + +int CpuExecutor::run() { + // The model has serialized the operation in execution order. + for (const auto& operation : mModel->getOperations()) { + int n = executeOperation(operation); + if (n != ANEURALNETWORKS_NO_ERROR) { + return n; + } + } + return ANEURALNETWORKS_NO_ERROR; +} + +void CpuExecutor::overrideOperand(uint32_t operandIndex, const InputOutputInfo& from) { + RunTimeOperandInfo& to = mOperands[operandIndex]; + if (from.dimensionChanged) { + nnAssert(to.shape.numberOfDimensions == from.dimensions.size()); + for (uint32_t i = 0; i < to.shape.numberOfDimensions; i++) { + to.shape.dimensions[i] = from.dimensions[i]; + } + } + nnAssert(to.buffer == nullptr); + to.buffer = from.buffer; + to.length = from.length; + to.numberOfUsesLeft = 0; +} + +void CpuExecutor::freeNoLongerUsedOperands(const Range<uint32_t>& inputs) { + for (uint32_t i : inputs) { + auto& info = mOperands[i]; + // Check if it's a static or model input/output. + if (info.numberOfUsesLeft == 0) { + continue; + } + nnAssert(mModel->getOperands()[i].location.pool == LOCATION_AT_RUN_TIME); + info.numberOfUsesLeft--; + if (info.numberOfUsesLeft == 0) { + auto* buffer = mOperands[i].buffer; + nnAssert(buffer != nullptr); + free(buffer); + buffer = nullptr; + } + } +} + +int CpuExecutor::executeOperation(const OperationEntry& operation) { + ALOGI("Executing %s", getOperationName(operation.opCode)); + const Range<uint32_t> ins = mModel->getOperandIndexes(operation.inputs); + const Range<uint32_t> outs = mModel->getOperandIndexes(operation.outputs); + bool success = false; + + // Function to verify that the number of input and output parameters + // matches what is expected. + auto parameterCountIs = [&ins, &outs, &operation](uint32_t expectedIns, + uint32_t expectedOuts) -> bool { + if (ins.count() != expectedIns || outs.count() != expectedOuts) { + ALOGE("%s: Invalid number of ins %u/%u and outs %u/%u", + getOperationName(operation.opCode), ins.count(), expectedIns, outs.count(), + expectedOuts); + return false; + } + return true; + }; + + switch (static_cast<OperatorType>(operation.opCode)) { + case OperatorType::ADD_FLOAT32: { + if (!parameterCountIs(2, 1)) { + return ANEURALNETWORKS_BAD_DATA; + } + const RunTimeOperandInfo& in1 = mOperands[ins[0]]; + const RunTimeOperandInfo& in2 = mOperands[ins[1]]; + RunTimeOperandInfo& out = mOperands[outs[0]]; + + success = addTensorsFloat32Prepare(in1.shape, in2.shape, &out.shape) && + allocateIfNeeded(&out) && + addTensorsFloat32(reinterpret_cast<const float*>(in1.buffer), + reinterpret_cast<const float*>(in2.buffer), + reinterpret_cast<float*>(out.buffer), in1.shape); + } break; + default: + nnAssert(false); + break; + } + if (!success) { + ALOGE("%s failed.", getOperationName(operation.opCode)); + return ANEURALNETWORKS_OP_FAILED; + } + + freeNoLongerUsedOperands(ins); + return ANEURALNETWORKS_NO_ERROR; +} + +} // namespace nn +} // namespace android
diff --git a/common/Operations.cpp b/common/Operations.cpp new file mode 100644 index 0000000..7e727ec --- /dev/null +++ b/common/Operations.cpp
@@ -0,0 +1,40 @@ +/* + * 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. + */ + +// Contains the implementation of the operations. + +#define LOG_TAG "Operations" + +#include "Operations.h" +#include "OperationsUtils.h" + +namespace android { +namespace nn { + +bool addTensorsFloat32Prepare(const Shape& in1, const Shape& in2, Shape* out1) { + return SameShape(in1, in2) && SetShape(in1, out1); +} + +bool addTensorsFloat32(const float* in1, const float* in2, float* out, const Shape& shape) { + uint32_t count = getNumberOfElements(shape); + for (size_t i = 0; i < count; i++) { + *(out++) = *(in1++) + *(in2++); + } + return true; +} + +} // namespace nn +} // namespace android
diff --git a/common/OperationsUtils.cpp b/common/OperationsUtils.cpp new file mode 100644 index 0000000..e9c232d --- /dev/null +++ b/common/OperationsUtils.cpp
@@ -0,0 +1,56 @@ +/* + * 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. + */ + +#define LOG_TAG "OperationsUtils" + +#include "OperationsUtils.h" +#include "Utils.h" + +namespace android { +namespace nn { + +bool SameShape(const Shape& in1, const Shape& in2) { + if (in1.type != in2.type || in1.numberOfDimensions != in2.numberOfDimensions) { + return false; + } + for (uint32_t i = 0; i < in1.numberOfDimensions; i++) { + if (in1.dimensions[i] != in2.dimensions[i]) { + return false; + } + } + return true; +} + +bool SetShape(const Shape& in, const Shape* out) { + if (in.type != out->type || in.numberOfDimensions != out->numberOfDimensions) { + return false; + } + for (uint32_t i = 0; i < in.numberOfDimensions; i++) { + out->dimensions[i] = in.dimensions[i]; + } + return true; +} + +uint32_t getNumberOfElements(const Shape& shape) { + uint32_t count = 1; + for (uint32_t i = 0; i < shape.numberOfDimensions; i++) { + count *= shape.dimensions[i]; + } + return count; +} + +} // namespace nn +} // namespace android
diff --git a/common/Utils.cpp b/common/Utils.cpp new file mode 100644 index 0000000..55f7726 --- /dev/null +++ b/common/Utils.cpp
@@ -0,0 +1,110 @@ +/* + * 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. + */ + +#define LOG_TAG "Utils" + +#include "Utils.h" + +#include "NeuralNetworks.h" + +namespace android { +namespace nn { + +const char* typeNames[ANEURALNETWORKS_NUMBER_DATA_TYPES] = { + "FLOAT16", + "FLOAT32", + "INT8", + "UINT8", + "INT16", + "UINT16", + "INT32", + "UINT32", + "TENSOR_FLOAT16", + "TENSOR_FLOAT32", + "TENSOR_SIMMETRICAL_QUANT8", +}; + +const char* errorNames[] = { + "NO_ERROR", "OUT_OF_MEMORY", "INCOMPLETE", "NULL", "BAD_DATA", + "NOT_IMPLEMENTED", // TODO remove +}; + +const char* kOperationNames[ANEURALNETWORKS_NUMBER_OPERATION_TYPES] = { + "AVERAGE_POOL_FLOAT32", + "CONCATENATION_FLOAT32", + "CONV_FLOAT32", + "DEPTHWISE_CONV_FLOAT32", + "MAX_POOL_FLOAT32", + "L2_POOL_FLOAT32", + "DEPTH_TO_SPACE_FLOAT32", + "SPACE_TO_DEPTH_FLOAT32", + "LOCAL_RESPONSE_NORMALIZATION_FLOAT32", + "SOFTMAX_FLOAT32", + "RESHAPE_FLOAT32", + "SPLIT_FLOAT32", + "FAKE_QUANT_FLOAT32", + "ADD_FLOAT32", + "FULLY_CONNECTED_FLOAT32", + "CAST_FLOAT32", + "MUL_FLOAT32", + "L2_NORMALIZATION_FLOAT32", + "LOGISTIC_FLOAT32", + "RELU_FLOAT32", + "RELU6_FLOAT32", + "RELU1_FLOAT32", + "TANH_FLOAT32", + "DEQUANTIZE_FLOAT32", + "FLOOR_FLOAT32", + "GATHER_FLOAT32", + "RESIZE_BILINEAR_FLOAT32", + "LSH_PROJECTION_FLOAT32", + "LSTM_FLOAT32", + "SVDF_FLOAT32", + "RNN_FLOAT32", + "N_GRAM_FLOAT32", + "LOOKUP_FLOAT32", +}; + +const char* getOperationName(uint32_t opCode) { + return kOperationNames[opCode]; +} + +uint32_t sizeOfDataType[ANEURALNETWORKS_NUMBER_DATA_TYPES]{ + 2, // ANEURALNETWORKS_FLOAT16 + 4, // ANEURALNETWORKS_FLOAT32 + 1, // ANEURALNETWORKS_INT8 + 1, // ANEURALNETWORKS_UINT8 + 2, // ANEURALNETWORKS_INT16 + 2, // ANEURALNETWORKS_UINT16 + 4, // ANEURALNETWORKS_INT32 + 4, // ANEURALNETWORKS_UINT32 + 2, // ANEURALNETWORKS_TENSOR_FLOAT16 + 4, // ANEURALNETWORKS_TENSOR_FLOAT32 + 1 // ANEURALNETWORKS_TENSOR_SIMMETRICAL_QUANT8 +}; + +uint32_t sizeOfData(uint32_t type, const Range<uint32_t>& dimensions) { + nnAssert(type < ANEURALNETWORKS_NUMBER_DATA_TYPES); + + uint32_t size = sizeOfDataType[type]; + for (auto d : dimensions) { + size *= d; + } + return size; +} + +} // namespace nn +} // namespace android
diff --git a/common/include/CpuExecutor.h b/common/include/CpuExecutor.h new file mode 100644 index 0000000..ecccd7c --- /dev/null +++ b/common/include/CpuExecutor.h
@@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef ANDROID_ML_NN_COMMON_CPU_EXECUTOR_H +#define ANDROID_ML_NN_COMMON_CPU_EXECUTOR_H + +#include "HalAbstraction.h" +#include "OperationsUtils.h" +#include "Utils.h" + +#include <vector> + +namespace android { +namespace nn { + +class IModel; + +// Information we maintain about each operand during execution. +struct RunTimeOperandInfo { + // The type and dimensions of the operand. The dimensions can + // change at runtime. We include the type because it's useful + // to pass together with the dimension to the functions implementing + // the operators. + Shape shape; + // Where the operand's data is stored. Check the corresponding + // location information in the model to figure out if this points + // to memory we have allocated for an temporary operand. + void* buffer; + // The length of the buffer. + uint32_t length; + // Keeps track of how many operations have yet to make use + // of this temporary variable. When the count is decremented to 0, + // we free the buffer. For non-temporary variables, this count is + // always 0. + uint32_t numberOfUsesLeft; +}; + +// This class is used to execute a model on the CPU. +class CpuExecutor { +public: + // The model must outlive the executor. We prevent it from being modified + // while this is executing. + CpuExecutor(const IModel* model, const std::vector<InputOutputInfo>& modelInputs, + const std::vector<InputOutputInfo>& modelOutputs); + // Executes the model. The results will be stored at the locations + // specified in the constructor. + int run(); + +private: + // Runs one operation of the graph. + int executeOperation(const OperationEntry& entry); + // Decrement the usage count for the operands listed. Frees the memory + // allocated for any temporary variable with a count of zero. + void freeNoLongerUsedOperands(const Range<uint32_t>& inputs); + + // The operand is a model input or output. Override the information that + // came with the model with the one passed by the calling program. + void overrideOperand(uint32_t operandIndex, const InputOutputInfo& info); + + // The model that we'll execute. + const IModel* mModel; + // We're copying the list of all the dimensions from the model, as + // these may be modified when we run the operatins. Since we're + // making a full copy, the indexes used in the operand description + // stay valid. + std::vector<uint32_t> mDimensions; + // Runtime information about all the operands. + std::vector<RunTimeOperandInfo> mOperands; +}; + +} // namespace nn +} // namespace android + +#endif // ANDROID_ML_NN_COMMON_CPU_EXECUTOR_H
diff --git a/common/include/HalAbstraction.h b/common/include/HalAbstraction.h new file mode 100644 index 0000000..9bf5a6c --- /dev/null +++ b/common/include/HalAbstraction.h
@@ -0,0 +1,153 @@ +/* + * 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. + */ + +#ifndef ANDROID_ML_NN_COMMON_HAL_ABSTRACTION_H +#define ANDROID_ML_NN_COMMON_HAL_ABSTRACTION_H + +#include <vector> + +// This class is used to abstract the HAL interface that will be created +// HIDL gen from the HIDL files. We may not need this long term, although +// it is useful for running on a desktop without the HIDL compiler. + +namespace android { +namespace nn { + +// The types the operands can take. These must be the same value as the NN API> +// TODO Use a single file for both. +enum class DataType { + FLOAT16 = 0, + FLOAT32 = 1, + INT8 = 2, + UINT8 = 3, + INT16 = 4, + UINT16 = 5, + INT32 = 6, + UINT32 = 7, + TENSOR_FLOAT16 = 8, + TENSOR_FLOAT32 = 9, + TENSOR_SIMMETRICAL_QUANT8 = 10, + + NUM_DATA_TYPES = 11 +}; + +// TODO There's currently a 1:1 mapping with the NN API constants. +// This will no longer be the case once an op supports more than one type. +// We'll need to add a conversion when finalizing the model. +enum class OperatorType { + AVERAGE_POOL_FLOAT32 = 0, + CONCATENATION_FLOAT32 = 1, + CONV_FLOAT32 = 2, + DEPTHWISE_CONV_FLOAT32 = 3, + MAX_POOL_FLOAT32 = 4, + L2_POOL_FLOAT32 = 5, + DEPTH_TO_SPACE_FLOAT32 = 6, + SPACE_TO_DEPTH_FLOAT32 = 7, + LOCAL_RESPONSE_NORMALIZATION_FLOAT32 = 8, + SOFTMAX_FLOAT32 = 9, + RESHAPE_FLOAT32 = 10, + SPLIT_FLOAT32 = 11, + FAKE_QUANT_FLOAT32 = 12, + ADD_FLOAT32 = 13, + FULLY_CONNECTED_FLOAT32 = 14, + CAST_FLOAT32 = 15, + MUL_FLOAT32 = 16, + L2_NORMALIZATION_FLOAT32 = 17, + LOGISTIC_FLOAT32 = 18, + RELU_FLOAT32 = 19, + RELU6_FLOAT32 = 20, + RELU1_FLOAT32 = 21, + TANH_FLOAT32 = 22, + DEQUANTIZE_FLOAT32 = 23, + FLOOR_FLOAT32 = 24, + GATHER_FLOAT32 = 25, + RESIZE_BILINEAR_FLOAT32 = 26, + LSH_PROJECTION_FLOAT32 = 27, + LSTM_FLOAT32 = 28, + SVDF_FLOAT32 = 29, + RNN_FLOAT32 = 30, + N_GRAM_FLOAT32 = 31, + LOOKUP_FLOAT32 = 32, + + NUM_OPERATOR_TYPES = 33 +}; + +// Status of a driver. +enum Status { AVAILABLE, BUSY, OFFLINE, UNKNOWN }; + +// Used by a driver to report its performance characteristics. +// TODO revisit the data types and scales. +struct PerformanceInfo { + float execTime; // in nanoseconds + float powerUsage; // in picoJoules +}; + +// Serialized representation of the model. +struct SerializedModel { + std::vector<uint8_t> memory; +}; + +// The capabilities of a driver. +struct Capabilities { + bool supportedOperatorTypes[static_cast<size_t>(OperatorType::NUM_OPERATOR_TYPES)]; + // TODO Do the same for baseline model IDs + bool cachesCompilation; + // TODO revisit the data types and scales. + float bootupTime; // in nanoseconds + PerformanceInfo float16Performance; + PerformanceInfo float32Performance; + PerformanceInfo quantized8Performance; +}; + +// Informaton about one input or output operand of a model. +struct InputOutputInfo { + void* buffer; + uint32_t length; // In bytes. + // If true, the calling program has provided different dimensions for the + // operand than was specified in the model. + bool dimensionChanged; + // The dimensions to use if the dimensions have been changed. + std::vector<uint32_t> dimensions; +}; + +// See the HAL files for documentation on these interfaces. +class IEvent { +public: + virtual ~IEvent(){} + virtual uint32_t wait() = 0; +}; + +class IRequest { +public: + virtual ~IRequest(){} + virtual int execute(const std::vector<InputOutputInfo>& inputs, + const std::vector<InputOutputInfo>& outputs, IEvent** event) = 0; + virtual void releaseTempMemory() = 0; +}; + +class IDevice { +public: + virtual ~IDevice(){} + virtual void initialize(Capabilities* capabilities) = 0; + virtual void getSupportedSubgraph(void* graph, std::vector<bool>& canDo) = 0; + virtual int prepareRequest(const SerializedModel* model, IRequest** request) = 0; + virtual Status getStatus() = 0; +}; + +} // namespace nn +} // namespace android + +#endif // ANDROID_ML_NN_COMMON_HAL_ABSTRACTION_H
diff --git a/common/include/HalModel.h b/common/include/HalModel.h new file mode 100644 index 0000000..40247e1 --- /dev/null +++ b/common/include/HalModel.h
@@ -0,0 +1,117 @@ +/* + * 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. + */ + +#ifndef ANDROID_ML_NN_COMMON_HAL_MODEL_H +#define ANDROID_ML_NN_COMMON_HAL_MODEL_H + +// This file contains the data structures that used to access the + +//namespace android { +//namespace nn { + +#include <cstdint> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +// The location will be specified at runtime. It's either a temporary +// variable, an input, or an output. +const uint32_t LOCATION_AT_RUN_TIME = 0xFFFFFFFF; +// The operand's value is in the same memory pool as the model. +const uint32_t LOCATION_SAME_BLOCK = 0xFFFFFFFE; + +// Used to represent a variable length array. +struct ArrayInfo { + // The number of elements of the array. + uint32_t count; + // The offset in whichevere data structure to find the first + // element of the array. The unit type of the offset depends + // on the data structure it is indexing. + uint32_t offset; +}; + +// A serialized model starts with this block of memory. +// TODO Look into alignment or padding issues. +struct ModelHeader { + // Size and location of the operation table, an array of OperationEntry. + // The offset is the distance in bytes from the start of the header. + ArrayInfo operations; + // Size and location of the operand table, an array of OperandEntry. + // The offset is the distance in bytes from the start of the header. + ArrayInfo operands; + // Size and location of the table of dimensions, an array of uint32_t. + // The offset is the distance in bytes from the start of the header. + ArrayInfo dimensions; + // Size and location of the table of operand indexes, an array of uint32_t. + // The offset is the distance in bytes from the start of the header. + ArrayInfo operandIndexes; + // Size and location of the memory block containing all the fixed + // operand values. The element type is uint8_t. + // The offset is the distance in bytes from the start of the header. + ArrayInfo operandValues; + + // The list of operand indexes for the inputs of the model. + // The offset is an index in the operandIndexes table. + ArrayInfo modelInputs; + // The list of operand indexes for the outputs of the model. + // The offset is an index in the operandIndexes table. + ArrayInfo modelOutputs; +}; + +// Describes one operation of the graph. +struct OperationEntry { + // The type of operation. + uint32_t opCode; + // Describes the table that contains the indexes of the inputs of the + // operation. The offset is the index in the operandIndexes table. + ArrayInfo inputs; + // Describes the table that contains the indexes of the outputs of the + // operation. The offset is the index in the operandIndexes table. + ArrayInfo outputs; +}; + +// Describes the location of a data object. +struct DataLocation { + // The index of the memory pool where this location is found. + // Two special values can also be used. See the LOCATION_* constants above. + uint32_t pool; + // Offset in bytes from the start of the pool. + uint32_t offset; +}; + +// Describes one operand of the graph. +struct OperandEntry { + uint32_t type; + // The number of operations that uses this operand as input. + uint32_t numberOfConsumers; + // TODO handle quantization params. + + // The following three fields maybe superseded at runtime. + + // Dimensions of the operand. The offset is an index in the dimensions table. + ArrayInfo dimensions; + // Where to find the data for this operand. + DataLocation location; + // The length of the data, in bytes. + uint32_t length; +}; + +__END_DECLS + +//} // namespace nn +//} // namespace android + +#endif // ANDROID_ML_NN_COMMON_HAL_MODEL_H
diff --git a/common/include/Model.h b/common/include/Model.h new file mode 100644 index 0000000..0efc967 --- /dev/null +++ b/common/include/Model.h
@@ -0,0 +1,43 @@ +/* + * 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. + */ + +// Interface used by the CpuExecutor to communicate with the two model +// implementations. + +#ifndef ANDROID_ML_NN_COMMON_MODEL_BUILDER_H +#define ANDROID_ML_NN_COMMON_MODEL_BUILDER_H + +#include "Utils.h" + +namespace android { +namespace nn { + +class IModel { +public: + virtual ~IModel() {} + virtual Range<OperationEntry> getOperations() const = 0; + virtual Range<OperandEntry> getOperands() const = 0; + virtual Range<uint32_t> getOperandIndexes(const ArrayInfo& info) const = 0; + virtual void copyDimensionStorage(std::vector<uint32_t>* dimensions) const = 0; + virtual uint32_t getInputOperandIndex(uint32_t listIndex) const = 0; + virtual uint32_t getOutputOperandIndex(uint32_t listIndex) const = 0; + virtual const void* getDataPointer(uint32_t offset) const = 0; +}; + +} // namespace nn +} // namespace android + +#endif // ANDROID_ML_NN_COMMON_MODEL_BUILDER_H
diff --git a/common/include/Operations.h b/common/include/Operations.h new file mode 100644 index 0000000..56d1c92 --- /dev/null +++ b/common/include/Operations.h
@@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef ANDROID_ML_NN_COMMON_OPERATIONS_H +#define ANDROID_ML_NN_COMMON_OPERATIONS_H + +#include <stddef.h> + +namespace android { +namespace nn { + +struct Shape; + +bool addTensorsFloat32(const float* in1, const float* in2, float* out, const Shape& shape); +bool addTensorsFloat32Prepare(const Shape& in1, const Shape& in2, Shape* out1); + +} // namespace nn +} // namespace android + +#endif // ANDROID_ML_NN_COMMON_OPERATIONS_H
diff --git a/common/include/OperationsUtils.h b/common/include/OperationsUtils.h new file mode 100644 index 0000000..ec45912 --- /dev/null +++ b/common/include/OperationsUtils.h
@@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef ANDROID_ML_NN_COMMON_OPERATIONS_UTILS_H +#define ANDROID_ML_NN_COMMON_OPERATIONS_UTILS_H + +#include <cstdint> + +namespace android { +namespace nn { + +// The type and dimensions of an operand. +struct Shape { + uint32_t type; + uint32_t numberOfDimensions; + uint32_t* dimensions; +}; + +// Verifies that the two shapes are the same. +bool SameShape(const Shape& in1, const Shape& in2); + +// Sets out to the same shape as in. +bool SetShape(const Shape& in, const Shape* out); + +// Return the total number of elements, i.e. all the dimensions multiplied +// together. For a scalar, returns one. +uint32_t getNumberOfElements(const Shape& shape); + +} // namespace nn +} // namespace android + +#endif // ANDROID_ML_NN_COMMON_OPERATIONS_UTILS_H
diff --git a/common/include/Utils.h b/common/include/Utils.h new file mode 100644 index 0000000..e3619e1 --- /dev/null +++ b/common/include/Utils.h
@@ -0,0 +1,107 @@ +/* + * 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. + */ + +#ifndef ANDROID_ML_NN_COMMON_UTILS_H +#define ANDROID_ML_NN_COMMON_UTILS_H + +#include "HalModel.h" + +#include <stdio.h> +#include <vector> + +namespace android { +namespace nn { + +// TODO Replace with the real Android logging macros. +#define ALOGE(format, ...) printf(LOG_TAG ": ERROR " format "\n", ##__VA_ARGS__) +#define ALOGI(format, ...) printf(LOG_TAG ": " format "\n", ##__VA_ARGS__) + +// Assert macro, as Android does not generally support assert. +#define nnAssert(v) \ + do { \ + if (!(v)) { \ + fprintf(stderr, "nnAssert failed at %s:%d - '%s'\n", __FILE__, __LINE__, #v); \ + abort(); \ + } \ + } while (0) + +// Represent a list of items. Handy to iterate over lists and sublists. +template <typename T> +class Range { +public: + // The default constructor should only be used when followed by a call + // to setFromBuffer. + Range() {} + // Range over all the elements of the vector. + Range(const std::vector<T>& data) { + mCount = static_cast<uint32_t>(data.size()); + mBegin = data.data(); + } + // Range over the sublist of elements of the vector, as specified by info. + Range(const std::vector<T>& data, const ArrayInfo& info) { + mCount = info.count; + mBegin = data.data() + info.offset; + } + // Range over the sublist of the range, as specified by info. + Range(const Range<T>& data, const ArrayInfo& info) { + mCount = info.count; + mBegin = data.begin() + info.offset; + } + // Range of the specified number of elements, starting at the specified value. + Range(uint32_t count, T* start) { + mCount = count; + mBegin = start; + } + + // Range over consecutive elements starting at buffer + info.offset. + void setFromBuffer(const ArrayInfo& info, const uint8_t* buffer) { + mCount = info.count; + mBegin = reinterpret_cast<const T*>(buffer + info.offset); + } + + // These two methods enable the use of for(x:Range(..)). + const T* begin() const { return mBegin; } + const T* end() const { return mBegin + mCount; } + + // Returns the element at the specifed index. + T operator[](uint32_t index) const { + nnAssert(index < mCount); + return mBegin[index]; + } + // All our ranges are read-only. If we need to write, use this: + // uint32_t& operator[] (uint32_t index) { + // nnAssert(index < mCount); + // return mBegin[index]; + // } + + uint32_t count() const { return mCount; } + +private: + const T* mBegin = nullptr; // The start of the range. + uint32_t mCount = 0; // The number of elements in the range. +}; + +// Returns the the amount of space needed to store a tensor of the specified +// dimensions and type. +uint32_t sizeOfData(uint32_t type, const Range<uint32_t>& dimensions); + +// Returns the name of the operation in ASCII. +const char* getOperationName(uint32_t opCode); + +} // namespace nn +} // namespace android + +#endif // ANDROID_ML_NN_COMMON_UTILS_H