mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 22:04:26 +00:00
Move NNAPI HAL CommonUtils to nnapi/SharedMemory -- hal
This CL relocates utility code that transfers data between pointer-based memory and shared memory for Request objects and Model objects, moving it from nnapi/hal/CommonUtils.h (hal utilities) to nnapi/SharedMemory.h (canonical library). This change also adds a check for whether Model and Requests have pointer-based data in neuralnetworks/aidl/utils/src/Conversions.cpp to make it consistent with the HIDL utility conversions. Bug: 217217023 Test: mma Test: presubmit Change-Id: I55a0fea186708d806bc709681e10027a9e4b0ffb
This commit is contained in:
@@ -967,6 +967,11 @@ nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert(
|
||||
}
|
||||
|
||||
nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
|
||||
if (!hal::utils::hasNoPointerData(model)) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
|
||||
<< "Model cannot be unvalidatedConverted because it contains pointer-based memory";
|
||||
}
|
||||
|
||||
return Model{
|
||||
.main = NN_TRY(unvalidatedConvert(model.main)),
|
||||
.referenced = NN_TRY(unvalidatedConvert(model.referenced)),
|
||||
@@ -982,6 +987,11 @@ nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority) {
|
||||
}
|
||||
|
||||
nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
|
||||
if (!hal::utils::hasNoPointerData(request)) {
|
||||
return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
|
||||
<< "Request cannot be unvalidatedConverted because it contains pointer-based memory";
|
||||
}
|
||||
|
||||
return Request{
|
||||
.inputs = NN_TRY(unvalidatedConvert(request.inputs)),
|
||||
.outputs = NN_TRY(unvalidatedConvert(request.outputs)),
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <nnapi/Result.h>
|
||||
#include <nnapi/SharedMemory.h>
|
||||
#include <nnapi/Types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
@@ -47,81 +48,10 @@ nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWit
|
||||
const nn::Capabilities::PerformanceInfo& float32Performance,
|
||||
const nn::Capabilities::PerformanceInfo& quantized8Performance);
|
||||
|
||||
// Indicates if the object contains no pointer-based data that could be relocated to shared memory.
|
||||
bool hasNoPointerData(const nn::Model& model);
|
||||
bool hasNoPointerData(const nn::Request& request);
|
||||
|
||||
// Relocate pointer-based data to shared memory. If `model` has no Operand::LifeTime::POINTER data,
|
||||
// the function returns with a reference to `model`. If `model` has Operand::LifeTime::POINTER data,
|
||||
// the model is copied to `maybeModelInSharedOut` with the POINTER data relocated to a memory pool,
|
||||
// and the function returns with a reference to `*maybeModelInSharedOut`.
|
||||
nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
|
||||
const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut);
|
||||
|
||||
// Record a relocation mapping between pointer-based data and shared memory.
|
||||
// Only two specializations of this template may exist:
|
||||
// - RelocationInfo<const void*> for request inputs
|
||||
// - RelocationInfo<void*> for request outputs
|
||||
template <typename PointerType>
|
||||
struct RelocationInfo {
|
||||
PointerType data;
|
||||
size_t length;
|
||||
size_t offset;
|
||||
};
|
||||
using InputRelocationInfo = RelocationInfo<const void*>;
|
||||
using OutputRelocationInfo = RelocationInfo<void*>;
|
||||
|
||||
// Keep track of the relocation mapping between pointer-based data and shared memory pool,
|
||||
// and provide method to copy the data between pointers and the shared memory pool.
|
||||
// Only two specializations of this template may exist:
|
||||
// - RelocationTracker<InputRelocationInfo> for request inputs
|
||||
// - RelocationTracker<OutputRelocationInfo> for request outputs
|
||||
template <typename RelocationInfoType>
|
||||
class RelocationTracker {
|
||||
public:
|
||||
static nn::GeneralResult<std::unique_ptr<RelocationTracker>> create(
|
||||
std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory) {
|
||||
auto mapping = NN_TRY(map(memory));
|
||||
return std::make_unique<RelocationTracker<RelocationInfoType>>(
|
||||
std::move(relocationInfos), std::move(memory), std::move(mapping));
|
||||
}
|
||||
|
||||
RelocationTracker(std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory,
|
||||
nn::Mapping mapping)
|
||||
: kRelocationInfos(std::move(relocationInfos)),
|
||||
kMemory(std::move(memory)),
|
||||
kMapping(std::move(mapping)) {}
|
||||
|
||||
// Specializations defined in CommonUtils.cpp.
|
||||
// For InputRelocationTracker, this method will copy pointer data to the shared memory pool.
|
||||
// For OutputRelocationTracker, this method will copy shared memory data to the pointers.
|
||||
void flush() const;
|
||||
|
||||
private:
|
||||
const std::vector<RelocationInfoType> kRelocationInfos;
|
||||
const nn::SharedMemory kMemory;
|
||||
const nn::Mapping kMapping;
|
||||
};
|
||||
using InputRelocationTracker = RelocationTracker<InputRelocationInfo>;
|
||||
using OutputRelocationTracker = RelocationTracker<OutputRelocationInfo>;
|
||||
|
||||
struct RequestRelocation {
|
||||
std::unique_ptr<InputRelocationTracker> input;
|
||||
std::unique_ptr<OutputRelocationTracker> output;
|
||||
};
|
||||
|
||||
// Relocate pointer-based data to shared memory. If `request` has no
|
||||
// Request::Argument::LifeTime::POINTER data, the function returns with a reference to `request`. If
|
||||
// `request` has Request::Argument::LifeTime::POINTER data, the request is copied to
|
||||
// `maybeRequestInSharedOut` with the POINTER data relocated to a memory pool, and the function
|
||||
// returns with a reference to `*maybeRequestInSharedOut`. The `relocationOut` will be set to track
|
||||
// the input and output relocations.
|
||||
//
|
||||
// Unlike `flushDataFromPointerToShared`, this method will not copy the input pointer data to the
|
||||
// shared memory pool. Use `relocationOut` to flush the input or output data after the call.
|
||||
nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
|
||||
const nn::Request* request, uint32_t alignment, uint32_t padding,
|
||||
std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut);
|
||||
using nn::convertRequestFromPointerToShared;
|
||||
using nn::flushDataFromPointerToShared;
|
||||
using nn::hasNoPointerData;
|
||||
using nn::RequestRelocation;
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::utils
|
||||
|
||||
|
||||
@@ -31,59 +31,6 @@
|
||||
#include <vector>
|
||||
|
||||
namespace android::hardware::neuralnetworks::utils {
|
||||
namespace {
|
||||
|
||||
bool hasNoPointerData(const nn::Operand& operand);
|
||||
bool hasNoPointerData(const nn::Model::Subgraph& subgraph);
|
||||
bool hasNoPointerData(const nn::Request::Argument& argument);
|
||||
|
||||
template <typename Type>
|
||||
bool hasNoPointerData(const std::vector<Type>& objects) {
|
||||
return std::all_of(objects.begin(), objects.end(),
|
||||
[](const auto& object) { return hasNoPointerData(object); });
|
||||
}
|
||||
|
||||
bool hasNoPointerData(const nn::DataLocation& location) {
|
||||
return std::visit([](auto ptr) { return ptr == nullptr; }, location.pointer);
|
||||
}
|
||||
|
||||
bool hasNoPointerData(const nn::Operand& operand) {
|
||||
return hasNoPointerData(operand.location);
|
||||
}
|
||||
|
||||
bool hasNoPointerData(const nn::Model::Subgraph& subgraph) {
|
||||
return hasNoPointerData(subgraph.operands);
|
||||
}
|
||||
|
||||
bool hasNoPointerData(const nn::Request::Argument& argument) {
|
||||
return hasNoPointerData(argument.location);
|
||||
}
|
||||
|
||||
void copyPointersToSharedMemory(nn::Operand* operand, nn::ConstantMemoryBuilder* memoryBuilder) {
|
||||
CHECK(operand != nullptr);
|
||||
CHECK(memoryBuilder != nullptr);
|
||||
|
||||
if (operand->lifetime != nn::Operand::LifeTime::POINTER) {
|
||||
return;
|
||||
}
|
||||
|
||||
const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
|
||||
operand->location.pointer);
|
||||
CHECK(data != nullptr);
|
||||
operand->lifetime = nn::Operand::LifeTime::CONSTANT_REFERENCE;
|
||||
operand->location = memoryBuilder->append(data, operand->location.length);
|
||||
}
|
||||
|
||||
void copyPointersToSharedMemory(nn::Model::Subgraph* subgraph,
|
||||
nn::ConstantMemoryBuilder* memoryBuilder) {
|
||||
CHECK(subgraph != nullptr);
|
||||
std::for_each(subgraph->operands.begin(), subgraph->operands.end(),
|
||||
[memoryBuilder](auto& operand) {
|
||||
copyPointersToSharedMemory(&operand, memoryBuilder);
|
||||
});
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
|
||||
const nn::Capabilities::PerformanceInfo& float32Performance,
|
||||
@@ -104,131 +51,4 @@ nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWit
|
||||
.value();
|
||||
}
|
||||
|
||||
bool hasNoPointerData(const nn::Model& model) {
|
||||
return hasNoPointerData(model.main) && hasNoPointerData(model.referenced);
|
||||
}
|
||||
|
||||
bool hasNoPointerData(const nn::Request& request) {
|
||||
return hasNoPointerData(request.inputs) && hasNoPointerData(request.outputs);
|
||||
}
|
||||
|
||||
nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
|
||||
const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut) {
|
||||
CHECK(model != nullptr);
|
||||
CHECK(maybeModelInSharedOut != nullptr);
|
||||
|
||||
if (hasNoPointerData(*model)) {
|
||||
return *model;
|
||||
}
|
||||
|
||||
// Make a copy of the model in order to make modifications. The modified model is returned to
|
||||
// the caller through `maybeModelInSharedOut` if the function succeeds.
|
||||
nn::Model modelInShared = *model;
|
||||
|
||||
nn::ConstantMemoryBuilder memoryBuilder(modelInShared.pools.size());
|
||||
copyPointersToSharedMemory(&modelInShared.main, &memoryBuilder);
|
||||
std::for_each(modelInShared.referenced.begin(), modelInShared.referenced.end(),
|
||||
[&memoryBuilder](auto& subgraph) {
|
||||
copyPointersToSharedMemory(&subgraph, &memoryBuilder);
|
||||
});
|
||||
|
||||
if (!memoryBuilder.empty()) {
|
||||
auto memory = NN_TRY(memoryBuilder.finish());
|
||||
modelInShared.pools.push_back(std::move(memory));
|
||||
}
|
||||
|
||||
*maybeModelInSharedOut = modelInShared;
|
||||
return **maybeModelInSharedOut;
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputRelocationTracker::flush() const {
|
||||
// Copy from pointers to shared memory.
|
||||
uint8_t* memoryPtr = static_cast<uint8_t*>(std::get<void*>(kMapping.pointer));
|
||||
for (const auto& [data, length, offset] : kRelocationInfos) {
|
||||
std::memcpy(memoryPtr + offset, data, length);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void OutputRelocationTracker::flush() const {
|
||||
// Copy from shared memory to pointers.
|
||||
const uint8_t* memoryPtr = static_cast<const uint8_t*>(
|
||||
std::visit([](auto ptr) { return static_cast<const void*>(ptr); }, kMapping.pointer));
|
||||
for (const auto& [data, length, offset] : kRelocationInfos) {
|
||||
std::memcpy(data, memoryPtr + offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
|
||||
const nn::Request* request, uint32_t alignment, uint32_t padding,
|
||||
std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut) {
|
||||
CHECK(request != nullptr);
|
||||
CHECK(maybeRequestInSharedOut != nullptr);
|
||||
CHECK(relocationOut != nullptr);
|
||||
|
||||
if (hasNoPointerData(*request)) {
|
||||
return *request;
|
||||
}
|
||||
|
||||
// Make a copy of the request in order to make modifications. The modified request is returned
|
||||
// to the caller through `maybeRequestInSharedOut` if the function succeeds.
|
||||
nn::Request requestInShared = *request;
|
||||
|
||||
RequestRelocation relocation;
|
||||
|
||||
// Change input pointers to shared memory.
|
||||
nn::MutableMemoryBuilder inputBuilder(requestInShared.pools.size());
|
||||
std::vector<InputRelocationInfo> inputRelocationInfos;
|
||||
for (auto& input : requestInShared.inputs) {
|
||||
const auto& location = input.location;
|
||||
if (input.lifetime != nn::Request::Argument::LifeTime::POINTER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
input.lifetime = nn::Request::Argument::LifeTime::POOL;
|
||||
const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
|
||||
location.pointer);
|
||||
CHECK(data != nullptr);
|
||||
input.location = inputBuilder.append(location.length, alignment, padding);
|
||||
inputRelocationInfos.push_back({data, input.location.length, input.location.offset});
|
||||
}
|
||||
|
||||
// Allocate input memory.
|
||||
if (!inputBuilder.empty()) {
|
||||
auto memory = NN_TRY(inputBuilder.finish());
|
||||
requestInShared.pools.push_back(memory);
|
||||
relocation.input = NN_TRY(
|
||||
InputRelocationTracker::create(std::move(inputRelocationInfos), std::move(memory)));
|
||||
}
|
||||
|
||||
// Change output pointers to shared memory.
|
||||
nn::MutableMemoryBuilder outputBuilder(requestInShared.pools.size());
|
||||
std::vector<OutputRelocationInfo> outputRelocationInfos;
|
||||
for (auto& output : requestInShared.outputs) {
|
||||
const auto& location = output.location;
|
||||
if (output.lifetime != nn::Request::Argument::LifeTime::POINTER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
output.lifetime = nn::Request::Argument::LifeTime::POOL;
|
||||
void* data = std::get<void*>(location.pointer);
|
||||
CHECK(data != nullptr);
|
||||
output.location = outputBuilder.append(location.length, alignment, padding);
|
||||
outputRelocationInfos.push_back({data, output.location.length, output.location.offset});
|
||||
}
|
||||
|
||||
// Allocate output memory.
|
||||
if (!outputBuilder.empty()) {
|
||||
auto memory = NN_TRY(outputBuilder.finish());
|
||||
requestInShared.pools.push_back(memory);
|
||||
relocation.output = NN_TRY(OutputRelocationTracker::create(std::move(outputRelocationInfos),
|
||||
std::move(memory)));
|
||||
}
|
||||
|
||||
*maybeRequestInSharedOut = requestInShared;
|
||||
*relocationOut = std::move(relocation);
|
||||
return **maybeRequestInSharedOut;
|
||||
}
|
||||
|
||||
} // namespace android::hardware::neuralnetworks::utils
|
||||
|
||||
Reference in New Issue
Block a user