Loading neuralnetworks/aidl/utils/src/Conversions.cpp +10 −0 Original line number Diff line number Diff line Loading @@ -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)), Loading @@ -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)), Loading neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h +5 −75 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <nnapi/Result.h> #include <nnapi/SharedMemory.h> #include <nnapi/Types.h> #include <functional> #include <vector> Loading Loading @@ -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 Loading neuralnetworks/utils/common/src/CommonUtils.cpp +0 −180 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 Loading
neuralnetworks/aidl/utils/src/Conversions.cpp +10 −0 Original line number Diff line number Diff line Loading @@ -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)), Loading @@ -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)), Loading
neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h +5 −75 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <nnapi/Result.h> #include <nnapi/SharedMemory.h> #include <nnapi/Types.h> #include <functional> #include <vector> Loading Loading @@ -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 Loading
neuralnetworks/utils/common/src/CommonUtils.cpp +0 −180 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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