已合并
【feat】: 为 MallocReadOnlyDevArgs 添加 v2 动态图支持 #3655
yuht9创建于 6月22日
【feat】: 为 MallocReadOnlyDevArgs 添加 v2 动态图支持 #3655
已合并
yuht9创建于 6月22日
13 个文件变更+558-109
@@ -127,12 +127,13 @@ void *EagerOpExecutionContext::MallocWorkSpace(size_t size) {
127 return mem_block->GetAddr();127 return mem_block->GetAddr();
128}128}
129 129 
130-const KernelArgs *EagerOpExecutionContext::MallocReadOnlyDevArgs(void *host_args, size_t args_size) const {130+const KernelArgs* EagerOpExecutionContext::MallocReadOnlyDevArgs(void *host_args, size_t args_size) const {
131- auto additional_start_index = GetAdditionalInputStartIndex();131+ auto additional_output_start = GetAdditionalOutputStartIndex();
132- GE_ASSERT_TRUE(additional_start_index >= 0);132+ GE_ASSERT_TRUE(additional_output_start >= 0);
133 133 
134- auto *handler =134+ auto *chain = GetOutput(static_cast<size_t>(additional_output_start) + static_cast<size_t>(AdditionalOutputIndex::kArgsHandler));
135- GetInputValue<ArgsHandler *>(additional_start_index + static_cast<int64_t>(AdditionalInputIndex::kArgsHandler));135+ GE_ASSERT_NOTNULL(chain);
136+ auto *handler = chain->GetValue<ArgsHandler *>();
136 GE_ASSERT_NOTNULL(handler);137 GE_ASSERT_NOTNULL(handler);
137 138 
138 return handler->MallocReadOnlyDevArgs(host_args, args_size);139 return handler->MallocReadOnlyDevArgs(host_args, args_size);
@@ -15,12 +15,14 @@
15 15 
16namespace gert {16namespace gert {
17 17 
18-const KernelArgs *UpdateArgsContext::GetKernelArgs(Placement placement, size_t index) const {18+const KernelArgs* UpdateArgsContext::GetKernelArgs(Placement placement, size_t index) const {
19- auto additional_start_index = GetAdditionalInputStartIndex();19+ auto additional_output_start = GetAdditionalOutputStartIndex();
20- GE_ASSERT_TRUE(additional_start_index >= 0);20+ GE_ASSERT_TRUE(additional_output_start >= 0);
21 21 
22- auto *handler = GetInputValue<ArgsHandler *>(22+ auto *chain = GetOutput(static_cast<size_t>(additional_output_start) +
23- additional_start_index + static_cast<int64_t>(EagerOpExecutionContext::AdditionalInputIndex::kArgsHandler));23+ static_cast<size_t>(EagerOpExecutionContext::AdditionalOutputIndex::kArgsHandler));
24+ GE_ASSERT_NOTNULL(chain);
25+ auto *handler = chain->GetValue<ArgsHandler *>();
24 GE_ASSERT_NOTNULL(handler);26 GE_ASSERT_NOTNULL(handler);
25 27 
26 const auto &args_deque = handler->GetKernelArgs(placement);28 const auto &args_deque = handler->GetKernelArgs(placement);
@@ -125,10 +125,14 @@ class EagerOpExecutionContext : public ExtendedKernelContext {
125 enum class AdditionalInputIndex : uint32_t {125 enum class AdditionalInputIndex : uint32_t {
126 kDeviceAllocator = 0,126 kDeviceAllocator = 0,
127 kStream,127 kStream,
128- kArgsHandler // Args handler base class pointer128+ kNum
129 };129 };
130 130 
131- enum class AdditionalOutputIndex : uint32_t { kWorkSpace = 0, kNum };131+ enum class AdditionalOutputIndex : uint32_t {
132+ kWorkSpace = 0,
133+ kArgsHandler,
134+ kNum
135+ };
132 136 
133 protected:137 protected:
134 int64_t GetAdditionalInputStartIndex() const {138 int64_t GetAdditionalInputStartIndex() const {
@@ -308,10 +308,8 @@ Status CustomTaskInfo::Distribute() {
308 308 
309 args_handler_ = ge::ComGraphMakeUnique<SinkOpArgsHandler>(this);309 args_handler_ = ge::ComGraphMakeUnique<SinkOpArgsHandler>(this);
310 GE_ASSERT_NOTNULL(args_handler_);310 GE_ASSERT_NOTNULL(args_handler_);
311- std::vector<void *> additional_inputs = {sink_only_allocator_.get(), stream_, args_handler_.get()};311+ std::vector<void*> additional_inputs = {sink_only_allocator_.get(), stream_};
312- 312+ std::vector<void*> additional_outputs = {&ws_vec_, args_handler_.get()};
313- std::vector<void *> additional_outputs;
314- additional_outputs.push_back(&ws_vec_);
315 313 
316 eager_context_holder_ = gert::KernelRunContextBuilder()314 eager_context_holder_ = gert::KernelRunContextBuilder()
317 .Inputs(GetHoldersRawPtr(inputs_holder_))315 .Inputs(GetHoldersRawPtr(inputs_holder_))
@@ -18,6 +18,7 @@
18#include "graph/custom_op_registry.h"18#include "graph/custom_op_registry.h"
19#include "lowering/placement/placed_lowering_result.h"19#include "lowering/placement/placed_lowering_result.h"
20#include "exe_graph/lowering/lowering_definitions.h"20#include "exe_graph/lowering/lowering_definitions.h"
21+#include "exe_graph/runtime/eager_op_execution_context.h"
21#include "common/ge_common/ge_types.h"22#include "common/ge_common/ge_types.h"
22#include "graph/utils/inference_rule.h"23#include "graph/utils/inference_rule.h"
23 24 
@@ -106,9 +107,11 @@ LowerResult LoweringCustomNode(const ge::NodePtr &node, const LowerInput &lower_
106 infer_output_shapes = bg::InferCustomOpShape(node, lower_input.input_shapes, *lower_input.global_data);107 infer_output_shapes = bg::InferCustomOpShape(node, lower_input.input_shapes, *lower_input.global_data);
107 input_holders.insert(input_holders.end(), infer_output_shapes.begin(), infer_output_shapes.end());108 input_holders.insert(input_holders.end(), infer_output_shapes.begin(), infer_output_shapes.end());
108 }109 }
109- // 最后需要一个workspace地址110+ // 最后需要workspace地址和args_handler地址
110 std::vector<bg::ValueHolderPtr> output_tensor_holders =111 std::vector<bg::ValueHolderPtr> output_tensor_holders =
111- bg::ValueHolder::CreateDataOutput(kernel_type.c_str(), input_holders, node->GetAllOutDataAnchorsSize() + 1);112+ bg::ValueHolder::CreateDataOutput(kernel_type.c_str(),
113+ input_holders, node->GetAllOutDataAnchorsSize() +
114+ static_cast<size_t>(gert::EagerOpExecutionContext::AdditionalOutputIndex::kNum));
112 std::vector<bg::ValueHolderPtr> output_shapes;115 std::vector<bg::ValueHolderPtr> output_shapes;
113 std::vector<bg::DevMemValueHolderPtr> output_addrs;116 std::vector<bg::DevMemValueHolderPtr> output_addrs;
114 for (size_t i = 0UL; i < node->GetAllOutDataAnchorsSize(); i++) {117 for (size_t i = 0UL; i < node->GetAllOutDataAnchorsSize(); i++) {
@@ -121,8 +124,11 @@ LowerResult LoweringCustomNode(const ge::NodePtr &node, const LowerInput &lower_
121 output_shapes.emplace_back(split_outputs[static_cast<size_t>(kernel::SplitTensorOutputs::kShape)]);124 output_shapes.emplace_back(split_outputs[static_cast<size_t>(kernel::SplitTensorOutputs::kShape)]);
122 output_addrs.emplace_back(split_outputs[static_cast<size_t>(kernel::SplitTensorOutputs::kTensorData)]);125 output_addrs.emplace_back(split_outputs[static_cast<size_t>(kernel::SplitTensorOutputs::kTensorData)]);
123 }126 }
124- LOWER_REQUIRE_NOTNULL(127+ LOWER_REQUIRE_NOTNULL(bg::ValueHolder::CreateVoidGuarder("FreeCustomOpWorkspaces",
125- bg::ValueHolder::CreateVoidGuarder("FreeCustomOpWorkspaces", output_tensor_holders.back(), {allocator_holder}));128+ output_tensor_holders[node->GetAllOutDataAnchorsSize()], {allocator_holder}));
129+ LOWER_REQUIRE_NOTNULL(bg::ValueHolder::CreateVoidGuarder("FreeArgsGuarder",
130+ output_tensor_holders[node->GetAllOutDataAnchorsSize() +
131+ static_cast<size_t>(gert::EagerOpExecutionContext::AdditionalOutputIndex::kArgsHandler)], {}));
126 // 输入tensor需要添加对地址guard的依赖边,否则会出现提前释放132 // 输入tensor需要添加对地址guard的依赖边,否则会出现提前释放
127 for (auto &addr : input_addr_holders) {133 for (auto &addr : input_addr_holders) {
128 auto guarder = addr->GetGuarder();134 auto guarder = addr->GetGuarder();
@@ -19,11 +19,17 @@
19#include "graph/utils/type_utils.h"19#include "graph/utils/type_utils.h"
20#include "exe_graph/runtime/eager_op_execution_context.h"20#include "exe_graph/runtime/eager_op_execution_context.h"
21#include "rt_external_kernel.h"21#include "rt_external_kernel.h"
22+#include "runtime/v2/engine/custom/kernel/eager_args_handler.h"
23+#include "runtime/kernel.h"
22 24 
23namespace gert {25namespace gert {
24namespace kernel {26namespace kernel {
25namespace {27namespace {
26-enum class CustomOpInput { kAllocator = 0, kStream, kFunc, kEnd };28+// 自定义算子特有的输入,从 AdditionalInputIndex::kNum 开始
29+enum class CustomOpInput {
30+ kFunc = static_cast<uint32_t>(EagerOpExecutionContext::AdditionalInputIndex::kNum),
31+ kEnd
32+};
atomgit-bot
atomgit-botatomgit-bot6月23日

🔵 Low Priority

CustomOpInput 枚举中移除了 kAllocatorkStream,现在仅保留 kFunc(值从 AdditionalInputIndex::kNum 即 2 开始)。这意味着 ExecuteCustomOpWithInferShapeFuncCopyShapeFromTemplateTensors 使用的 CustomOpInput::kEnd 值(=3)与之前相同,因为旧代码中 kAllocator=0, kStream=1, kFunc=2, kEnd=3,新代码中 kFunc=2, kEnd=3。数值一致,无偏移。

ExecuteCustomOpImpl 中通过 AdditionalInputIndex::kDeviceAllocator(值为 0)获取 allocator,其索引计算为 node_input_num + 0。输入布局中 additional inputs 为 [allocator, stream, custom_func](共 3 个),其中 allocator 在 index 0(即 kDeviceAllocator),custom_func 在 index 2(即 kFunc)。这与枚举值一致。

该变更在概念上正确,但 CustomOpInput 移除了 kAllocatorkStream 后,代码中曾引用这两个值的遗留注释或文档可能会产生误导。不过在当前 diff 范围内无实际问题。

这是一个信息性评论,不构成需要修复的缺陷。

建议:无需修改;此评论仅为信息性确认:CustomOpInput::kEnd 的值(3)在枚举重构前后保持一致,不影响 CopyShapeFromTemplateTensors 的索引计算。

likedislike
不准确?
yuht9
6月24日 评论:
27 33 
28std::string PrintNodeType(const KernelContext *context) {34std::string PrintNodeType(const KernelContext *context) {
29 std::stringstream ss;35 std::stringstream ss;
@@ -110,13 +116,23 @@ static ge::graphStatus CreateWorkspaceHolder(KernelContext *context, size_t node
110 return ge::GRAPH_SUCCESS;116 return ge::GRAPH_SUCCESS;
111}117}
112 118 
113-static ge::graphStatus CreateWorkspacesMemory(const ge::FastNode *node, KernelContext *context) {119+static ge::graphStatus CreateCustomOpOutputs(const ge::FastNode *node, KernelContext *context) {
114 (void)node;120 (void)node;
115 auto *extended_kernel_context = reinterpret_cast<ExtendedKernelContext *>(context);121 auto *extended_kernel_context = reinterpret_cast<ExtendedKernelContext *>(context);
116 GE_ASSERT_NOTNULL(extended_kernel_context);122 GE_ASSERT_NOTNULL(extended_kernel_context);
117 const size_t node_output_num = extended_kernel_context->GetComputeNodeOutputNum();123 const size_t node_output_num = extended_kernel_context->GetComputeNodeOutputNum();
118 GE_ASSERT_SUCCESS(CreateOutputTensors(extended_kernel_context, context));124 GE_ASSERT_SUCCESS(CreateOutputTensors(extended_kernel_context, context));
119 GE_ASSERT_SUCCESS(CreateWorkspaceHolder(context, node_output_num));125 GE_ASSERT_SUCCESS(CreateWorkspaceHolder(context, node_output_num));
126+ 
127+ // allocator 在 Create 阶段尚未就绪(Init 图未执行),创建空 EagerArgsHandler,在 RunFunc 阶段初始化
128+ auto *args_handler = new (std::nothrow) EagerArgsHandler();
129+ GE_ASSERT_NOTNULL(args_handler);
130+ 
131+ auto *args_output = context->GetOutput(node_output_num +
132+ static_cast<size_t>(EagerOpExecutionContext::AdditionalOutputIndex::kArgsHandler));
133+ GE_ASSERT_NOTNULL(args_output);
134+ args_output->SetWithDefaultDeleter(static_cast<ArgsHandler *>(args_handler));
135+ 
120 return ge::GRAPH_SUCCESS;136 return ge::GRAPH_SUCCESS;
121}137}
122 138 
@@ -138,6 +154,21 @@ static ge::graphStatus ExecuteCustomOpImpl(KernelContext *context) {
138 auto *eager_context = reinterpret_cast<EagerOpExecutionContext *>(context);154 auto *eager_context = reinterpret_cast<EagerOpExecutionContext *>(context);
139 GE_ASSERT_NOTNULL(eager_context);155 GE_ASSERT_NOTNULL(eager_context);
140 const size_t node_input_num = eager_context->GetComputeNodeInputNum();156 const size_t node_input_num = eager_context->GetComputeNodeInputNum();
157+ const size_t node_output_num = eager_context->GetComputeNodeOutputNum();
158+ auto *chain = context->GetOutput(node_output_num +
159+ static_cast<size_t>(EagerOpExecutionContext::AdditionalOutputIndex::kArgsHandler));
160+ GE_ASSERT_NOTNULL(chain);
161+ auto *args_handler = static_cast<EagerArgsHandler *>(chain->GetValue<ArgsHandler *>());
162+ GE_ASSERT_NOTNULL(args_handler);
163+ if (!args_handler->IsInitialized()) {
164+ auto *allocator = context->GetInputValue<GertAllocator *>(
165+ node_input_num + static_cast<size_t>(EagerOpExecutionContext::AdditionalInputIndex::kDeviceAllocator));
166+ GE_ASSERT_NOTNULL(allocator);
167+ auto stream_id = allocator->GetStreamId();
168+ args_handler->Initialize(allocator, stream_id);
169+ GELOGD("EagerArgsHandler initialized in RunFunc with allocator %p", allocator);
170+ }
171+ 
141 auto custom_op_ptr =172 auto custom_op_ptr =
142 context->GetInputValue<ge::BaseCustomOp *>(node_input_num + static_cast<size_t>(CustomOpInput::kFunc));173 context->GetInputValue<ge::BaseCustomOp *>(node_input_num + static_cast<size_t>(CustomOpInput::kFunc));
143 GE_ASSERT_NOTNULL(custom_op_ptr);174 GE_ASSERT_NOTNULL(custom_op_ptr);
@@ -177,6 +208,14 @@ ge::graphStatus FreeCustomOpWorkspacesFunc(KernelContext *context) {
177 return ge::GRAPH_SUCCESS;208 return ge::GRAPH_SUCCESS;
178}209}
179 210 
211+ge::graphStatus FreeArgsGuarderFunc(KernelContext *context) {
212+ auto *handler_base = context->GetInputValue<ArgsHandler *>(0);
213+ if (handler_base != nullptr) {
214+ static_cast<EagerArgsHandler *>(handler_base)->Release();
215+ }
216+ return ge::GRAPH_SUCCESS;
217+}
218+ 
180static std::vector<std::string> CustomOpExecuteKernelTrace(const KernelContext *context) {219static std::vector<std::string> CustomOpExecuteKernelTrace(const KernelContext *context) {
181 auto extend_context = reinterpret_cast<const ExtendedKernelContext *>(context);220 auto extend_context = reinterpret_cast<const ExtendedKernelContext *>(context);
182 auto compute_node_info = extend_context->GetComputeNodeInfo();221 auto compute_node_info = extend_context->GetComputeNodeInfo();
@@ -251,16 +290,13 @@ ge::graphStatus CustomOpProfilingDataFill(const KernelContext *context, Profilin
251}290}
252 291 
253REGISTER_KERNEL(FindCustomOp).RunFunc(FindCustomOpFunc);292REGISTER_KERNEL(FindCustomOp).RunFunc(FindCustomOpFunc);
254-REGISTER_KERNEL(ExecuteCustomOp)293+REGISTER_KERNEL(ExecuteCustomOp).OutputsCreator(CreateCustomOpOutputs)
255- .OutputsCreator(CreateWorkspacesMemory)294+ .RunFunc(ExecuteCustomOpFunc).TracePrinter(CustomOpExecuteKernelTrace)
256- .RunFunc(ExecuteCustomOpFunc)
257- .TracePrinter(CustomOpExecuteKernelTrace)
258 .ProfilingInfoFiller(CustomOpProfilingDataFill);295 .ProfilingInfoFiller(CustomOpProfilingDataFill);
259-REGISTER_KERNEL(ExecuteCustomOpWithInferShape)296+REGISTER_KERNEL(ExecuteCustomOpWithInferShape).OutputsCreator(CreateCustomOpOutputs)
260- .OutputsCreator(CreateWorkspacesMemory)297+ .RunFunc(ExecuteCustomOpWithInferShapeFunc).TracePrinter(CustomOpExecuteKernelTrace)
261- .RunFunc(ExecuteCustomOpWithInferShapeFunc)
262- .TracePrinter(CustomOpExecuteKernelTrace)
263 .ProfilingInfoFiller(CustomOpProfilingDataFill);298 .ProfilingInfoFiller(CustomOpProfilingDataFill);
264REGISTER_KERNEL(FreeCustomOpWorkspaces).RunFunc(FreeCustomOpWorkspacesFunc);299REGISTER_KERNEL(FreeCustomOpWorkspaces).RunFunc(FreeCustomOpWorkspacesFunc);
265-} // namespace kernel300+REGISTER_KERNEL(FreeArgsGuarder).RunFunc(FreeArgsGuarderFunc);
266-} // namespace gert301+}
302+}
@@ -0,0 +1,64 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "eager_args_handler.h"
12+#include "acl/acl_rt.h"
13+#include "common/checker.h"
14+ 
15+namespace gert {
16+ 
17+EagerArgsHandler::EagerArgsHandler(GertAllocator *allocator, int64_t stream_id)
18+ : allocator_(allocator), stream_id_(stream_id) {}
19+ 
20+void EagerArgsHandler::Initialize(GertAllocator *allocator, int64_t stream_id) {
21+ allocator_ = allocator;
22+ stream_id_ = stream_id;
23+}
24+ 
25+const KernelArgs* EagerArgsHandler::MallocReadOnlyDevArgs(void *host_args, size_t args_size) {
26+ GE_ASSERT_NOTNULL(allocator_);
27+ GE_ASSERT_NOTNULL(host_args);
28+ GE_ASSERT_TRUE(args_size > 0);
atomgit-bot
atomgit-botatomgit-bot6月23日

🟡 Medium Priority

eager_args_handler.cc 第 26 行使用 GE_ASSERT_NOTNULL(allocator_) 检查 allocator_ 是否为空。但对应的单元测试 MallocReadOnlyDevArgs_WithoutInitialize_ReturnsNullptr(第 191-197 行)期望当 handler 未初始化(allocator_ 为 nullptr)时,该函数返回 nullptr 而非崩溃。

从代码模式看,GE_ASSERT_NOTNULL 在断言失败时很可能会导致进程终止(如 assert),这意味着单元测试会崩溃而非通过。即使断言不终止进程,后续 allocator_->Malloc(args_size) 也会对空指针解引用导致崩溃。

CreateCustomOpOutputs 创建了一个未初始化的 EagerArgsHandler(allocator_=nullptr),如果 ExecuteCustomOpImpl 因任何原因未能执行(或执行前就发生了异常),FreeArgsGuarder 仍会尝试调用 handler,但不会调用 MallocReadOnlyDevArgs。但单元测试明确覆盖了"未初始化就调用 MallocReadOnlyDevArgs 应返回 nullptr"的场景,因此实现应与测试预期一致。

建议:将 GE_ASSERT_NOTNULL(allocator_) 替换为显式的空指针检查并返回 nullptr,使实现与测试预期一致。同时,GE_ASSERT_NOTNULL(host_args)GE_ASSERT_TRUE(args_size > 0) 也应改为显式检查。

likedislike
不准确?
yuht9
6月24日 评论:
29+ 
30+ auto *block = allocator_->Malloc(args_size);
31+ GE_ASSERT_NOTNULL(block);
32+ 
33+ auto ret = aclrtMemcpy(block->GetAddr(), args_size, host_args, args_size, ACL_MEMCPY_HOST_TO_DEVICE);
34+ GE_ASSERT_RT_OK(ret);
35+ allocated_blocks_.push_back(block);
36+ 
37+ KernelArgs args;
38+ args.args_data = block->GetAddr();
39+ args.args_size = args_size;
40+ args.placement = Placement::kPlacementDevice;
41+ device_args_.push_back(args);
42+ 
43+ return &device_args_.back();
44+}
45+ 
46+const std::deque<KernelArgs>& EagerArgsHandler::GetKernelArgs(Placement placement) const {
47+ if (placement == Placement::kPlacementHost) {
48+ static const std::deque<KernelArgs> kEmptyArgs;
49+ return kEmptyArgs;
50+ }
51+ return device_args_;
52+}
53+ 
54+void EagerArgsHandler::Release() {
55+ for (auto *block : allocated_blocks_) {
56+ if (block != nullptr) {
57+ block->Free(stream_id_);
58+ }
59+ }
60+ allocated_blocks_.clear();
61+ device_args_.clear();
62+}
63+ 
64+} // namespace gert
@@ -0,0 +1,46 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef AIR_CXX_RUNTIME_V2_ENGINE_CUSTOM_KERNEL_EAGER_ARGS_HANDLER_H_
12+#define AIR_CXX_RUNTIME_V2_ENGINE_CUSTOM_KERNEL_EAGER_ARGS_HANDLER_H_
13+ 
14+#include <cstdint>
15+#include <deque>
16+#include <vector>
17+#include "framework/runtime/args_handler.h"
18+#include "exe_graph/runtime/gert_mem_allocator.h"
19+#include "exe_graph/runtime/gert_mem_block.h"
20+#include "exe_graph/runtime/kernel_args.h"
atomgit-bot
atomgit-botatomgit-bot6月23日

🔵 Low Priority

eager_args_handler.h 在第 26、29、37 行使用了 int64_t 类型,但没有显式包含 头文件。虽然当前可能通过其他头文件(如 gert_mem_allocator.h、kernel_args.h 等)的传递包含而编译通过,但 C++ 标准不保证标准库头文件 <deque><vector> 会提供 <cstdint> 中的定宽整数类型。如果未来包含链发生变化,可能导致编译失败。

建议:在现有 #include 列表中添加 #include ,确保 int64_t 类型显式可用。

改动建议
20
+ #include <cstdint>
21
+ #include <deque>
22
+ #include <vector>
23
+ #include "framework/runtime/args_handler.h"
24
+ #include "exe_graph/runtime/gert_mem_allocator.h"
25
+ #include "exe_graph/runtime/gert_mem_block.h"
20
26
  #include "exe_graph/runtime/kernel_args.h"
应用建议
likedislike
不准确?
21+ 
22+namespace gert {
23+ 
24+class EagerArgsHandler : public ArgsHandler {
25+ public:
26+ EagerArgsHandler() : allocator_(nullptr), stream_id_(-1) {}
27+ EagerArgsHandler(GertAllocator *allocator, int64_t stream_id);
28+ ~EagerArgsHandler() override { Release(); }
29+ 
30+ void Initialize(GertAllocator *allocator, int64_t stream_id);
31+ bool IsInitialized() const { return allocator_ != nullptr; }
32+ const KernelArgs* MallocReadOnlyDevArgs(void *host_args, size_t args_size) override;
33+ const std::deque<KernelArgs>& GetKernelArgs(Placement placement) const override;
34+ 
35+ void Release();
36+ 
37+ private:
38+ GertAllocator *allocator_; // 生命周期由框架保证,覆盖整个执行过程
39+ int64_t stream_id_;
40+ std::deque<KernelArgs> device_args_;
41+ std::vector<GertMemBlock *> allocated_blocks_;
42+};
43+ 
44+} // namespace gert
45+ 
46+#endif // AIR_CXX_RUNTIME_V2_ENGINE_CUSTOM_KERNEL_EAGER_ARGS_HANDLER_H_
@@ -102,6 +102,24 @@ class TestCompilableCustomOp : public EagerExecuteOp, CompilableOp {
102 std::string mock_compile_path_;102 std::string mock_compile_path_;
103};103};
104 104 
105+class TestMallocReadOnlyDevArgsCustomOp : public EagerExecuteOp {
106+ public:
107+ graphStatus Execute(gert::EagerOpExecutionContext *ctx) override {
108+ uint64_t host_args[4] = {0xAAAA, 0xBBBB, 0xCCCC, 0xDDDD};
109+ auto *dev_args = ctx->MallocReadOnlyDevArgs(host_args, sizeof(host_args));
110+ GE_ASSERT_NOTNULL(dev_args);
111+ GE_ASSERT_NOTNULL(dev_args->args_data);
112+ GE_ASSERT_EQ(dev_args->args_size, sizeof(host_args));
113+ GE_ASSERT_EQ(dev_args->placement, gert::Placement::kPlacementDevice);
114+ auto output_tensor = ctx->MallocOutputTensor(0, StorageShape({2048}, {2048}),
115+ StorageFormat(FORMAT_ND, FORMAT_ND, ExpandDimsType()), DT_FLOAT);
116+ GE_ASSERT_NOTNULL(output_tensor);
117+ output_addr = output_tensor->GetAddr();
118+ GE_ASSERT_NOTNULL(output_addr);
119+ return SUCCESS;
120+ }
121+};
122+ 
105class TestCustomOpWithShapeInfer : public EagerExecuteOp, public ShapeInferOp {123class TestCustomOpWithShapeInfer : public EagerExecuteOp, public ShapeInferOp {
106 public:124 public:
107 graphStatus Execute(EagerOpExecutionContext *ctx) override {125 graphStatus Execute(EagerOpExecutionContext *ctx) override {
@@ -190,29 +208,36 @@ class TestEmptyOutputInstanceCompileCustomOp : public EagerExecuteOp, Compilable
190};208};
191 209 
192REG_OP(CustomOp)210REG_OP(CustomOp)
193- .INPUT(x1, TensorType::BasicType())211+ .INPUT(x1, TensorType::BasicType())
194- .INPUT(x2, TensorType::BasicType())212+ .INPUT(x2, TensorType::BasicType())
195- .INPUT(x3, TensorType::BasicType())213+ .INPUT(x3, TensorType::BasicType())
196- .OUTPUT(y, TensorType::BasicType())214+ .OUTPUT(y, TensorType::BasicType())
197- .OP_END_FACTORY_REG(CustomOp)215+ .OP_END_FACTORY_REG(CustomOp)
198 216 
199- REG_OP(NoInputCompileOutputCustomOp)217+REG_OP(NoInputCompileOutputCustomOp)
200- .OUTPUT(y, TensorType::BasicType())218+ .OUTPUT(y, TensorType::BasicType())
201- .DYNAMIC_OUTPUT(dy, TensorType::BasicType())219+ .DYNAMIC_OUTPUT(dy, TensorType::BasicType())
202- .OP_END_FACTORY_REG(NoInputCompileOutputCustomOp)220+ .OP_END_FACTORY_REG(NoInputCompileOutputCustomOp)
203 221 
204- REG_OP(EmptyOutputInstanceCompileCustomOp)222+REG_OP(EmptyOutputInstanceCompileCustomOp)
205- .OUTPUT(y, TensorType::BasicType())223+ .OUTPUT(y, TensorType::BasicType())
206- .OP_END_FACTORY_REG(EmptyOutputInstanceCompileCustomOp)224+ .OP_END_FACTORY_REG(EmptyOutputInstanceCompileCustomOp)
207 225 
208- REG_OP(StCustomOpWithShapeInfer)226+REG_OP(StCustomOpWithShapeInfer)
209- .INPUT(x1, TensorType::BasicType())227+ .INPUT(x1, TensorType::BasicType())
210- .INPUT(x2, TensorType::BasicType())228+ .INPUT(x2, TensorType::BasicType())
211- .INPUT(x3, TensorType::BasicType())229+ .INPUT(x3, TensorType::BasicType())
212- .OUTPUT(y, TensorType::BasicType())230+ .OUTPUT(y, TensorType::BasicType())
213- .OP_END_FACTORY_REG(StCustomOpWithShapeInfer)231+ .OP_END_FACTORY_REG(StCustomOpWithShapeInfer)
214 232 
215- TEST_F(TestCustomNodeKernel, custom_op_kernel_execute_test) {233+REG_OP(MallocReadOnlyDevArgsCustomOp)
234+ .INPUT(x1, TensorType::BasicType())
235+ .INPUT(x2, TensorType::BasicType())
236+ .INPUT(x3, TensorType::BasicType())
237+ .OUTPUT(y, TensorType::BasicType())
238+ .OP_END_FACTORY_REG(MallocReadOnlyDevArgsCustomOp)
239+ 
240+TEST_F(TestCustomNodeKernel, custom_op_kernel_execute_test) {
216 auto graph = ShareGraph::BuildCustomOpGraph();241 auto graph = ShareGraph::BuildCustomOpGraph();
217 graph->TopologicalSorting();242 graph->TopologicalSorting();
218 CustomOpFactory::RegisterCustomOpCreator(243 CustomOpFactory::RegisterCustomOpCreator(
@@ -249,6 +274,7 @@ REG_OP(CustomOp)
249 GRAPH_SUCCESS);274 GRAPH_SUCCESS);
250 EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType("CustomOp", "ExecuteCustomOp"), 1);275 EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType("CustomOp", "ExecuteCustomOp"), 1);
251 EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType("CustomOp", "FreeCustomOpWorkspaces"), 1);276 EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType("CustomOp", "FreeCustomOpWorkspaces"), 1);
277+ EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType("CustomOp", "FreeArgsGuarder"), 1);
252 EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType("CustomOp", "FreeMemory"), 1);278 EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType("CustomOp", "FreeMemory"), 1);
253 EXPECT_TRUE(MemoryTraceChecker(runtime_stub.GetSlogStub(), output_addr)279 EXPECT_TRUE(MemoryTraceChecker(runtime_stub.GetSlogStub(), output_addr)
254 .AppendExpectEvent(kAllocRe, 0) // (1) alloc in stream 0280 .AppendExpectEvent(kAllocRe, 0) // (1) alloc in stream 0
@@ -387,5 +413,67 @@ TEST_F(TestCustomNodeKernel, custom_op_shape_infer_op_execute_test) {
387 EXPECT_EQ(model_executor->UnLoad(), GRAPH_SUCCESS);413 EXPECT_EQ(model_executor->UnLoad(), GRAPH_SUCCESS);
388 aclrtDestroyStream(stream);414 aclrtDestroyStream(stream);
389}415}
390-} // namespace kernel416+ 
391-} // namespace gert417+/**
418+ * 用例描述:验证自定义算子MallocReadOnlyDevArgs接口在V2运行时中正确分配设备侧只读args
419+ * 预置条件:
420+ * 1. 注册一个继承EagerExecuteOp的自定义算子,Execute中调用MallocReadOnlyDevArgs分配kernel args
421+ * 2. 构造包含该自定义算子的计算图,并通过ModelV2Executor加载执行
422+ * 测试步骤:
423+ * 1. 创建计算图并注册MallocReadOnlyDevArgsCustomOp算子
424+ * 2. 通过ModelV2Executor编译加载并执行
425+ * 3. 验证Execute/FreeArgsGuarder/FreeCustomOpWorkspaces kernel执行计数
426+ * 4. 通过MemoryTraceChecker验证输出Tensor内存的分配和释放事件
427+ * 5. 调用UnLoad卸载模型
428+ * 预期结果:
429+ * 1. 算子执行成功,各kernel执行计数符合预期
430+ * 2. 输出Tensor内存在stream 0上正确分配和释放
431+ * 3. 模型卸载成功
432+ */
433+TEST_F(TestCustomNodeKernel, custom_op_malloc_read_only_dev_args_test) {
434+ const char *const op_type = "MallocReadOnlyDevArgsCustomOp";
435+ auto graph = ShareGraph::BuildCustomOpGraph();
436+ auto custom_op = graph->FindNode("custom_op");
437+ ASSERT_NE(custom_op, nullptr);
438+ custom_op->GetOpDesc()->SetType(op_type);
439+ graph->TopologicalSorting();
440+ CustomOpFactory::RegisterCustomOpCreator(op_type, []()->std::unique_ptr<BaseCustomOp> {
441+ return std::make_unique<TestMallocReadOnlyDevArgsCustomOp>();
442+ });
443+ GertRuntimeStub runtime_stub;
444+ runtime_stub.GetKernelStub().StubTiling();
445+ GeModelBuilder builder(graph);
446+ auto ge_root_model = builder.BuildGeRootModel();
447+ bg::ValueHolder::PopGraphFrame();
448+ auto exe_graph = ModelConverter().ConvertGeModelToExecuteGraph(ge_root_model, {});
449+ ASSERT_NE(exe_graph, nullptr);
450+ TaskProducerFactory::GetInstance().SetProducerType(TaskProducerType::KERNEL);
451+ auto model_executor = ModelV2Executor::Create(exe_graph,
452+ ExecutorOption(ExecutorType::kTopologicalPriority), ge_root_model);
453+ ASSERT_NE(model_executor, nullptr);
454+ ASSERT_EQ(model_executor->Load(), GRAPH_SUCCESS);
455+ 
456+ auto outputs = FakeTensors({2048}, 1);
457+ auto inputs = FakeTensors({2048}, 3);
458+ rtStream_t stream;
459+ ASSERT_EQ(aclrtCreateStreamWithConfig(&stream, static_cast<uint32_t>(RT_STREAM_PRIORITY_DEFAULT), 0), RT_ERROR_NONE);
460+ auto i3 = FakeValue<uint64_t>(reinterpret_cast<uint64_t>(stream));
461+ 
462+ auto ess = StartExecutorStatistician(model_executor);
463+ ess->Clear();
464+ ExecutorTracerOn executor_tracer_on; // 开启trace以验证内存事件
465+ ASSERT_EQ(model_executor->Execute({i3.value}, inputs.GetTensorList(), inputs.size(),
466+ reinterpret_cast<Tensor **>(outputs.GetAddrList()), outputs.size()),
467+ GRAPH_SUCCESS);
468+ EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType(op_type, "ExecuteCustomOp"), 1);
469+ EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType(op_type, "FreeArgsGuarder"), 1);
470+ EXPECT_EQ(ess->GetExecuteCountByNodeTypeAndKernelType(op_type, "FreeCustomOpWorkspaces"), 1);
471+ EXPECT_TRUE(MemoryTraceChecker(runtime_stub.GetSlogStub(), output_addr)
472+ .AppendExpectEvent(kAllocRe, 0)
473+ .AppendExpectEvent(kFreeRe, 0)
474+ .AsYouWish());
475+ EXPECT_EQ(model_executor->UnLoad(), GRAPH_SUCCESS);
476+ aclrtDestroyStream(stream);
477+}
478+}
479+}
@@ -17,6 +17,7 @@ list(REMOVE_ITEM RUNTIME2_TEST_SRCS
17 17 
18add_executable(ut_fast_runtime2_test18add_executable(ut_fast_runtime2_test
19 "${AIR_CODE_DIR}/tests/depends/graph_tuner/rt2_src/graph_tunner_rt2_stub.cc"19 "${AIR_CODE_DIR}/tests/depends/graph_tuner/rt2_src/graph_tunner_rt2_stub.cc"
20+ "${AIR_CODE_DIR}/tests/graph_metadef/depends/faker/allocator_faker.cc"
20 "${AIR_CODE_DIR}/tests/ge/ut/ge/ffts_plus_proto_tools.cc"21 "${AIR_CODE_DIR}/tests/ge/ut/ge/ffts_plus_proto_tools.cc"
21 "${AIR_CODE_DIR}/tests/ge/ut/ge/test_tools_task_info.cc"22 "${AIR_CODE_DIR}/tests/ge/ut/ge/test_tools_task_info.cc"
22 "${AIR_CODE_DIR}/tests/depends/op_stub/op_impl/autotiling/norm.cc"23 "${AIR_CODE_DIR}/tests/depends/op_stub/op_impl/autotiling/norm.cc"
@@ -90,6 +90,7 @@ TEST_F(CustomNodeConverterUT, custom_op_convert_test) {
90 {"CalcTensorSizeFromStorage", 3},90 {"CalcTensorSizeFromStorage", 3},
91 {"ExecuteCustomOp", 1},91 {"ExecuteCustomOp", 1},
92 {"FreeCustomOpWorkspaces", 1},92 {"FreeCustomOpWorkspaces", 1},
93+ {"FreeArgsGuarder", 1},
93 {"FreeMemory", 4},94 {"FreeMemory", 4},
94 {"SelectL2Allocator", 1},95 {"SelectL2Allocator", 1},
95 {"SplitRtStreams", 1},96 {"SplitRtStreams", 1},
@@ -0,0 +1,198 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include <gtest/gtest.h>
12+#include <gmock/gmock.h>
13+#include <cstring>
14+#include "securec.h"
15+#include "engine/custom/kernel/eager_args_handler.h"
16+#include "graph_metadef/depends/faker/allocator_faker.h"
17+ 
18+namespace gert {
19+namespace {
20+ 
21+class TrackingAllocatorFaker : public AllocatorFaker {
22+ public:
23+ class TrackingGertMemBlockFaker : public GertMemBlockFaker {
24+ public:
25+ explicit TrackingGertMemBlockFaker(void *addr, size_t *free_count)
26+ : GertMemBlockFaker(addr), free_count_(free_count) {}
27+ void Free(int64_t stream_id) override {
28+ if (free_count_ != nullptr) {
29+ ++(*free_count_);
30+ }
31+ GertMemBlockFaker::Free(stream_id);
32+ }
33+ private:
34+ size_t *free_count_;
35+ };
36+ 
37+ TrackingAllocatorFaker() = default;
38+ ~TrackingAllocatorFaker() override {
39+ for (auto *block : blocks_) {
40+ delete block;
41+ }
42+ }
43+ size_t GetFreeCount() const { return free_count_; }
44+ 
45+ GertMemBlock *Malloc(size_t size) override {
46+ void *addr = malloc(size);
47+ if (addr == nullptr) {
48+ return nullptr;
49+ }
50+ auto *block = new TrackingGertMemBlockFaker(addr, &free_count_);
51+ blocks_.push_back(block);
52+ return block;
53+ }
54+ private:
55+ size_t free_count_ = 0;
56+ std::vector<GertMemBlock *> blocks_;
57+};
58+ 
59+class EagerArgsHandlerTest : public ::testing::Test {
60+ protected:
61+ void SetUp() override {
62+ allocator_ = std::make_unique<AllocatorFaker>();
63+ stream_id_ = 123;
64+ }
65+ 
66+ void TearDown() override {
67+ handler_.reset();
68+ allocator_.reset();
69+ }
70+ 
71+ std::unique_ptr<AllocatorFaker> allocator_;
72+ std::unique_ptr<EagerArgsHandler> handler_;
73+ int64_t stream_id_;
74+};
75+ 
76+TEST_F(EagerArgsHandlerTest, MallocReadOnlyDevArgs_Success) {
77+ handler_ = std::make_unique<EagerArgsHandler>(allocator_.get(), stream_id_);
78+ uint8_t host_data[1024] = {};
79+ for (size_t i = 0; i < sizeof(host_data); ++i) {
80+ host_data[i] = static_cast<uint8_t>(i & 0xFF);
81+ }
82+ 
83+ const KernelArgs *result = handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
84+ 
85+ ASSERT_NE(result, nullptr);
86+ EXPECT_NE(result->args_data, nullptr);
87+ EXPECT_EQ(result->args_size, sizeof(host_data));
88+ EXPECT_EQ(result->placement, ge::kPlacementDevice);
89+ EXPECT_EQ(memcmp(result->args_data, host_data, sizeof(host_data)), 0);
90+}
91+ 
92+TEST_F(EagerArgsHandlerTest, MallocReadOnlyDevArgs_MultipleCallsReturnIndependentArgs) {
93+ handler_ = std::make_unique<EagerArgsHandler>(allocator_.get(), stream_id_);
94+ uint8_t host_data1[512] = {};
95+ uint8_t host_data2[256] = {};
96+ memset_s(host_data1, sizeof(host_data1), 0xAA, sizeof(host_data1));
97+ memset_s(host_data2, sizeof(host_data2), 0xBB, sizeof(host_data2));
98+ 
99+ const KernelArgs *result1 = handler_->MallocReadOnlyDevArgs(host_data1, sizeof(host_data1));
100+ const KernelArgs *result2 = handler_->MallocReadOnlyDevArgs(host_data2, sizeof(host_data2));
101+ 
102+ ASSERT_NE(result1, nullptr);
103+ ASSERT_NE(result2, nullptr);
104+ EXPECT_NE(result1->args_data, result2->args_data);
105+ EXPECT_EQ(result1->args_size, sizeof(host_data1));
106+ EXPECT_EQ(result2->args_size, sizeof(host_data2));
107+ EXPECT_EQ(memcmp(result1->args_data, host_data1, sizeof(host_data1)), 0);
108+ EXPECT_EQ(memcmp(result2->args_data, host_data2, sizeof(host_data2)), 0);
109+}
110+ 
111+TEST_F(EagerArgsHandlerTest, Release_ClearsAllAllocatedBlocks) {
112+ auto tracking_allocator = std::make_unique<TrackingAllocatorFaker>();
113+ handler_ = std::make_unique<EagerArgsHandler>(tracking_allocator.get(), stream_id_);
114+ uint8_t host_data[512] = {};
115+ 
116+ handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
117+ handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
118+ 
119+ const auto &args_before = handler_->GetKernelArgs(ge::kPlacementDevice);
120+ EXPECT_EQ(args_before.size(), 2U);
121+ EXPECT_EQ(tracking_allocator->GetFreeCount(), 0U);
122+ 
123+ handler_->Release();
124+ 
125+ const auto &args_after = handler_->GetKernelArgs(ge::kPlacementDevice);
126+ EXPECT_EQ(args_after.size(), 0U);
127+ EXPECT_EQ(tracking_allocator->GetFreeCount(), 2U);
128+}
129+ 
130+TEST_F(EagerArgsHandlerTest, GetKernelArgs_DevicePlacement_ReturnsDeviceArgs) {
131+ handler_ = std::make_unique<EagerArgsHandler>(allocator_.get(), stream_id_);
132+ uint8_t host_data[256] = {};
133+ 
134+ handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
135+ 
136+ const auto &args = handler_->GetKernelArgs(ge::kPlacementDevice);
137+ ASSERT_EQ(args.size(), 1U);
138+ EXPECT_EQ(args[0].placement, ge::kPlacementDevice);
139+ EXPECT_EQ(args[0].args_size, sizeof(host_data));
140+}
141+ 
142+TEST_F(EagerArgsHandlerTest, GetKernelArgs_HostPlacement_ReturnsEmpty) {
143+ handler_ = std::make_unique<EagerArgsHandler>(allocator_.get(), stream_id_);
144+ uint8_t host_data[256] = {};
145+ 
146+ handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
147+ 
148+ const auto &args = handler_->GetKernelArgs(ge::kPlacementHost);
149+ EXPECT_EQ(args.size(), 0U);
150+}
151+ 
152+TEST_F(EagerArgsHandlerTest, Destructor_CallsReleaseWithoutCrash) {
153+ auto handler = std::make_unique<EagerArgsHandler>(allocator_.get(), stream_id_);
154+ uint8_t host_data[256] = {};
155+ handler->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
156+ handler->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
157+ handler.reset();
158+}
159+ 
160+TEST_F(EagerArgsHandlerTest, Release_CanBeCalledMultipleTimes) {
161+ handler_ = std::make_unique<EagerArgsHandler>(allocator_.get(), stream_id_);
162+ uint8_t host_data[256] = {};
163+ handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
164+ 
165+ handler_->Release();
166+ handler_->Release();
167+ 
168+ const auto &args = handler_->GetKernelArgs(ge::kPlacementDevice);
169+ EXPECT_EQ(args.size(), 0U);
170+}
171+ 
172+TEST_F(EagerArgsHandlerTest, MallocReadOnlyDevArgs_AfterRelease) {
173+ handler_ = std::make_unique<EagerArgsHandler>(allocator_.get(), stream_id_);
174+ uint8_t host_data[256] = {};
175+ memset_s(host_data, sizeof(host_data), 0xCC, sizeof(host_data));
176+ 
177+ handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
178+ handler_->Release();
179+ 
180+ const KernelArgs *result = handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
181+ ASSERT_NE(result, nullptr);
182+ EXPECT_EQ(result->args_size, sizeof(host_data));
183+ EXPECT_EQ(result->placement, ge::kPlacementDevice);
184+ 
185+ const auto &args = handler_->GetKernelArgs(ge::kPlacementDevice);
186+ EXPECT_EQ(args.size(), 1U);
187+}
188+ 
189+TEST_F(EagerArgsHandlerTest, MallocReadOnlyDevArgs_WithoutInitialize_ReturnsNullptr) {
190+ handler_ = std::make_unique<EagerArgsHandler>();
191+ uint8_t host_data[256] = {};
192+ 
193+ const KernelArgs *result = handler_->MallocReadOnlyDevArgs(host_data, sizeof(host_data));
194+ EXPECT_EQ(result, nullptr);
195+}
196+ 
197+} // namespace
198+} // namespace gert
@@ -133,22 +133,23 @@ TEST_F(UtestUpdateArgsContext, GetKernelArgs_Host_Success) {
133 inputs.push_back(reinterpret_cast<void *>(0xAAAA0000ULL)); // IR input 0133 inputs.push_back(reinterpret_cast<void *>(0xAAAA0000ULL)); // IR input 0
134 inputs.push_back(nullptr); // additional: allocator134 inputs.push_back(nullptr); // additional: allocator
135 inputs.push_back(nullptr); // additional: stream135 inputs.push_back(nullptr); // additional: stream
136- inputs.push_back(reinterpret_cast<void *>(&handler)); // additional: args_handler
137 136 
138- std::vector<void *> outputs;137+ std::vector<void*> outputs;
139- outputs.push_back(nullptr);138+ outputs.push_back(nullptr); // compute output 0
139+ outputs.push_back(nullptr); // additional: workspace
140+ outputs.push_back(reinterpret_cast<void*>(&handler)); // additional: args_handler
140 141 
141 auto context_holder = KernelRunContextFaker()142 auto context_holder = KernelRunContextFaker()
142- .KernelIONum(4, 1)143+ .KernelIONum(3, 3)
143- .NodeIoNum(1, 1)144+ .NodeIoNum(1, 1)
144- .IrInputNum(1)145+ .IrInputNum(1)
145- .IrOutputNum(0)146+ .IrOutputNum(0)
146- .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_NCHW, ge::FORMAT_NCHW)147+ .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_NCHW, ge::FORMAT_NCHW)
147- .Inputs(inputs)148+ .Inputs(inputs)
148- .Outputs(outputs)149+ .Outputs(outputs)
149- .Build();150+ .Build();
150 151 
151- auto *ctx = context_holder.template GetContext<UpdateArgsContext>();152+ auto* ctx = context_holder.template GetContext<UpdateArgsContext>();
152 ASSERT_NE(ctx, nullptr);153 ASSERT_NE(ctx, nullptr);
153 154 
154 const auto *args = ctx->GetKernelArgs(Placement::kPlacementHost, 0U);155 const auto *args = ctx->GetKernelArgs(Placement::kPlacementHost, 0U);
@@ -171,22 +172,23 @@ TEST_F(UtestUpdateArgsContext, GetKernelArgs_Device_Success) {
171 inputs.push_back(reinterpret_cast<void *>(0xAAAA0000ULL)); // IR input 0172 inputs.push_back(reinterpret_cast<void *>(0xAAAA0000ULL)); // IR input 0
172 inputs.push_back(nullptr); // additional: allocator173 inputs.push_back(nullptr); // additional: allocator
173 inputs.push_back(nullptr); // additional: stream174 inputs.push_back(nullptr); // additional: stream
174- inputs.push_back(reinterpret_cast<void *>(&handler)); // additional: args_handler
175 175 
176- std::vector<void *> outputs;176+ std::vector<void*> outputs;
177- outputs.push_back(nullptr);177+ outputs.push_back(nullptr); // compute output 0
178+ outputs.push_back(nullptr); // additional: workspace
179+ outputs.push_back(reinterpret_cast<void*>(&handler)); // additional: args_handler
178 180 
179 auto context_holder = KernelRunContextFaker()181 auto context_holder = KernelRunContextFaker()
180- .KernelIONum(4, 1)182+ .KernelIONum(3, 3)
181- .NodeIoNum(1, 1)183+ .NodeIoNum(1, 1)
182- .IrInputNum(1)184+ .IrInputNum(1)
183- .IrOutputNum(0)185+ .IrOutputNum(0)
184- .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_NCHW, ge::FORMAT_NCHW)186+ .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_NCHW, ge::FORMAT_NCHW)
185- .Inputs(inputs)187+ .Inputs(inputs)
186- .Outputs(outputs)188+ .Outputs(outputs)
187- .Build();189+ .Build();
188 190 
189- auto *ctx = context_holder.template GetContext<UpdateArgsContext>();191+ auto* ctx = context_holder.template GetContext<UpdateArgsContext>();
190 ASSERT_NE(ctx, nullptr);192 ASSERT_NE(ctx, nullptr);
191 193 
192 const auto *args = ctx->GetKernelArgs(Placement::kPlacementDevice, 0U);194 const auto *args = ctx->GetKernelArgs(Placement::kPlacementDevice, 0U);
@@ -210,26 +212,27 @@ TEST_F(UtestUpdateArgsContext, GetKernelArgs_IndexOutOfRange_ReturnsNullptr) {
210 host_args[0].placement = Placement::kPlacementHost;212 host_args[0].placement = Placement::kPlacementHost;
211 handler.SetHostArgs(host_args);213 handler.SetHostArgs(host_args);
212 214 
213- std::vector<void *> inputs;215+ std::vector<void*> inputs;
214- inputs.push_back(reinterpret_cast<void *>(0xAAAA0000ULL));216+ inputs.push_back(reinterpret_cast<void*>(0xAAAA0000ULL)); // IR input 0
215- inputs.push_back(nullptr);217+ inputs.push_back(nullptr); // additional: allocator
216- inputs.push_back(nullptr);218+ inputs.push_back(nullptr); // additional: stream
217- inputs.push_back(reinterpret_cast<void *>(&handler));
218 219 
219- std::vector<void *> outputs;220+ std::vector<void*> outputs;
220- outputs.push_back(nullptr);221+ outputs.push_back(nullptr); // compute output 0
222+ outputs.push_back(nullptr); // additional: workspace
223+ outputs.push_back(reinterpret_cast<void*>(&handler)); // additional: args_handler
221 224 
222 auto context_holder = KernelRunContextFaker()225 auto context_holder = KernelRunContextFaker()
223- .KernelIONum(4, 1)226+ .KernelIONum(3, 3)
224- .NodeIoNum(1, 1)227+ .NodeIoNum(1, 1)
225- .IrInputNum(1)228+ .IrInputNum(1)
226- .IrOutputNum(0)229+ .IrOutputNum(0)
227- .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_NCHW, ge::FORMAT_NCHW)230+ .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_NCHW, ge::FORMAT_NCHW)
228- .Inputs(inputs)231+ .Inputs(inputs)
229- .Outputs(outputs)232+ .Outputs(outputs)
230- .Build();233+ .Build();
231 234 
232- auto *ctx = context_holder.template GetContext<UpdateArgsContext>();235+ auto* ctx = context_holder.template GetContext<UpdateArgsContext>();
233 ASSERT_NE(ctx, nullptr);236 ASSERT_NE(ctx, nullptr);
234 237 
235 const auto *args = ctx->GetKernelArgs(Placement::kPlacementHost, 100U);238 const auto *args = ctx->GetKernelArgs(Placement::kPlacementHost, 100U);
@@ -237,26 +240,27 @@ TEST_F(UtestUpdateArgsContext, GetKernelArgs_IndexOutOfRange_ReturnsNullptr) {
237}240}
238 241 
239TEST_F(UtestUpdateArgsContext, GetKernelArgs_NullHandlerInContext_ReturnsNullptr) {242TEST_F(UtestUpdateArgsContext, GetKernelArgs_NullHandlerInContext_ReturnsNullptr) {
240- std::vector<void *> inputs;243+ std::vector<void*> inputs;
241- inputs.push_back(reinterpret_cast<void *>(0xAAAA0000ULL));244+ inputs.push_back(reinterpret_cast<void*>(0xAAAA0000ULL)); // IR input 0
242- inputs.push_back(nullptr);245+ inputs.push_back(nullptr); // additional: allocator
243- inputs.push_back(nullptr);246+ inputs.push_back(nullptr); // additional: stream
244- inputs.push_back(nullptr);
245 247 
246- std::vector<void *> outputs;248+ std::vector<void*> outputs;
247- outputs.push_back(nullptr);249+ outputs.push_back(nullptr); // compute output 0
250+ outputs.push_back(nullptr); // additional: workspace
251+ outputs.push_back(nullptr); // additional: args_handler (null)
248 252 
249 auto context_holder = KernelRunContextFaker()253 auto context_holder = KernelRunContextFaker()
250- .KernelIONum(4, 1)254+ .KernelIONum(3, 3)
251- .NodeIoNum(1, 1)255+ .NodeIoNum(1, 1)
252- .IrInputNum(1)256+ .IrInputNum(1)
253- .IrOutputNum(0)257+ .IrOutputNum(0)
254- .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_NCHW, ge::FORMAT_NCHW)258+ .NodeInputTd(0, ge::DT_FLOAT16, ge::FORMAT_NCHW, ge::FORMAT_NCHW)
255- .Inputs(inputs)259+ .Inputs(inputs)
256- .Outputs(outputs)260+ .Outputs(outputs)
257- .Build();261+ .Build();
258 262 
259- auto *ctx = context_holder.template GetContext<UpdateArgsContext>();263+ auto* ctx = context_holder.template GetContext<UpdateArgsContext>();
260 ASSERT_NE(ctx, nullptr);264 ASSERT_NE(ctx, nullptr);
261 265 
262 const auto *args = ctx->GetKernelArgs(Placement::kPlacementHost, 0U);266 const auto *args = ctx->GetKernelArgs(Placement::kPlacementHost, 0U);