Memory Domain Runtime: Explicit memory copying.
- Implement ANNMemory_copy.
- Add validation tests.
Bug: 141353602
Bug: 141363565
Test: NNT_static
Change-Id: Id0ec2431cf9cfff650cd8885de4d15abee306a9c
Merged-In: Id0ec2431cf9cfff650cd8885de4d15abee306a9c
(cherry picked from commit 9af11e783aadacf78330a5e6e4e8255f41789c13)
diff --git a/runtime/Memory.cpp b/runtime/Memory.cpp
index 561df07..f8f90f7 100644
--- a/runtime/Memory.cpp
+++ b/runtime/Memory.cpp
@@ -26,6 +26,7 @@
#include <vector>
#include "CompilationBuilder.h"
+#include "CpuExecutor.h"
#include "ExecutionBurstController.h"
#include "Manager.h"
#include "MemoryUtils.h"
@@ -52,6 +53,11 @@
return true;
}
+ Metadata getMetadata() const override { return {.logicalSize = kSize}; }
+ bool updateMetadata(const Metadata& metadata) override {
+ return metadata.logicalSize == 0 || metadata.logicalSize == kSize;
+ }
+
private:
const uint32_t kSize;
};
@@ -73,6 +79,9 @@
<< ") for Non-BLOB format AHardwareBuffer.";
return true;
}
+
+ Metadata getMetadata() const override { return {}; }
+ bool updateMetadata(const Metadata&) override { return true; }
};
// The validator for a memory created from ANNMemory_createFromDesc.
@@ -80,10 +89,10 @@
// with both offset and length set to zero.
class DeviceMemoryValidator : public MemoryValidatorBase {
public:
- DeviceMemoryValidator(std::set<CompilationRole> roles, hal::OperandType type,
+ DeviceMemoryValidator(std::set<CompilationRole> roles, Operand operand,
std::vector<uint32_t> dimensions)
: kCompilationRoles(std::move(roles)),
- mDataType(type),
+ kOperand(std::move(operand)),
kInitialDimensions(std::move(dimensions)),
mUpdatedDimensions(kInitialDimensions) {}
@@ -95,7 +104,7 @@
NN_RET_CHECK(offset == 0 && length == 0)
<< "non-zero offset and/or length for driver-allocated memory.";
if (type) {
- const bool isTensor = TypeManager::get()->isTensorType(mDataType);
+ const bool isTensor = TypeManager::get()->isTensorType(kOperand.type);
NN_RET_CHECK(isTensor || type->dimensionCount == 0)
<< "invalid dimensions for scalar memory.";
std::vector<uint32_t> dimensions(type->dimensions,
@@ -119,19 +128,40 @@
return true;
}
- bool updateDimensions(const std::vector<uint32_t>& dimensions) override {
- NN_RET_CHECK(TypeManager::get()->isTensorType(mDataType) || dimensions.empty());
- auto combined = combineDimensions(dimensions, kInitialDimensions);
+ Metadata getMetadata() const override {
+ CHECK(mInitialized);
+ return {.logicalSize = TypeManager::get()->getSizeOfData(kOperand.type, mUpdatedDimensions),
+ .dimensions = mUpdatedDimensions,
+ .operand = kOperand};
+ }
+
+ bool updateMetadata(const Metadata& metadata) override {
+ NN_RET_CHECK(!metadata.operand.has_value() ||
+ (metadata.operand->type == kOperand.type &&
+ metadata.operand->scale == kOperand.scale &&
+ metadata.operand->zeroPoint == kOperand.zeroPoint &&
+ metadata.operand->extraParams == kOperand.extraParams));
+
+ NN_RET_CHECK(metadata.dimensions.empty() ||
+ TypeManager::get()->isTensorType(kOperand.type));
+ auto combined = combineDimensions(metadata.dimensions, kInitialDimensions);
NN_RET_CHECK(combined.has_value());
+ NN_RET_CHECK(metadata.logicalSize == 0 ||
+ metadata.logicalSize ==
+ TypeManager::get()->getSizeOfData(kOperand.type, combined.value()));
mUpdatedDimensions = std::move(combined.value());
return true;
}
void setInitialized(bool initialized) override { mInitialized = initialized; }
+ bool isInitialized() const override { return mInitialized; }
private:
const std::set<CompilationRole> kCompilationRoles;
- OperandType mDataType;
+
+ // Keep track of the data type, scale, zero point, and extra parameters of the target operand.
+ // Other fields will be ignored, including dimensions, lifetime, location, etc.
+ const Operand kOperand;
// The dimensions of the memory when the memory object is created.
// May have unknown dimensions or rank.
@@ -182,6 +212,90 @@
mUsedBy.emplace(burst.get(), burst);
}
+static int copyHidlMemories(const hidl_memory& src, const hidl_memory& dst) {
+ if (src.size() != dst.size()) {
+ LOG(ERROR) << "ANeuralNetworksMemory_copy -- incompatible memory size";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ auto srcPool = RunTimePoolInfo::createFromHidlMemory(src);
+ auto dstPool = RunTimePoolInfo::createFromHidlMemory(dst);
+ if (!srcPool.has_value() || !dstPool.has_value()) {
+ LOG(ERROR) << "ANeuralNetworksMemory_copy -- unable to map memory";
+ return ANEURALNETWORKS_UNMAPPABLE;
+ }
+ CHECK(srcPool->getBuffer() != nullptr);
+ CHECK(dstPool->getBuffer() != nullptr);
+ std::copy(srcPool->getBuffer(), srcPool->getBuffer() + src.size(), dstPool->getBuffer());
+ dstPool->flush();
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+static int copyIBufferToHidlMemory(const sp<IBuffer>& src, const hidl_memory& dst) {
+ const auto ret = src->copyTo(dst);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "ANeuralNetworksMemory_copy failure: " << ret.description();
+ return ANEURALNETWORKS_OP_FAILED;
+ }
+ return convertErrorStatusToResultCode(static_cast<ErrorStatus>(ret));
+}
+
+static int copyHidlMemoryToIBuffer(const hidl_memory& src, const sp<IBuffer>& dst,
+ const std::vector<uint32_t>& dimensions) {
+ const auto ret = dst->copyFrom(src, dimensions);
+ if (!ret.isOk()) {
+ LOG(ERROR) << "ANeuralNetworksMemory_copy failure: " << ret.description();
+ return ANEURALNETWORKS_OP_FAILED;
+ }
+ return convertErrorStatusToResultCode(static_cast<ErrorStatus>(ret));
+}
+
+static int copyIBuffers(const sp<IBuffer>& src, const sp<IBuffer>& dst,
+ const MemoryValidatorBase::Metadata& srcMetadata) {
+ // TODO(xusongw): Use BLOB mode AHardwareBuffer.
+ hidl_memory hidlMemory = allocateSharedMemory(srcMetadata.logicalSize);
+ if (!hidlMemory.valid()) return ANEURALNETWORKS_OUT_OF_MEMORY;
+ NN_RETURN_IF_ERROR(copyIBufferToHidlMemory(src, hidlMemory));
+ NN_RETURN_IF_ERROR(copyHidlMemoryToIBuffer(hidlMemory, dst, srcMetadata.dimensions));
+ return ANEURALNETWORKS_NO_ERROR;
+}
+
+static int copyInternal(const Memory& src, const Memory& dst) {
+ if (&src == &dst) return ANEURALNETWORKS_NO_ERROR;
+
+ if (!src.getValidator().isInitialized()) {
+ LOG(ERROR) << "ANeuralNetworksMemory_copy -- uninitialized source memory";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ const auto srcMetadata = src.getValidator().getMetadata();
+ if (!dst.getValidator().updateMetadata(srcMetadata)) {
+ LOG(ERROR) << "ANeuralNetworksMemory_copy -- incompatible memories";
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+
+ bool srcHasHidlMemory = src.getHidlMemory().valid();
+ bool dstHasHidlMemory = dst.getHidlMemory().valid();
+ bool srcHasIBuffer = src.getIBuffer() != nullptr;
+ bool dstHasIBuffer = dst.getIBuffer() != nullptr;
+ if (srcHasIBuffer && dstHasIBuffer) {
+ return copyIBuffers(src.getIBuffer(), dst.getIBuffer(), srcMetadata);
+ } else if (srcHasHidlMemory && dstHasHidlMemory) {
+ return copyHidlMemories(src.getHidlMemory(), dst.getHidlMemory());
+ } else if (srcHasHidlMemory && dstHasIBuffer) {
+ return copyHidlMemoryToIBuffer(src.getHidlMemory(), dst.getIBuffer(),
+ srcMetadata.dimensions);
+ } else if (srcHasIBuffer && dstHasHidlMemory) {
+ return copyIBufferToHidlMemory(src.getIBuffer(), dst.getHidlMemory());
+ }
+ return ANEURALNETWORKS_OP_FAILED;
+}
+
+int Memory::copy(const Memory& src, const Memory& dst) {
+ int n = copyInternal(src, dst);
+ dst.getValidator().setInitialized(n == ANEURALNETWORKS_NO_ERROR);
+ return n;
+}
+
bool MemoryBuilder::badState(const char* name) const {
if (mFinished) {
LOG(ERROR) << "ANeuralNetworksMemoryDesc_" << name << " can't modify after finished";
@@ -375,7 +489,7 @@
if (n == ANEURALNETWORKS_NO_ERROR) {
CHECK(memory != nullptr);
auto validator =
- std::make_unique<DeviceMemoryValidator>(mRoles, mOperand->type, mDesc.dimensions);
+ std::make_unique<DeviceMemoryValidator>(mRoles, mOperand.value(), mDesc.dimensions);
memory->setValidator(std::move(validator));
}
return {n, std::move(memory)};