* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#ifndef AIR_CXX_RUNTIME_V2_CORE_MODEL_V_2_EXECUTOR_H_
#define AIR_CXX_RUNTIME_V2_CORE_MODEL_V_2_EXECUTOR_H_
#include <memory>
#include <cstdlib>
#include "graph/compute_graph.h"
#include "graph/fast_graph/execute_graph.h"
#include "graph/ge_error_codes.h"
#include "model_desc.h"
#include "runtime/stream.h"
#include "exe_graph/runtime/tensor.h"
#include "common/ge_visibility.h"
#include "exe_graph_resource_guard.h"
#include "exe_graph_executor.h"
#include "subscriber/executor_subscribers_scheduler.h"
#include "common/ge_types.h"
#include "mem_allocator.h"
#include "framework/runtime/rt_session.h"
#include "framework/common/ge_types.h"
#include "framework/runtime/executor_option/executor_option.h"
#include "framework/runtime/stream_allocator.h"
#include "framework/runtime/event_allocator.h"
#include "common/host_resource_center/host_resource_center.h"
namespace gert {
enum class ExecutorState { kInit, kLoaded };
inline const ge::char_t *GetSubExeGraphTypeStr(const SubExeGraphType type) {
constexpr const ge::char_t *kSubExeGraphTypeStrs[kSubExeGraphTypeEnd] = {"Init", "Main", "DeInit"};
return kSubExeGraphTypeStrs[type];
}
enum class ExecuteArgIndex {
kNum = 4,
kNotifies = -1 * kNum,
kRtEvents,
kExternalAllocator,
kStream,
kEnd,
};
struct OuterWeightMem {
const void *weight_ptr;
size_t weight_size;
};
struct ModelExecuteArg {
* 如果外部传入的stream不为空,那么本模型将执行在外部传入的流上。
*/
rtStream_t stream;
* 是否使用外部的allocator来申请流上内存。如果本成员指针为空,那么执行器会自行创建一个默认allocator使用。
* 由于Host与Device之间下发任务后,Device总是在流上异步执行这个任务,因此allocator需要满足如下几个要求:
* 1. 一个allocator仅对应唯一的stream
* 2. 在对应的流同步之前,allocator内存池中的内存不可以归还到操作系统
* 3. 在对应的流同步之前,allocator不可以被析构(原理同2)
*/
Allocators *external_allocator;
* 是否使用外部的stream allocator来申请辅流。如果本成员指针为空,那么执行器会自行创建一个默认的stream allocator使用。
* 该stream allocator与ModelExecuteArg的第一个参数stream(外部主流)生命周期一致。若切换主流,也需要对应切换该allocator
*/
StreamAllocator *external_stream_allocator;
* 是否使用外部的event allocator来申请event。如果本成员指针为空,那么执行器会自行创建一个默认的event allocator使用。
* 该event allocator与ModelExecuteArg的第一个参数stream(外部主流)生命周期一致。若切换主流,也需要对应切换该allocator
* event allocator与stream allocator需要同时传入
*/
EventAllocator *external_event_allocator;
* 是否使用外部的notify allocator来申请notify。如果本成员指针为空,那么执行器会自行创建一个默认的notify
* allocator使用。 该notify
* allocator与ModelExecuteArg的第一个参数stream(外部主流)生命周期一致。若切换主流,也需要对应切换该allocator notify
* allocator与stream allocator需要同时传入
*/
NotifyAllocator *external_notify_allocator;
uint64_t reserved[8];
ModelExecuteArg() : ModelExecuteArg(nullptr, nullptr) {}
ModelExecuteArg(const rtStream_t stream_, Allocators *const external_allocator_ = nullptr)
: stream(stream_),
external_allocator(external_allocator_),
external_stream_allocator{nullptr},
external_event_allocator{nullptr},
external_notify_allocator{nullptr},
reserved{0U} {}
};
static_assert(std::is_standard_layout<ModelExecuteArg>::value, "The class ModelExecuteArg must be a POD");
struct ModelLoadArg {
RtSession *rt_session;
OuterWeightMem outer_weight_mem;
ModelLoadArg() : rt_session(nullptr), outer_weight_mem({nullptr, 0U}) {}
ModelLoadArg(RtSession *rt_session_tmp = nullptr, OuterWeightMem outer_weight_mem_tmp = {nullptr, 0U})
: rt_session(rt_session_tmp),
outer_weight_mem(outer_weight_mem_tmp) {}
};
static_assert(std::is_standard_layout<ModelLoadArg>::value, "The class ModelLoadArg must be a POD");
class VISIBILITY_EXPORT ModelV2Executor {
public:
static std::unique_ptr<ModelV2Executor> Create(const ge::ExecuteGraphPtr &exe_graph, const ge::ModelData &model_data,
const std::shared_ptr<ge::GeRootModel> &root_model,
RtSession *session = nullptr);
static std::unique_ptr<ModelV2Executor> Create(const ge::ExecuteGraphPtr &exe_graph,
const std::shared_ptr<ge::GeRootModel> &root_model,
RtSession *session = nullptr);
static std::unique_ptr<ModelV2Executor> Create(const ge::ExecuteGraphPtr &exe_graph, const ExecutorOption &option,
const std::shared_ptr<ge::GeRootModel> &root_model,
RtSession *session = nullptr);
ge::graphStatus Load();
* 加载模型,本接口需要在模型执行前被调用。加载流程会完成模型的初始化、将重要数据拷贝到NPU等整个模型生命周期内仅需要执行一次的行为。
* @param arg
* 模型的执行参数,需要注意的是,此处传入的执行参数应该与Execute接口传入的执行参数具有相同的stream和allocator,
* 否则在load完成后,外部需要调用流同步以保证不出现时序问题
* @return 成功时返回`ge::GRAPH_SUCCESS`
*/
ge::graphStatus Load(const ModelExecuteArg &arg);
* 加载模型,本接口需要在模型执行前被调用。加载流程会完成模型的初始化、将重要数据拷贝到NPU等整个模型生命周期内仅需要执行一次的行为。
* @param execute_arg
* 模型的执行参数,需要注意的是,此处传入的执行参数应该与Execute接口传入的执行参数具有相同的stream和allocator,
* 否则在load完成后,外部需要调用流同步以保证不出现时序问题
* @param load_arg 模型的加载参数,加载时由外部传入, 执行时不会改变的参数,如RtSession.
* @return 成功时返回`ge::GRAPH_SUCCESS`
*/
ge::graphStatus Load(const ModelExecuteArg &arg, const ModelLoadArg &load_arg);
* 异步执行模型,本接口将模型异步下发到NPU执行,本接口返回不代表模型执行完成,用户需要手动调用流同步等待模型执行完成。
* 调用本接口前,请确保已经调用`Load`接口
*
* 用户可以通过多种方式指定输出Tensor,其行为分别为:
*
* * 调用本接口前,用户自行申请了足量空间的输出内存,并通过输出Tensor传入:执行完成后,输出内容被写入到用户申请的输出Tensor。
* 若用户申请的输出Tensor不够长,那么本接口返回失败。
* * 用户生成了输出Tensor,但是没有申请输出内存,将不包含输出内存的Tensor传入:本接口内部主动申请输出内存,并将输出内存传出。
* 若用户没有在arg中指定Allocator,那么本接口输出的内存生命周期与本Executor一致;
* 如果用户在arg中传入了Allocator,那么输出内存将使用用户传入的Allocator申请
*
* 注意:
*
* 1. 本接口不支持并发调用
* 2.
* 如果外部指定了Allocator,那么建议Allocator应该与stream绑定,如果出现同一个allocator,匹配不同的stream多次调用Execute接口时,
* 需要满足两个条件:不可以并发调用,在切换stream执行中间,需要对上一条stream做流同步
* 3.
* 若外部指定了Allocator,在模型执行完成前,不可以将Allocator中的内存归还给操作系统(即使这块内存已经由执行器归还给Allocator)
*
* @param arg 执行参数
* @param inputs 网络的输入tensor,从调用本接口开始,到流同步等待本模型执行结束之前,用户需要保证传入的Tensor有效
* @param input_num 输入tensor的数量
* @param outputs 网络的输出tensor
* @param output_num 输出tensor的数量
* @return 成功时返回`ge::GRAPH_SUCCESS`
*/
ge::graphStatus Execute(const ModelExecuteArg &arg, Tensor **inputs, size_t input_num, Tensor **outputs,
size_t output_num);
ge::graphStatus ExecuteSync(Tensor **inputs, size_t input_num, Tensor **outputs, size_t output_num);
ge::graphStatus UnLoad();
const ModelDesc &GetModelDesc() const;
void SetModelDesc(ModelDesc *model_desc);
ExeGraphExecutor *GetExeGraphExecutor(const SubExeGraphType type) {
if (type >= kSubExeGraphTypeEnd) {
return nullptr;
}
return &graphs_[static_cast<size_t>(type)];
}
ExecutorSubscribersScheduler &GetSubscribers();
uint32_t GetIterationNum() const;
const ExecutorSubscribersScheduler &GetSubscribers() const;
ge::graphStatus ArrangeModelLoadArg(const ModelLoadArg &arg, std::vector<void *> &const_inputs);
ModelV2Executor(const ModelV2Executor &) = delete;
ModelV2Executor(ModelV2Executor &&) = delete;
ModelV2Executor &operator=(const ModelV2Executor &) = delete;
ModelV2Executor &operator=(ModelV2Executor &&) = delete;
void SetFileConstantWeightDir(const std::string &file_constant_weight_dir) {
file_constant_weight_dir_ = file_constant_weight_dir;
}
const std::string &GetFileConstantWeightDir() const {
return file_constant_weight_dir_;
}
* @brief 获取aipp相关config info
* @param [in] index 输入index
* @param [out] aipp_info 待输出的aipp info
* @return 成功时返回`ge::SUCCESS`
*/
ge::Status GetAippInfo(const uint32_t index, ge::AippConfigInfo &aipp_info) const;
* @brief 获取aipp输入类型
* @param [in] index 输入index
* @param [out] aipp_type 待返回的aipp type
* @param [out] aipp_index 待返回的aipp index
* @return 成功时返回`ge::SUCCESS`
*/
ge::Status GetAippType(const uint32_t index, ge::InputAippType &aipp_type, size_t &aipp_index) const;
ge::Status GetOriginAippInputInfo(const uint32_t index, ge::OriginInputInfo &orig_aipp_input_info) const;
ge::Status GetAllAippInputOutputDims(const uint32_t index, std::vector<ge::InputOutputDims> &input_dims,
std::vector<ge::InputOutputDims> &output_dims) const;
ge::Status InitAipp(const ge::ComputeGraphPtr &root_graph);
private:
friend class ModelV2ExecutorBuilder;
friend class ModelV2ExecutorTestHelper;
ModelV2Executor();
ge::graphStatus SpecifyArgsInputs(const ModelExecuteArg &arg, size_t input_num, ExeGraphExecutor &graph_executor);
ge::graphStatus OccupyStreamResource(const ModelExecuteArg &arg, TypedContinuousVector<rtStream_t> *&streams,
TypedContinuousVector<rtEvent_t> *&events,
TypedContinuousVector<rtNotify_t> *¬ifies);
ge::Status InitRtVarManager(const ModelLoadArg &load_arg);
ge::graphStatus CheckIoReuseAddrs(Tensor **inputs, size_t input_num, Tensor **outputs, size_t output_num) const;
private:
ge::HostResourceCenterPtr host_resource_center_;
TopologicalResourceGuard resource_guard_;
std::array<ExeGraphExecutor, kSubExeGraphTypeEnd> graphs_;
ModelDesc *model_desc_ = nullptr;
rtStream_t default_stream_ = nullptr;
ExecutorSubscribersScheduler subscribers_;
ExecutorState state_ = ExecutorState::kInit;
std::string file_constant_weight_dir_;
* 背景:对于aipp离线推理场景下,acl需要获取编译时期很多aipp的相关信息,rt2场景下需要适配
* 临时规避方案:因为是客户问题,时间比较紧,所以当前简单仿照静态shape下获取aipp的逻辑,
数据结构和对应给acl的api保持一致,rt2放在ModelV2Executor类成员变量并提供相应接口
* 正式方案:对于aipp场景下新定义一个数据结构,例如AllAippInfo的结构体,该结构体里把所有aipp需要的信息都放在里面,
静态动态下只提供一个API,例如GetAllAippINfo()返回给acl,这样ge只提供一个接口,acl获取完之后相应的Get接口直接在acl层闭环,
该方案涉及metadef,air,acl三个仓的联合修改。
*/
std::map<uint32_t, ge::AippConfigInfo> aipp_info_list_;
std::map<uint32_t, std::pair<ge::InputAippType, size_t>> aipp_type_list_;
std::map<uint32_t, ge::OriginInputInfo> orig_aipp_input_info_;
std::map<uint32_t, std::pair<std::vector<ge::InputOutputDims>, std::vector<ge::InputOutputDims>>> aipp_dims_info_;
EventAllocator builtin_event_allocator_;
StreamAllocator builtin_stream_allocator_;
NotifyAllocator builtin_notify_allocator_;
uint64_t load_session_id_{std::numeric_limits<uint64_t>::max()};
RtSession default_rt_session_{load_session_id_};
std::vector<std::pair<size_t, size_t>> io_same_addr_pairs_;
};
}
#endif