Line data Source code
1 : /**
2 : * Copyright (c) 2025 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 "graph/build/memory/block_mem_assigner.h"
12 : #include <cinttypes>
13 : #include <algorithm>
14 : #include <sstream>
15 : #include <stack>
16 :
17 : #include "graph/ge_context.h"
18 : #include "graph/utils/graph_utils.h"
19 : #include "graph/utils/node_utils.h"
20 : #include "graph/utils/op_desc_utils.h"
21 : #include "graph/utils/tensor_utils.h"
22 : #include "graph/utils/type_utils.h"
23 : #include "graph/utils/op_type_utils.h"
24 : #include "graph/debug/ge_attr_define.h"
25 : #include "graph/build/memory/var_mem_assign_util.h"
26 : #include "graph/optimize/mem_layout_conflict_optimize/mem_layout_conflict_util.h"
27 : #include "common/context/local_context.h"
28 : #include "common/math/ge_math_util.h"
29 : #include "common/checker.h"
30 : #include "common/memory/mem_type_utils.h"
31 : #include "framework/common/op/ge_op_utils.h"
32 : #include "graph/custom_op_factory.h"
33 : #include "graph/optimize/params.h"
34 : #include "framework/common/runtime_tensor_desc.h"
35 : #include "graph/build/memory/dynamic_batch_mem_assigner.h"
36 : #include "runtime/subscriber/global_profiler.h"
37 : #include "common/ge_common/ge_types.h"
38 : #include "memory_block.h"
39 : #include "block_mem_stream.h"
40 : #include "block_mem_zero_copy.h"
41 : #include "mem_reuse_strategy.h"
42 :
43 : namespace {
44 : const char *const kAttrNameWorkspaceReuseFlag = "workspace_reuse_flag";
45 : const char *const kL2FusionDynamicConvergeOp = "l2fusion_dynamic_converge_op";
46 : const char *const kOpNoReuseMem = "no_reuse_mem_flag";
47 : const std::string kOffline = "offline";
48 : const int32_t kReuseMaxOpNum = 10;
49 : const int32_t kReuseMaxCharNum = 2000;
50 :
51 : std::string FormatStreamEdgeName(const char *src_name, const char *dst_name) {
52 : if ((src_name != nullptr) && (dst_name != nullptr)) {
53 : return "[" + std::string(dst_name) + "<-" + std::string(src_name) + "] ";
54 : }
55 : return "";
56 : }
57 :
58 : int64_t GetStreamId(const ge::OpDesc *const desc) {
59 : return ge::MemReuseUtils::GetStreamId(desc);
60 : }
61 :
62 : std::string GetStreamIdDesc(const ge::OpDesc *const desc) {
63 : std::string stream_id_str;
64 : if (desc != nullptr) {
65 : const int64_t stream_id = GetStreamId(desc);
66 : stream_id_str = std::to_string(stream_id);
67 : if (stream_id != desc->GetStreamId()) {
68 : stream_id_str.append("--");
69 : stream_id_str.append(std::to_string(desc->GetStreamId()));
70 : }
71 : }
72 : return stream_id_str;
73 : }
74 :
75 : bool NotMatchNoReuseType(const std::set<std::string> &no_reuse_types, const std::string &type) {
76 : // Match BaseType, BaseTypeV1~BaseTypeV4
77 : const auto type_length = type.length();
78 : const std::string::size_type version_length = 2U;
79 : if ((type_length > version_length) && ((type.at(type_length - version_length) == 'V')) &&
80 : (type.at(type_length - 1U) >= '1') && (type.at(type_length - 1U) <= '4')) {
81 : return (no_reuse_types.count(type.substr(0, type_length - version_length)) == 0UL);
82 : }
83 : return (no_reuse_types.count(type) == 0UL);
84 : }
85 :
86 : } // namespace
87 : namespace ge {
88 : // Memory size is fixed and has nothing to do with different batches.
89 : bool SizeIndependentOfBatch(const std::string &node_type) {
90 : static const std::unordered_set<std::string> kSizeIndependentOps = {
91 : HCOMBROADCAST, HVDCALLBACKBROADCAST, HCOMALLREDUCE, HVDCALLBACKALLREDUCE, HCOMALLGATHER, HVDCALLBACKALLGATHER};
92 : return (kSizeIndependentOps.count(node_type) != 0UL);
93 : }
94 :
95 : void CheckAndGetOpReuseEnv(const std::string &env, std::unordered_set<std::string> &env_set, bool &op_reuse_env_valid) {
96 : std::string env_str = std::string(env);
97 : if (env_str.size() > kReuseMaxCharNum) {
98 : GELOGE(FAILED, "[Check][Param] The OP_NO_REUSE_MEM has more than %d characters.", kReuseMaxCharNum);
99 : return;
100 : }
101 :
102 : std::vector<std::string> env_vec;
103 : SplitStringByComma(env_str, env_vec);
104 : if (env_vec.size() > kReuseMaxOpNum) {
105 : GELOGE(FAILED, "[Check][Param] The OP_NO_REUSE_MEM has more than %d nodes.", kReuseMaxOpNum);
106 : return;
107 : }
108 :
109 : for (const auto &item : env_vec) {
110 : env_set.insert(item);
111 : }
112 : op_reuse_env_valid = true;
113 : return;
114 : }
115 :
116 : bool CheckIsZeroMemNodeType(const std::string &node_type) {
117 : return (node_type == VARIABLE) || (node_type == CONSTANT) || (node_type == MULTISHAPE) || (node_type == CONSTANTOP) ||
118 : (node_type == HVDWAIT) || (node_type == FILECONSTANT) || (node_type == CONSTPLACEHOLDER);
119 : }
120 :
121 : /*
122 : * 直连或经过RefNode间接连接连续输入节点的,有些情况只给第0个输入分配内存,大小是所有输入的总大小,其他输入不分配内存。
123 : * 1. NoPadding连续输入只给第0个输入分配内存
124 : * 2. 带Padding连续输入,一般情况下每个输入都分配内存,以下两个特殊情况下,只给第0个输入分配内存
125 : * 2.1 输入上带有lx fusion(ATTR_NAME_OUTPUT_OFFSET_FOR_BUFFER_FUSION)属性的
126 : * 2.2 连续输入需要对输入做单独清零的(有need_gentask_atomic/ATOMIC_ATTR_INPUT_INDEX属性)
127 : * 3. NoPadding连续输入级联场景,只给最后一个PhonyConcat的第0个输入分配内存
128 : * 4. 既作为第0个输入,又作为其他输入的,需要分配内存。
129 : */
130 : Status GetNoNeedAssignMemoryFlag(const NodePtr &n, uint32_t out_index, bool &no_need_assign_memory) {
131 : no_need_assign_memory = false;
132 : auto node_desc = n->GetOpDescBarePtr();
133 : GE_ASSERT_NOTNULL(node_desc);
134 : auto out_anchor = n->GetOutDataAnchor(out_index);
135 : GE_ASSERT_NOTNULL(out_anchor);
136 : std::vector<int64_t> offsets_for_fusion = {};
137 : const auto has_lx_fusion_attr =
138 : AttrUtils::GetListInt(node_desc, ATTR_NAME_OUTPUT_OFFSET_FOR_BUFFER_FUSION, offsets_for_fusion);
139 :
140 : for (auto const peer_in_anchor : out_anchor->GetPeerInDataAnchorsPtr()) {
141 : InDataAnchor *new_peer_in_anchor = nullptr;
142 : const auto nopadding_continuous =
143 : MemLayoutConflictUtil::IsContinuousInputThroughRefNode(peer_in_anchor, true, new_peer_in_anchor);
144 : bool continuous = false;
145 : if (!nopadding_continuous) {
146 : continuous = MemLayoutConflictUtil::IsContinuousInputThroughRefNode(peer_in_anchor, false, new_peer_in_anchor);
147 : }
148 : if (!(continuous || nopadding_continuous)) {
149 : continue; // peer node does not need continuous memory
150 : }
151 : /*
152 : * 判断输出节点是不是连续内存节点,不能只看直连的输出,而且也要看经过RefNode连接的节点
153 : * new_peer不一定是n的直连输出,也可能是n经过一个或多个RefNod连接的输出节点
154 : */
155 : GE_ASSERT_NOTNULL(new_peer_in_anchor);
156 : const auto continuous_node = new_peer_in_anchor->GetOwnerNodeBarePtr();
157 : GE_ASSERT_NOTNULL(continuous_node);
158 :
159 : if (new_peer_in_anchor->GetIdx() == 0) {
160 : no_need_assign_memory = false;
161 : break;
162 : }
163 : if (continuous) {
164 : // lx_fusion memory only assign first input, broadcast's input some are variable some are not, reassign later
165 : // In CleanSeparately policy, padding continuous input only allocate index 0 input
166 : if (!(has_lx_fusion_attr || MemReuseUtils::IsSeparateCleanContinuousInputNode(continuous_node))) {
167 : continue;
168 : }
169 : }
170 : no_need_assign_memory = true;
171 : GELOGI(
172 : "%s name[%s] output[%u] peer[%s] input[%d] need continuous, input size[%u], nopadding_continuous[%d], "
173 : "continuous[%d], has_lx_fusion_attr[%d]",
174 : n->GetOwnerComputeGraphBarePtr()->GetName().c_str(), n->GetNamePtr(), out_index, continuous_node->GetNamePtr(),
175 : new_peer_in_anchor->GetIdx(), continuous_node->GetAllInDataAnchorsSize(), nopadding_continuous, continuous,
176 : has_lx_fusion_attr);
177 : }
178 : GELOGI("%s name[%s] output[%u] no_need_assign_memory:%d.", n->GetOwnerComputeGraphBarePtr()->GetName().c_str(),
179 : n->GetNamePtr(), out_index, no_need_assign_memory);
180 : return SUCCESS;
181 : }
182 :
183 : uint64_t GetWorkSpaceMemoryType(const size_t no_reuse_scope_size, const size_t index, const bool is_p2p_memory,
184 : const bool session_scope_memory, std::vector<bool> &workspace_reuse_flag) {
185 : if (is_p2p_memory) {
186 : return RT_MEMORY_P2P_DDR;
187 : }
188 :
189 : if (session_scope_memory) {
190 : if (workspace_reuse_flag.empty()) {
191 : workspace_reuse_flag.assign(no_reuse_scope_size, true);
192 : }
193 : workspace_reuse_flag[index] = false;
194 : return kSessionScopeMemory | RT_MEMORY_HBM;
195 : }
196 :
197 : return RT_MEMORY_HBM;
198 : }
199 :
200 : BlockMemAssigner::BlockMemAssigner(const MemAssistInfo &mem_assist_info)
201 : : compute_graph_(mem_assist_info.compute_graph),
202 : symbol_to_anchors_(mem_assist_info.symbol_to_anchors),
203 : anchor_to_symbol_(mem_assist_info.anchor_to_symbol),
204 : life_time_(0),
205 : parent_nodes_to_stream_ids_(mem_assist_info.parent_nodes_to_stream_ids) {
206 : std::string memory_optimization_policy;
207 : ge::GetContext().GetOption(MEMORY_OPTIMIZATION_POLICY, memory_optimization_policy);
208 : if (memory_optimization_policy == kMemoryPriority) {
209 : memory_priority_mode_ = true;
210 : }
211 :
212 : (void)InitIoReuseFlag();
213 : ParseGraphIoAllocMode();
214 :
215 : std::string refreshable;
216 : (void)ge::GetContext().GetOption(ge::OPTION_FEATURE_BASE_REFRESHABLE, refreshable);
217 : is_feature_map_refreshable_ = (refreshable == "1");
218 :
219 : input_fusion_size_ = ge::GetContext().GetInputFusionSize();
220 : GELOGI("feature map refreshable: %d, input_fusion_size: %" PRIu64, is_feature_map_refreshable_, input_fusion_size_);
221 : }
222 :
223 : BlockMemAssigner::~BlockMemAssigner() {
224 : GELOGD("[Destruct][BlockMemAssigner]blocks_store_ size : %lu", blocks_store_.size());
225 : for (MemoryBlock *memory_block : blocks_store_) {
226 : GE_DELETE_NEW_SINGLE(memory_block);
227 : }
228 : }
229 :
230 : void BlockMemAssigner::InsertStreamOutEdge() {
231 : for (const auto &dst_stream_to_edges : in_stream_edges_) {
232 : const auto dst_stream_id = dst_stream_to_edges.first;
233 : for (const auto &src_stream_to_edges : dst_stream_to_edges.second) {
234 : const auto src_stream_id = src_stream_to_edges.first;
235 : auto &out_stream_edge_set = out_stream_edges_[src_stream_id][dst_stream_id];
236 : for (const auto &edge : src_stream_to_edges.second) {
237 : out_stream_edge_set.insert({edge.peer_node_id, edge.node_id});
238 : GELOGI("[StreamEdge]Out depend Node: stream_id:[%" PRId64 "->%" PRId64 "] life_time:[%zu->%zu], only insert.",
239 : src_stream_id, dst_stream_id, edge.peer_node_id, edge.node_id);
240 : }
241 : }
242 : }
243 : }
244 :
245 : /*
246 : * stream1 stream2
247 : * 1------+
248 : * |
249 : * 2 ---- 3
250 : * |
251 : * +------4
252 : * 对于stream2 的入边来讲
253 : * stream2<-stream1的入边 in_edge 3<-1 删掉
254 : * 3<-2 保留
255 : * 可以简单记为:id差越小越好
256 : */
257 : void BlockMemAssigner::InsertStreamInEdge(std::set<EdgeLife, CompareEdgeLife> &in_edge_set, const EdgeLife &new_in_edge,
258 : const int64_t src_stream_id, const int64_t dst_stream_id,
259 : const std::pair<const char *, const char *> &node_names) {
260 : const auto old_in_edge_iter = in_edge_set.find(new_in_edge);
261 : if (old_in_edge_iter != in_edge_set.end()) {
262 : if (old_in_edge_iter->peer_node_id < new_in_edge.peer_node_id) {
263 : const auto old_peer_node_id = old_in_edge_iter->peer_node_id;
264 : in_edge_set.erase(old_in_edge_iter); // after erase, cannot use old_peer_node_id below
265 : in_edge_set.insert(new_in_edge);
266 : GELOGI("[StreamEdge]In depend Node: %sstream_id:[%" PRId64 "<-%" PRId64
267 : "] life_time:[%zu<-%zu], erase and insert,"
268 : " old_peer_node_id[%zu].",
269 : FormatStreamEdgeName(node_names.first, node_names.second).c_str(), dst_stream_id, src_stream_id,
270 : new_in_edge.node_id, new_in_edge.peer_node_id, old_peer_node_id);
271 : } else {
272 : GELOGI("[StreamEdge]In depend Node: %sstream_id:[%" PRId64 "<-%" PRId64
273 : "] life_time:[%zu<-%zu], not erase,"
274 : " not insert, old_peer_node_id[%zu] >= new_peer_node_id[%zu].",
275 : FormatStreamEdgeName(node_names.first, node_names.second).c_str(), dst_stream_id, src_stream_id,
276 : new_in_edge.node_id, new_in_edge.peer_node_id, old_in_edge_iter->peer_node_id, new_in_edge.peer_node_id);
277 : }
278 : } else {
279 : in_edge_set.insert(new_in_edge);
280 : GELOGI("[StreamEdge]In depend Node: %sstream_id:[%" PRId64 "<-%" PRId64 "] life_time:[%zu<-%zu], only insert.",
281 : FormatStreamEdgeName(node_names.first, node_names.second).c_str(), dst_stream_id, src_stream_id,
282 : new_in_edge.node_id, new_in_edge.peer_node_id);
283 : }
284 : }
285 :
286 : /*
287 : * 函数作用:
288 : * 该函数用于建立跨流的边,比如已有stream1->stream2->stream3, 建立stream1->stream3的边
289 : * 主要逻辑:在建立完stream2->stream3的边后,遍历所有到stream2的流(比如stream1),并建立该流到stream3的边。
290 : *
291 : * in_stream_edges : 入边
292 : * out_stream_edges : 出边
293 : * node_desc: 当前节点
294 : * in_node_desc: 输入节点
295 : *
296 : * 返回值:
297 : * 无返回值,函数直接修改传入的in_stream_edges和out_stream_edges。
298 : *
299 : * in edge: stream_id:[2<-1] life_time:[1<-0]
300 : * in edge: stream_id:[3<-2] life_time:[3<-1]
301 : * in edge: stream_id:[3<-1] life_time:[3<-0] new edge
302 : * in edge: stream_id:[3<-0] life_time:[3<-2]
303 : * in edge: stream_id:[1<-3] life_time:[4<-3]
304 : * in edge: stream_id:[1<-0] life_time:[4<-2] new edge
305 : * in edge: stream_id:[1<-1] life_time:[4<-0] new edge
306 : * in edge: stream_id:[1<-2] life_time:[4<-1] new edge
307 : */
308 : void BlockMemAssigner::AddInStreamEdge(const ge::OpDesc *const node_desc, const ge::OpDesc *const in_node_desc) {
309 : const auto stream_id = GetStreamId(node_desc);
310 : const auto node_id = static_cast<size_t>(node_desc->GetId());
311 : const auto in_stream_id = GetStreamId(in_node_desc);
312 : const auto in_node_id = static_cast<size_t>(in_node_desc->GetId());
313 : for (const auto &stream_to_in_edges : in_stream_edges_[in_stream_id]) {
314 : const auto &in_edges = stream_to_in_edges.second;
315 : const auto third_stream_id = stream_to_in_edges.first;
316 : if (in_edges.empty() || (stream_id == third_stream_id)) {
317 : continue;
318 : }
319 :
320 : /*
321 : * 这段逻辑是目的是找到一条边,其peer_node_id作为stream_id<-third_stream_id的peer_node_id
322 : * 比如stream_id=3, in_stream_id=2, node_id=5, in_node_id=4, third_stream_id=1
323 : * in edge: stream_id:[3<-2] life_time:[5<-4]
324 : *
325 : * in edge: stream_id:[2<-1] life_time:[1<-0]
326 : * in edge: stream_id:[2<-1] life_time:[4<-2] <---edge_it 小于等于4的,最大的, 2就作为new_in_edge.peer_node_id
327 : * in edge: stream_id:[2<-1] life_time:[7<-3]
328 : */
329 : auto edge_it = in_edges.lower_bound({in_node_id, 0UL}); // 0UL不参与比较
330 : if ((edge_it == in_edges.end()) || ((*edge_it).node_id > in_node_id)) {
331 : // only one data
332 : if (edge_it == in_edges.begin()) {
333 : continue;
334 : }
335 : --edge_it;
336 : }
337 :
338 : // 要给in edge: stream_id:[stream_id<-third_stream_id] 建立新的边
339 : const EdgeLife new_in_edge{node_id, (*edge_it).peer_node_id};
340 : auto &in_edge_set = in_stream_edges_[stream_id][third_stream_id];
341 : const auto old_edge_it = in_edge_set.lower_bound({node_id, 0UL}); // 0UL不参与比较
342 : // 删除冗余交叉边,缩短node_id与peer_node_id的距离
343 : if (old_edge_it != in_edge_set.end()) {
344 : EraseIntersectedEdge(in_edge_set, *old_edge_it, new_in_edge, third_stream_id, stream_id);
345 : }
346 : InsertStreamInEdge(in_edge_set, new_in_edge, third_stream_id, stream_id);
347 : }
348 : }
349 :
350 : /// Data
351 : /// |----------
352 : /// | |
353 : /// D stream 0 E stream 1
354 : /// Data不是实际执行节点,产生stream 1->stream 0的依赖会导致错误结果,因此ge local类型不处理
355 : void BlockMemAssigner::GetDiffStreamEdgeLife(const NodePtr &node, const std::set<int64_t> &exclude_merge_streams) {
356 : auto node_desc = node->GetOpDescBarePtr();
357 : GE_CHECK_NOTNULL_JUST_RETURN(node_desc);
358 : if (NodeUtils::IsLikeAtomicClean(node) || (node_desc->GetOpKernelLibName() == kEngineNameGeLocal)) {
359 : return;
360 : }
361 : const auto stream_id = GetStreamId(node_desc);
362 : for (const auto &out_anchor : node->GetAllOutAnchors()) {
363 : GE_CHECK_NOTNULL_JUST_RETURN(out_anchor);
364 : for (auto const peer_in_anchor : out_anchor->GetPeerAnchorsPtr()) {
365 : GE_CHECK_NOTNULL_JUST_RETURN(peer_in_anchor);
366 : const auto peer_node = peer_in_anchor->GetOwnerNodeBarePtr();
367 : GE_CHECK_NOTNULL_JUST_RETURN(peer_node);
368 : const auto peer_in_node_desc = peer_node->GetOpDescBarePtr();
369 : GE_CHECK_NOTNULL_JUST_RETURN(peer_in_node_desc);
370 : const auto peer_in_stream_id = GetStreamId(peer_in_node_desc);
371 : if (stream_id == peer_in_stream_id) {
372 : continue;
373 : }
374 :
375 : if (exclude_merge_streams.find(stream_id) != exclude_merge_streams.cend() ||
376 : exclude_merge_streams.find(peer_in_stream_id) != exclude_merge_streams.cend()) {
377 : GELOGI("Stream [%" PRId64 "->%" PRId64 "] will interrupt memory reuse among streams", stream_id,
378 : peer_in_stream_id);
379 : continue;
380 : }
381 :
382 : const auto node_id = static_cast<size_t>(node_desc->GetId());
383 : const auto peer_node_id = static_cast<size_t>(peer_in_node_desc->GetId());
384 : const EdgeLife new_in_edge{peer_node_id, node_id}; // 从peer_node看,由node连接进来的边称为入边
385 : auto &in_edge_set = in_stream_edges_[peer_in_stream_id][stream_id];
386 : InsertStreamInEdge(in_edge_set, new_in_edge, stream_id, peer_in_stream_id,
387 : {node_desc->GetNamePtr(), peer_in_node_desc->GetNamePtr()});
388 : AddInStreamEdge(peer_in_node_desc, node_desc);
389 : }
390 : }
391 : }
392 :
393 : /// a stream:1
394 : /// / |
395 : /// b stream:0 c stream:1
396 : /// \ |
397 : /// d stream:1
398 : /// b can be reused as stream 1
399 : void BlockMemAssigner::OptimizeStreamIdForMemoryReuse(const NodePtr &node) {
400 : SetRealStreamIdForDataNode(node.get());
401 : MemReuseStrategy::OptimizeDiffStream(node.get());
402 : }
403 :
404 : void BlockMemAssigner::SetRealStreamIdForDataNode(const Node *const node) {
405 : auto node_op_desc = node->GetOpDescBarePtr();
406 : if (node_op_desc == nullptr) {
407 : return;
408 : }
409 : // data use out put node's stream,
410 : if (OpTypeUtils::IsDataNode(node->GetType()) && (GetStreamId(node->GetOpDescBarePtr()) == kInvalidStreamId)) {
411 : const auto &out_anchor = node->GetOutDataAnchor(0U);
412 : if (out_anchor != nullptr) {
413 : std::set<int64_t> peer_streams;
414 : for (auto const peer_in_anchor : out_anchor->GetPeerInDataAnchorsPtr()) {
415 : if ((peer_in_anchor == nullptr) || (peer_in_anchor->GetOwnerNodeBarePtr() == nullptr)) {
416 : continue;
417 : }
418 : peer_streams.insert(GetStreamId(peer_in_anchor->GetOwnerNodeBarePtr()->GetOpDescBarePtr()));
419 : if (peer_streams.size() > 1U) {
420 : break;
421 : }
422 : }
423 : // data输出都是相同stream才做处理
424 : if (peer_streams.size() == 1U) {
425 : MemReuseUtils::SetStreamId(node_op_desc, *peer_streams.begin());
426 : }
427 : }
428 : }
429 : }
430 :
431 : // 对于父节点,stream应该使用对应真实节点的(子图内netoutput输入节点),如果有多个真实节点并且stream不同,则设置为不可复用
432 : Status BlockMemAssigner::SetRealStreamIdForParentNode(MemAssistInfo &mem_assist_info) {
433 : const auto compute_graph = mem_assist_info.compute_graph;
434 : auto &parent_nodes_to_stream_ids = mem_assist_info.parent_nodes_to_stream_ids;
435 : const auto root_graph = GraphUtils::FindRootGraph(compute_graph);
436 : GE_ASSERT_NOTNULL(root_graph);
437 : // 父节点可能有多个输出,每个输出对应子图内netoutput的一个输入节点的输出,vector保存的是这些输入节点的stream id
438 : std::map<int64_t, const Node *> ids_to_parent_node;
439 : for (const NodePtr &n : compute_graph->GetAllNodes()) {
440 : const auto op_desc = n->GetOpDescBarePtr();
441 : GE_ASSERT_NOTNULL(op_desc);
442 : if (op_desc->GetSubgraphInstanceNames().empty()) {
443 : continue;
444 : }
445 : parent_nodes_to_stream_ids[n.get()].resize(op_desc->GetOutputsSize(), kParentNodeDefaultStreamId);
446 : ids_to_parent_node[op_desc->GetId()] = n.get();
447 : }
448 :
449 : // 先处理topoid最大的,对于子图嵌套场景,保证了先处理最内层的
450 : for (auto iter = ids_to_parent_node.rbegin(); iter != ids_to_parent_node.rend(); ++iter) {
451 : const auto parent_node = iter->second;
452 : for (const auto &subgraph_name : parent_node->GetOpDescBarePtr()->GetSubgraphInstanceNames()) {
453 : const auto sub_graph = root_graph->GetSubgraph(subgraph_name);
454 : if (sub_graph == nullptr) {
455 : continue;
456 : }
457 : const auto netoutput = sub_graph->FindFirstNodeMatchType(NETOUTPUT);
458 : GE_ASSERT_NOTNULL(netoutput);
459 : GE_ASSERT_NOTNULL(netoutput->GetOpDesc());
460 : GE_ASSERT_SUCCESS(GetNetoutputInNodeStream(netoutput.get(), parent_node, parent_nodes_to_stream_ids));
461 : }
462 : }
463 : return SUCCESS;
464 : }
465 :
466 : // 对于父节点,stream应该使用对应真实节点的(子图内netoutput输入节点),如果有多个真实节点并且stream不同,则设置为不可复用
467 : Status BlockMemAssigner::GetRealStreamIdForParentNode(const NodePtr &node, const uint32_t out_index, int64_t &stream_id,
468 : bool &is_reuse) const {
469 : is_reuse = true;
470 : const auto iter = parent_nodes_to_stream_ids_.find(node.get());
471 : if ((iter == parent_nodes_to_stream_ids_.end()) || (out_index >= iter->second.size()) ||
472 : (iter->second.at(out_index) == kParentNodeDefaultStreamId)) {
473 : return SUCCESS;
474 : }
475 : if (iter->second.at(out_index) == kInvalidStreamId) {
476 : is_reuse = false;
477 : GELOGI("node %s(%s) out_index: %u has multi streams, set no reuse", node->GetNamePtr(), node->GetTypePtr(),
478 : out_index);
479 : return SUCCESS;
480 : }
481 : stream_id = iter->second.at(out_index);
482 : GELOGI("node %s(%s) out_index: %u get stream %lld", node->GetNamePtr(), node->GetTypePtr(), out_index, stream_id);
483 : return SUCCESS;
484 : }
485 :
486 : /*
487 : * 不能改为非static的,不能修改成员变量,因为在HybridMemAssigner中,
488 : * 只有一个binary_assigner对象会调用该函数,其他开启多线程创建的assigner对象并没有调用这个接口
489 : */
490 : Status BlockMemAssigner::PreparationForAssign(MemAssistInfo &mem_assist_info) {
491 : for (const NodePtr &n : mem_assist_info.compute_graph->GetAllNodes()) {
492 : OptimizeStreamIdForMemoryReuse(n);
493 : }
494 : // call after PreparationForAssign
495 : GE_ASSERT_SUCCESS(BlockMemAssigner::SetRealStreamIdForParentNode(mem_assist_info));
496 : return SUCCESS;
497 : }
498 :
499 : Status BlockMemAssigner::GetOutAndWorkSpaceMem(std::vector<int64_t> &all_memory_size) {
500 : std::vector<int64_t> temp;
501 : std::map<std::string, std::vector<int64_t>> batch_all_memory_size;
502 : std::map<std::string, int64_t> batch_total_size;
503 : std::set<int64_t> exclude_merge_streams = GetStreamMergeAndOutStreams(compute_graph_);
504 : for (const NodePtr &n : compute_graph_->GetAllNodes()) {
505 : GetDiffStreamEdgeLife(n, exclude_merge_streams);
506 : GetContinuousNodeLifeTimeBegin(n.get(), n.get(), 0, 0U);
507 :
508 : auto node_op_desc = n->GetOpDescBarePtr();
509 : GE_ASSERT_NOTNULL(node_op_desc);
510 :
511 : if (CheckIsZeroMemNodeType(node_op_desc->GetTypePtr())) {
512 : continue;
513 : }
514 :
515 : std::string batch_label;
516 : (void)ge::AttrUtils::GetStr(node_op_desc, ATTR_NAME_BATCH_LABEL, batch_label);
517 :
518 : if (NodeUtils::IsLikeAtomicClean(n)) {
519 : atomic_addr_clean_id_ = node_op_desc->GetId();
520 : }
521 :
522 : for (auto out_anchor : n->GetAllOutDataAnchorsPtr()) {
523 : auto output_desc = node_op_desc->MutableOutputDesc(out_anchor->GetIdx());
524 : if (output_desc == nullptr) {
525 : continue;
526 : }
527 : int64_t size = 0;
528 : (void)MemReuseUtils::GetTensorSize(*output_desc, size, MemReuseUtils::IsNeedSplitSize(n, out_anchor->GetIdx()));
529 : GE_ASSERT_TRUE(size >= 0,
530 : "[Check][TensorSize]tensor_size:%" PRId64
531 : " is invalid, "
532 : "maybe it is unknown shape node, Node_name:%s",
533 : size, node_op_desc->GetNamePtr());
534 : batch_all_memory_size[batch_label].emplace_back(size);
535 : batch_total_size[batch_label] += size;
536 :
537 : if (!anchor_to_symbol_.empty()) {
538 : auto iter1 = anchor_to_symbol_.find(NodeIndexIO(n.get(), out_anchor->GetIdx(), kOut).ToString());
539 : if (iter1 == anchor_to_symbol_.end()) {
540 : continue;
541 : }
542 : const std::string &symbol = iter1->second;
543 : auto iter2 = symbol_mem_reuse_info_.find(symbol);
544 : if (iter2 == symbol_mem_reuse_info_.end()) {
545 : symbol_mem_reuse_info_[symbol].size_ = size;
546 : } else if (size > static_cast<int64_t>(iter2->second.size_)) {
547 : iter2->second.size_ = size;
548 : }
549 : }
550 : }
551 : temp.clear();
552 : GetNodeWorkSpaceSize(n, temp, batch_total_size[batch_label]);
553 : batch_all_memory_size[batch_label].insert(batch_all_memory_size[batch_label].cend(), temp.cbegin(), temp.cend());
554 : }
555 : HandleInStreamRedundantDependence(in_stream_edges_);
556 : InsertStreamOutEdge();
557 :
558 : GELOGI("The last atomic_addr_clean node id: %" PRId64 "", atomic_addr_clean_id_);
559 : GetMaxBatchAllMemorySize(batch_all_memory_size, batch_total_size, all_memory_size, max_batch_label_);
560 : InitReuseFlag();
561 : GE_ASSERT_SUCCESS(continuous_mem_mng_.Init(compute_graph_), "continuous memory manager init failed, graph: %s",
562 : compute_graph_->GetName().c_str());
563 : PrintSymbolMap();
564 : return SUCCESS;
565 : }
566 :
567 : /// @ingroup domi
568 : /// @brief decide memory size based on actual input memory size
569 : /// @param [in] size actual memory size in need
570 : /// @param [in] ranges memory size provided
571 : /// @return size_t memory size to apply
572 : size_t GetBlockSize(size_t size, const std::vector<int64_t> &ranges, bool use_range) {
573 : // binary block use real size
574 : if (!use_range) {
575 : size_t align_size = size;
576 : MemReuseUtils::AlignMemOffset(align_size);
577 : return align_size;
578 : }
579 :
580 : for (int64_t x : ranges) {
581 : auto x_temp = static_cast<size_t>(x);
582 : if (size <= x_temp) {
583 : return x_temp;
584 : }
585 : }
586 :
587 : GELOGW("Memory needed size:%zu is beyond the biggest block in memory ranges.", size);
588 : return size;
589 : }
590 :
591 : /// a b c
592 : /// |___|___|
593 : /// |
594 : /// d e f
595 : /// |___|___|
596 : /// |
597 : /// g
598 : /// e ref input b, g are nopading continuous input, no need to alloc b's memory
599 : bool BlockMemAssigner::IsNoNeedAssignMemory(const NodePtr &n, const NodeIndexIO &out_node_index_io,
600 : const uint32_t index) const {
601 : // ptr has been checked
602 : const auto op_desc = n->GetOpDescBarePtr();
603 : const auto output_tensor_desc = op_desc->MutableOutputDesc(index);
604 : std::string var_name;
605 : if (ge::AttrUtils::GetStr(output_tensor_desc, ASSIGN_VAR_NAME, var_name) && !var_name.empty()) {
606 : GELOGI("Op[%s] output[%u] ref var[%s].", op_desc->GetNamePtr(), index, var_name.c_str());
607 : return true;
608 : }
609 : const auto iter = symbol_mem_reuse_info_.find(out_node_index_io.ToString());
610 : if (iter != symbol_mem_reuse_info_.end()) {
611 : return iter->second.no_assign_mem_;
612 : }
613 : return false;
614 : }
615 :
616 : void BlockMemAssigner::GetRefContinuousInputNodeAndFixedAddrPriorFlag(const std::string &symbol,
617 : const std::list<NodeIndexIO> &anchors) {
618 : uint32_t in_count = 0U;
619 : uint32_t out_count = 0U;
620 : NodeIndexIO tail_node(nullptr, 0U, kIn);
621 : bool is_fixed_addr_prior = false;
622 : for (const auto &node_index_io : anchors) {
623 : if (node_index_io.node_ptr_ == nullptr) {
624 : continue;
625 : }
626 : if (node_index_io.io_type_ == kIn) {
627 : in_count++;
628 : } else if (node_index_io.io_type_ == kOut) {
629 : out_count++;
630 : } else {
631 : // do nothing
632 : }
633 : tail_node.node_ptr_ = node_index_io.node_ptr_;
634 : tail_node.index_ = node_index_io.index_;
635 : tail_node.io_type_ = node_index_io.io_type_;
636 :
637 : if (is_fixed_addr_prior) {
638 : continue;
639 : }
640 :
641 : (void)ge::AttrUtils::GetBool(node_index_io.node_ptr_->GetOpDesc(), ATTR_NAME_IS_FIXED_ADDR_PRIOR,
642 : is_fixed_addr_prior);
643 : if (is_fixed_addr_prior) {
644 : symbol_mem_reuse_info_[symbol].is_fixed_addr_prior_ = true;
645 : GELOGI("Symbol=%s is fixed addr prior, peer node=%s.", symbol.c_str(), node_index_io.ToString().c_str());
646 : }
647 : }
648 :
649 : // one or more ref node, one continuous input node and not continuous input node's first input node
650 : if ((in_count >= 2U) && (in_count == out_count) && (tail_node.index_ != 0U) && (tail_node.io_type_ == kIn) &&
651 : (tail_node.node_ptr_->GetOpDescBarePtr() != nullptr)) {
652 : // Get the continuous input type of the node, default is false
653 : bool is_input_continuous = false;
654 : // If GetBool fail, is_input_continuous is false.
655 : (void)ge::AttrUtils::GetBool(tail_node.node_ptr_->GetOpDescBarePtr(), ATTR_NAME_NOPADDING_CONTINUOUS_INPUT,
656 : is_input_continuous);
657 : if (is_input_continuous) {
658 : symbol_mem_reuse_info_[symbol].no_assign_mem_ = true;
659 : GELOGI("Symbol=%s, ref count:%d, tail node:%s is continuous input.", symbol.c_str(), in_count,
660 : tail_node.node_ptr_->GetNamePtr());
661 : }
662 : }
663 : }
664 :
665 : /// a b c
666 : /// | | |
667 : /// d e f
668 : /// |___|___|
669 : /// |
670 : /// g h i
671 : /// |___|___|
672 : /// |
673 : /// j
674 : /// h and j are nopading continuous input, g can't reuse with a,b,c
675 : /// because their(d,e,f) memory will be replaced by g's memory (cascade continuous input)
676 : /// so g's real life time begin is min of d,e,f
677 : void BlockMemAssigner::GetContinuousNodeLifeTimeBegin(const Node *const org_node, const Node *const node,
678 : const int32_t index, uint32_t depth) {
679 : ++depth;
680 : GE_IF_BOOL_EXEC((depth > kMaxDepthNum), return);
681 :
682 : bool is_nopading_input_continuous = false;
683 : const auto node_op_desc = node->GetOpDescBarePtr();
684 : GE_CHECK_NOTNULL_EXEC(node_op_desc, return);
685 : const auto &org_node_desc = org_node->GetOpDescBarePtr();
686 : GE_CHECK_NOTNULL_EXEC(org_node_desc, return);
687 : (void)ge::AttrUtils::GetBool(node_op_desc, ATTR_NAME_NOPADDING_CONTINUOUS_INPUT, is_nopading_input_continuous);
688 : if (is_nopading_input_continuous) {
689 : for (const auto in_anchor : node->GetAllInDataAnchorsPtr()) {
690 : const bool invalid_node = ((in_anchor == nullptr) || (in_anchor->GetPeerOutAnchor() == nullptr) ||
691 : (in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr() == nullptr));
692 : GE_IF_BOOL_EXEC(invalid_node, continue);
693 : GetContinuousNodeLifeTimeBegin(org_node, in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr(),
694 : in_anchor->GetIdx(), depth);
695 : }
696 :
697 : if (org_node == node) {
698 : SetContinuousNodeLifeTimeBegin(node, node, 0U);
699 : }
700 : } else {
701 : // 2 means has continuous input
702 : GE_IF_BOOL_EXEC((depth < 2U), return);
703 : auto it = cascade_min_life_time_.find(org_node_desc->GetNamePtr());
704 : if (it == cascade_min_life_time_.end()) {
705 : cascade_min_life_time_[org_node_desc->GetNamePtr()] = node_op_desc->GetId();
706 : } else {
707 : if (static_cast<size_t>(node_op_desc->GetId()) < it->second) {
708 : it->second = node_op_desc->GetId();
709 : }
710 : }
711 : // only set first node, continuous first input need alloc memory
712 : if (index == 0) {
713 : cascade_min_life_time_[node_op_desc->GetNamePtr()] = node_op_desc->GetId();
714 : }
715 : GELOGD("Find node:%s life time begin:%" PRId64 " by ref node:%s index:%d.", node_op_desc->GetNamePtr(),
716 : node_op_desc->GetId(), org_node_desc->GetNamePtr(), index);
717 : }
718 : return;
719 : }
720 :
721 : void BlockMemAssigner::SetContinuousNodeLifeTimeBegin(const Node *const org_node, const Node *const node,
722 : uint32_t depth) {
723 : ++depth;
724 : if (depth > kMaxDepthNum) {
725 : return;
726 : }
727 :
728 : const auto node_op_desc = node->GetOpDescBarePtr();
729 : GE_CHECK_NOTNULL_EXEC(node_op_desc, return);
730 : bool is_nopading_input_continuous = false;
731 : (void)ge::AttrUtils::GetBool(node_op_desc, ATTR_NAME_NOPADDING_CONTINUOUS_INPUT, is_nopading_input_continuous);
732 : if (is_nopading_input_continuous) {
733 : for (const auto in_anchor : node->GetAllInDataAnchorsPtr()) {
734 : const bool invalid_node = (in_anchor == nullptr) || (in_anchor->GetPeerOutAnchor() == nullptr);
735 : GE_IF_BOOL_EXEC(invalid_node, continue);
736 : const auto peer_in_node = in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr();
737 : GE_CHECK_NOTNULL_EXEC(peer_in_node, continue);
738 : SetContinuousNodeLifeTimeBegin(org_node, peer_in_node, depth);
739 : }
740 : } else {
741 : // set min life time, only set first node
742 : auto it = cascade_min_life_time_.find(node_op_desc->GetNamePtr());
743 : if (it != cascade_min_life_time_.end()) {
744 : const auto org_node_desc = org_node->GetOpDescBarePtr();
745 : GE_CHECK_NOTNULL_EXEC(org_node_desc, return);
746 : const auto it_org = cascade_min_life_time_.find(org_node_desc->GetNamePtr());
747 : if (it_org != cascade_min_life_time_.cend()) {
748 : GELOGI("Node:%s set min life time begin from %zu to %zu by ref node:%s.", node->GetNamePtr(), it->second,
749 : it_org->second, org_node_desc->GetNamePtr());
750 : it->second = it_org->second;
751 : }
752 : }
753 : }
754 : return;
755 : }
756 : /*
757 : * 1. NoPadding连续输入,仅首个输入分配一个block,所有输入使用这一个block
758 : * 2. 带Padding连续输入,每个输入有自己的block,连续在一起。
759 : * (如果ATTR_NAME_OUTPUT_OFFSET_FOR_BUFFER_FUSION为true,或设置了单独清零,等同于NoPadding连续输入了)
760 : */
761 : bool BlockMemAssigner::IsOutNodeSetContinuousInput(const NodePtr &n, uint32_t out_index,
762 : InDataAnchor *&continuous_in_anchor, bool &is_reuse_zero_copy,
763 : std::set<int64_t> &streams) {
764 : if (out_index >= n->GetAllOutDataAnchorsSize()) {
765 : return false;
766 : }
767 : auto node_desc = n->GetOpDescBarePtr();
768 : GE_ASSERT_NOTNULL(node_desc);
769 : std::vector<int64_t> offsets_for_fusion = {};
770 : bool has_lx_fusion_attr =
771 : AttrUtils::GetListInt(node_desc, ATTR_NAME_OUTPUT_OFFSET_FOR_BUFFER_FUSION, offsets_for_fusion);
772 :
773 : auto out_anchor = n->GetOutDataAnchor(out_index);
774 : GE_ASSERT_NOTNULL(out_anchor);
775 : bool is_out_node_continuous_input = false;
776 :
777 : for (auto const peer_in_anchor : out_anchor->GetPeerInDataAnchorsPtr()) {
778 : InDataAnchor *new_peer_in_anchor = nullptr;
779 : auto is_input_continuous =
780 : MemLayoutConflictUtil::IsContinuousInputThroughRefNode(peer_in_anchor, true, new_peer_in_anchor);
781 : if (is_input_continuous) {
782 : has_lx_fusion_attr = true;
783 : } else {
784 : is_input_continuous =
785 : MemLayoutConflictUtil::IsContinuousInputThroughRefNode(peer_in_anchor, false, new_peer_in_anchor);
786 : }
787 : if (!is_input_continuous) {
788 : continue; // peer node does not need continuous memory
789 : }
790 : /*
791 : * 判断输出节点是不是连续内存节点,不能只看直连的输出,而且也要看经过RefNode连接的节点
792 : * new_peer不一定是n的直连输出,也可能是n经过一个或多个RefNod连接的输出节点
793 : */
794 : GE_ASSERT_NOTNULL(new_peer_in_anchor);
795 : const auto continuous_node = new_peer_in_anchor->GetOwnerNodeBarePtr();
796 : GE_ASSERT_NOTNULL(continuous_node);
797 :
798 : // lx_fusion memory only assign first input, broadcast's input some are variable some are not, reassign later
799 : // In CleanSeparately policy, padding continuous input only allocate index 0 input
800 : const bool is_separate_clean_continuous_input = MemReuseUtils::IsSeparateCleanContinuousInputNode(continuous_node);
801 : if (CheckIsZeroMemNodeType(continuous_node->GetTypePtr()) ||
802 : ((has_lx_fusion_attr || is_separate_clean_continuous_input) && (new_peer_in_anchor->GetIdx() != 0))) {
803 : GELOGI("Node[%s] output[%u] peer node[%s] type[%s] input[%u].", n->GetNamePtr(), out_index,
804 : continuous_node->GetNamePtr(), continuous_node->GetTypePtr(), new_peer_in_anchor->GetIdx());
805 : return false;
806 : }
807 :
808 : // 到这里continuous_node有两种,一种是noPadding连续输入节点,且n是第0个输入。另一种是带Padding连续输入节点,n不一定是第0个输入
809 : if (n->GetOwnerComputeGraphBarePtr() == nullptr) {
810 : continue;
811 : }
812 : GELOGI("%s name[%s] output[%u] peer[%s] input[%d] need continuous, input size[%u].",
813 : n->GetOwnerComputeGraphBarePtr()->GetName().c_str(), n->GetNamePtr(), out_index,
814 : continuous_node->GetNamePtr(), new_peer_in_anchor->GetIdx(), continuous_node->GetAllInDataAnchorsSize());
815 :
816 : // Only set attr one times.
817 : const auto continuous_op_desc = continuous_node->GetOpDescBarePtr();
818 : GE_ASSERT_NOTNULL(continuous_op_desc);
819 : if (node_continuous_input_blocks_[continuous_op_desc->GetId()].size() == 0U) {
820 : is_reuse_zero_copy = false;
821 : // lx fusion case assign max size for first block, so reuse as none continuous
822 : // In CleanSeparately policy, need to calculate the size of the input application memory of index 0 through
823 : // is_out_node_continuous_input
824 : if (has_lx_fusion_attr || is_separate_clean_continuous_input) {
825 : is_op_reuse_mem_ = IsContinuousMemoryReuse(n.get(), out_index, continuous_node, streams);
826 : is_out_node_continuous_input = is_separate_clean_continuous_input ? true : is_out_node_continuous_input;
827 : is_separate_clean_continuous_inputs_ = is_separate_clean_continuous_input;
828 : continue;
829 : }
830 : node_continuous_input_counts_[continuous_op_desc->GetId()] =
831 : std::make_pair(continuous_node->GetTypePtr(), continuous_node->GetAllInDataAnchorsSize());
832 : }
833 : continuous_in_anchor = new_peer_in_anchor;
834 : is_out_node_continuous_input = true;
835 : }
836 : return is_out_node_continuous_input;
837 : }
838 :
839 : Status BlockMemAssigner::CalNodeAsContinuousInputMaxLife(const Node *const n, uint32_t out_index,
840 : const Node *const continuous_node,
841 : int64_t &first_node_max_life, std::set<int64_t> &streams) {
842 : // n,peer_node_desc have been checked
843 : auto node_desc = n->GetOpDescBarePtr();
844 : auto peer_node_desc = continuous_node->GetOpDescBarePtr();
845 : life_begin_ = static_cast<size_t>(node_desc->GetId());
846 : // lx fusion case check all continuous input node, first input node's life time should be min
847 : for (const auto &in_anchor : continuous_node->GetAllInDataAnchorsPtr()) {
848 : GE_CHECK_NOTNULL(in_anchor);
849 : if (in_anchor->GetPeerOutAnchor() == nullptr) {
850 : continue;
851 : }
852 : if ((in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr() == nullptr) ||
853 : (in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr()->GetOpDescBarePtr() == nullptr)) {
854 : GELOGE(FAILED, "[Check][OpDesc]Node[%s] output[%u] peer input node desc is null.", n->GetNamePtr(), out_index);
855 : return FAILED;
856 : }
857 : auto peer_out_node_desc = in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr()->GetOpDescBarePtr();
858 : Node *src_node = nullptr;
859 : int32_t src_out_index = 0;
860 : GE_ASSERT_SUCCESS(
861 : MemReuseUtils::GetSrcNodeThroughRefNode(continuous_node, in_anchor->GetIdx(), src_node, src_out_index));
862 : (void)src_out_index;
863 : GE_ASSERT_NOTNULL(src_node->GetOpDescBarePtr());
864 : /*
865 : * a(stream 0)
866 : * |
867 : * b(stream 0)--+
868 : * | |
869 : * c(stream 0) d(stream 1)
870 : * \ /
871 : * Phonyconcat
872 : *
873 : * 如果Phonyconcat的所有输入的流相同,则取id最小的作为life_begin_
874 : * 如果有的输入与首个输入流不同,例如c是首个输入,而d的流不同,则找stream1 <- stream0的入边,本例子会找到b
875 : */
876 : int64_t min_life_time = kMinLifeTime;
877 : GetDiffStreamMinLifeTime(src_node, GetStreamId(n->GetOpDescBarePtr()), in_stream_edges_, min_life_time);
878 : if (static_cast<size_t>(min_life_time) < life_begin_) {
879 : life_begin_ = static_cast<size_t>(min_life_time);
880 : GELOGI("Node[%s] life[%" PRId64 "] output[%u] is not continuous input node[%s] life[%" PRId64
881 : "]'s min life time, "
882 : "min is life[%zu], src_node[%s], life[%" PRId64 "], stream_id[%" PRId64 "]",
883 : n->GetNamePtr(), node_desc->GetId(), out_index, peer_node_desc->GetNamePtr(), peer_node_desc->GetId(),
884 : min_life_time, src_node->GetNamePtr(), src_node->GetOpDescBarePtr()->GetId(),
885 : GetStreamId(src_node->GetOpDescBarePtr()));
886 : }
887 : // when node5's first input node2's life time is not max(node6 > node5), set it to max
888 : int64_t max_node_life_time_by_symbol = 0;
889 : const int64_t node_max_life =
890 : GetNodeMaxLife(symbol_to_anchors_, out_stream_edges_, in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr(),
891 : in_anchor->GetPeerOutAnchor()->GetIdx(), max_node_life_time_by_symbol, streams,
892 : GetStreamId(n->GetOpDescBarePtr()));
893 : if (node_max_life > first_node_max_life) {
894 : first_node_max_life = node_max_life;
895 : GELOGI("Node[%s] life[%" PRId64 "] output[%u]'s continuous input node[%s] life[%" PRId64
896 : "]'s is not node[%s] output[%d]'s "
897 : "max life node",
898 : n->GetNamePtr(), node_desc->GetId(), out_index, peer_node_desc->GetNamePtr(), peer_node_desc->GetId(),
899 : peer_out_node_desc->GetNamePtr(), in_anchor->GetPeerOutAnchor()->GetIdx());
900 : }
901 : }
902 : return SUCCESS;
903 : }
904 :
905 : /// @ingroup GE
906 : /// @brief Check continuous memory reusable
907 : /// @return void
908 : bool BlockMemAssigner::IsContinuousMemoryReuse(const Node *const n, uint32_t out_index,
909 : const Node *const continuous_node, std::set<int64_t> &streams) {
910 : if (!is_op_reuse_mem_) {
911 : return false;
912 : }
913 :
914 : int64_t first_node_max_life = 0;
915 : if (CalNodeAsContinuousInputMaxLife(n, out_index, continuous_node, first_node_max_life, streams) != SUCCESS) {
916 : return false;
917 : }
918 : life_end_ = static_cast<size_t>(first_node_max_life);
919 : return true;
920 : }
921 :
922 : void IsSymbolNodePreReuse(const Node *const node, const bool has_subgraph_data, bool &pre_reuse_flag,
923 : bool &post_reuse_flag) {
924 : static const std::string kFunctionOp = "FunctionOp";
925 : // node reference subgraph data, data output cannot reuse
926 : bool is_ref = false;
927 : (void)ge::AttrUtils::GetBool(node->GetOpDescBarePtr(), ATTR_NAME_REFERENCE, is_ref);
928 : if (has_subgraph_data && is_ref) {
929 : pre_reuse_flag = false;
930 : }
931 : // iteratorGetNext output cannot reuse
932 : if (node->GetType() == kFunctionOp) {
933 : std::string original_type;
934 : (void)AttrUtils::GetStr(node->GetOpDescBarePtr(), ATTR_NAME_FRAMEWORK_ORIGINAL_TYPE, original_type);
935 : if (original_type == ITERATORV2) {
936 : pre_reuse_flag = false;
937 : post_reuse_flag = false;
938 : }
939 : }
940 : }
941 :
942 450 : bool BlockMemAssigner::GetOutputNodeReuseMemFlagByIndex(const int32_t index) const {
943 : if (output_index_to_reuse_mem_flag_.size() == 0U) {
944 : return false;
945 : }
946 :
947 : return ((index >= 0) && (static_cast<size_t>(index) < output_index_to_reuse_mem_flag_.size()))
948 : ? output_index_to_reuse_mem_flag_[index]
949 : : false;
950 : }
951 :
952 746 : bool BlockMemAssigner::GetInputNodeReuseMemFlag(const NodePtr &n) const {
953 : if (input_index_to_reuse_mem_flag_.size() == 0U) {
954 : return false;
955 : }
956 :
957 : const auto op_desc = n->GetOpDescBarePtr();
958 : int32_t index = 0;
959 : if (!(ge::AttrUtils::GetInt(op_desc, ATTR_NAME_INDEX, index))) {
960 : GELOGW("Node[%s] Get index from data attr failed.", op_desc->GetName().c_str());
961 : return false;
962 : }
963 :
964 : return ((index >= 0) && (static_cast<size_t>(index) < input_index_to_reuse_mem_flag_.size()))
965 : ? input_index_to_reuse_mem_flag_[index]
966 : : false;
967 : }
968 :
969 : /// @ingroup GE
970 : /// @brief Check pre_reuse flag & post_reuse glag for each symbol
971 : /// @return void
972 : void BlockMemAssigner::InitReuseFlag() {
973 : static const std::set<std::string> kNoPreReuseTypes = {
974 : ge::DATA_TYPE, ge::AIPP_DATA_TYPE, ge::ANN_DATA_TYPE, ge::QUEUE_DATA, ge::PROPOSAL, ge::CONSTANT,
975 : ge::CONSTANTOP, ge::GETNEXT, ge::DROPOUTGENMASK, ge::REFDATA, "AdpGetNext", "DynamicGetNext"};
976 : static const std::set<std::string> kNoPostReuseTypes = {
977 : ge::DATA_TYPE, ge::AIPP_DATA_TYPE, ge::QUEUE_DATA, ge::ENTER, ge::REFENTER, ge::NEXTITERATION,
978 : ge::REFNEXTITERATION, ge::REFDATA, ge::GETNEXT, "AdpGetNext", "DynamicGetNext", ge::DROPOUTGENMASK};
979 : InitDiffStreamSameOutTable();
980 : for (const auto &pair : symbol_to_anchors_) {
981 : const std::string &symbol = pair.first;
982 : bool pre_reuse_flag = true;
983 : bool post_reuse_flag = true;
984 : // default memory type
985 : int64_t mem_type = RT_MEMORY_HBM;
986 : GetSymbolMemType(pair.second, mem_type);
987 : AddSymbolMemType(symbol, mem_type);
988 : GetRefContinuousInputNodeAndFixedAddrPriorFlag(symbol, pair.second);
989 : GELOGD("The memory type of symbol[%s] is [%" PRId64 "].", symbol.c_str(), mem_type);
990 : if (mem_type == RT_MEMORY_P2P_DDR) {
991 : UpdateOpTensorMemType(pair.second, mem_type);
992 : }
993 : bool has_subgraph_data = false;
994 : bool diff_stream_prior = false;
995 : for (const auto &node_index_io : pair.second) {
996 : GE_CHECK_NOTNULL_EXEC(node_index_io.node_ptr_, continue);
997 : GE_CHECK_NOTNULL_EXEC(node_index_io.node_ptr_->GetOpDescBarePtr(), continue);
998 : bool in_flag = MemReuseUtils::IsDirectInputNode(node_index_io.node_ptr_, compute_graph_);
999 : bool in_reuse_mem_flag = in_flag ? GetInputNodeReuseMemFlag(node_index_io.node_) : false;
1000 :
1001 : // unknown graph subgraph data cannot reuse because zero copy.
1002 : if (!in_flag && (node_index_io.node_ptr_->GetType() == DATA) &&
1003 : node_index_io.node_ptr_->GetOpDescBarePtr()->HasAttr(ATTR_NAME_PARENT_NODE_INDEX)) {
1004 : GELOGD("Node: %s is subgraph data, continue", node_index_io.node_ptr_->GetNamePtr());
1005 : has_subgraph_data = true;
1006 : continue;
1007 : }
1008 : if (node_index_io.io_type_ == kIn) {
1009 : continue;
1010 : }
1011 : IsSymbolNodePreReuse(node_index_io.node_ptr_, has_subgraph_data, pre_reuse_flag, post_reuse_flag);
1012 : diff_stream_prior = MemReuseStrategy::GetDiffStreamPrior(node_index_io.node_ptr_);
1013 : OutDataAnchorPtr out_anchor = node_index_io.node_ptr_->GetOutDataAnchor(node_index_io.index_);
1014 : if (out_anchor == nullptr) {
1015 : continue;
1016 : }
1017 :
1018 : bool out_flg = false;
1019 : bool out_reuse_mem_flag = false;
1020 : for (const auto in_anchor : out_anchor->GetPeerInDataAnchorsPtr()) {
1021 : if (MemReuseUtils::IsDirectOutputNode(in_anchor->GetOwnerNodeBarePtr(), compute_graph_)) {
1022 : out_flg = true;
1023 : out_reuse_mem_flag = GetOutputNodeReuseMemFlagByIndex(in_anchor->GetIdx());
1024 : break;
1025 : }
1026 : }
1027 :
1028 : const auto type = out_anchor->GetOwnerNodeBarePtr()->GetTypePtr();
1029 : if (in_reuse_mem_flag || out_reuse_mem_flag) {
1030 : // model data no pre reuse, post reuse
1031 : // model net out pre reuse, no post reuse
1032 : pre_reuse_flag = pre_reuse_flag && (!in_flag) && (out_flg || NotMatchNoReuseType(kNoPreReuseTypes, type));
1033 : post_reuse_flag = post_reuse_flag && (!out_flg) && (in_flag || NotMatchNoReuseType(kNoPostReuseTypes, type));
1034 : } else {
1035 : // model data no pre reuse, no post reuse
1036 : // model net out no pre reuse, no post reuse
1037 : pre_reuse_flag = pre_reuse_flag && (!in_flag) && (!out_flg) && NotMatchNoReuseType(kNoPreReuseTypes, type);
1038 : post_reuse_flag = post_reuse_flag && (!in_flag) && (!out_flg) && NotMatchNoReuseType(kNoPostReuseTypes, type);
1039 : }
1040 : if (!pre_reuse_flag && !post_reuse_flag) {
1041 : break;
1042 : }
1043 : }
1044 : MemoryReuseInfo &memory_reuse_info = symbol_mem_reuse_info_[symbol];
1045 : memory_reuse_info.pre_reuse_flag_ = pre_reuse_flag;
1046 : memory_reuse_info.post_reuse_flag_ = post_reuse_flag;
1047 : memory_reuse_info.diff_stream_prior_ = diff_stream_prior;
1048 : // 全局不复用的内存后面会单独累加,这里只处理pre_reuse_flag_为false的
1049 : if ((!memory_reuse_info.pre_reuse_flag_) && memory_reuse_info.post_reuse_flag_) {
1050 : auto &memory_stat = memory_stat_[mem_type];
1051 : auto align_size = memory_reuse_info.size_;
1052 : MemReuseUtils::AlignMemOffset(align_size);
1053 : memory_stat.theory_memory_size_ += align_size;
1054 : }
1055 : }
1056 : }
1057 :
1058 : /*
1059 : * 输出使用同一个memory_block的当成一组,在进行复用时,要么同时复用某个节点,要么同时不复用某个节点,共同进退。这里只需要处理不同流的,
1060 : * StreamMerge/Merge输入必然不同流,NoPaddingContinuousInput 会计算同流或不同流的所有输入最小life_begin_。
1061 : */
1062 : void BlockMemAssigner::InitDiffStreamSameOutTable() {
1063 : for (auto &node : compute_graph_->GetAllNodesPtr()) {
1064 : if (!MemReuseUtils::IsMergeNode(node)) {
1065 : continue;
1066 : }
1067 : std::list<OutDataAnchor *> same_out_anchor;
1068 : std::set<int64_t> stream_id_set;
1069 : for (auto in_anchor : node->GetAllInDataAnchorsPtr()) {
1070 : if ((in_anchor->GetPeerOutAnchor() != nullptr) &&
1071 : (in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr() != nullptr)) {
1072 : same_out_anchor.push_back(in_anchor->GetPeerOutAnchor().get());
1073 : stream_id_set.insert(GetStreamId(in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr()->GetOpDescBarePtr()));
1074 : }
1075 : }
1076 : if (stream_id_set.size() <= 1U) {
1077 : continue;
1078 : }
1079 : same_out_group_holder_.push_back(std::move(same_out_anchor));
1080 : for (auto out_anchor : same_out_group_holder_.back()) {
1081 : same_out_group_[out_anchor] = &same_out_group_holder_.back();
1082 : }
1083 : if (IsLogEnable(GE, DLOG_INFO)) {
1084 : std::stringstream ss;
1085 : for (auto out_anchor : same_out_group_holder_.back()) {
1086 : ss << "topoid_" << out_anchor->GetOwnerNodeBarePtr()->GetOpDescBarePtr()->GetId() << "_out_"
1087 : << out_anchor->GetIdx() << ", ";
1088 : }
1089 : GELOGI("diff stream same out: %s", ss.str().c_str());
1090 : }
1091 : }
1092 : }
1093 :
1094 : // 是否存在和n的index输出使用同一块的节点,且不同流的
1095 : bool BlockMemAssigner::HasSameOutAnchorWithDiffStream(const Node *n, const uint32_t index) const {
1096 : GE_ASSERT_NOTNULL(n);
1097 : const auto out_data_anchor = n->GetOutDataAnchor(index);
1098 : GE_ASSERT_NOTNULL(out_data_anchor);
1099 : return same_out_group_.find(out_data_anchor.get()) != same_out_group_.end();
1100 : }
1101 :
1102 : void BlockMemAssigner::AddSymbolMemType(const std::string &symbol, int64_t memory_type) {
1103 : // Only the memory with special requirements is processed. The HBM uses the default processing mode.
1104 : if ((memory_type == RT_MEMORY_P2P_DDR) || (memory_type == RT_MEMORY_HOST) || (memory_type == RT_MEMORY_HOST_SVM)) {
1105 : symbol_mem_reuse_info_[symbol].mem_type_ = memory_type;
1106 : } else {
1107 : symbol_mem_reuse_info_[symbol].mem_type_ = RT_MEMORY_HBM;
1108 : }
1109 : }
1110 :
1111 : /// @ingroup GE
1112 : /// @brief get pre_reuse flag
1113 : /// @param [in] node
1114 : /// @param [in] out_index
1115 : /// @return bool
1116 : bool BlockMemAssigner::IsPreReuse(const NodeIndexIO &cur_node_index_io, std::string &symbol) const {
1117 : auto iter1 = anchor_to_symbol_.find(cur_node_index_io.ToString());
1118 : if (iter1 == anchor_to_symbol_.end()) {
1119 : return false;
1120 : }
1121 :
1122 : symbol = iter1->second;
1123 : auto iter2 = symbol_mem_reuse_info_.find(symbol);
1124 : if (iter2 == symbol_mem_reuse_info_.end()) {
1125 : return false;
1126 : }
1127 : return iter2->second.pre_reuse_flag_;
1128 : }
1129 :
1130 : bool BlockMemAssigner::IsPostReuse(const std::string &symbol, bool &diff_stream_prior) const {
1131 : auto iter = symbol_mem_reuse_info_.find(symbol);
1132 : if (iter == symbol_mem_reuse_info_.end()) {
1133 : return true;
1134 : }
1135 :
1136 : diff_stream_prior = iter->second.diff_stream_prior_;
1137 : return iter->second.post_reuse_flag_;
1138 : }
1139 :
1140 : /// @ingroup GE
1141 : /// @brief get post_reuse flag
1142 : /// @param [in] mem_block
1143 : /// @return bool
1144 : bool BlockMemAssigner::IsPostReuse(const ge::MemoryBlock *const mem_block) const {
1145 : if (mem_block == nullptr) {
1146 : return false;
1147 : }
1148 : return mem_block->post_reuse_flag_;
1149 : }
1150 : /// @ingroup GE
1151 : /// @brief check if symbol of cur node_index_io has block
1152 : /// @param [in] node_index_io
1153 : /// @param [out] symbol
1154 : /// @return bool
1155 : bool BlockMemAssigner::IsSymbolExist(const NodeIndexIO &node_index_io, std::string &symbol, MemoryBlock *&block) const {
1156 : block = nullptr;
1157 : const auto node_io = node_index_io.ToString();
1158 : auto iter = anchor_to_symbol_.find(node_io);
1159 : if (iter == anchor_to_symbol_.end()) {
1160 : return false;
1161 : }
1162 :
1163 : symbol = iter->second;
1164 : auto it_block = symbol_blocks_.find(iter->second);
1165 : auto symbol_exist = (it_block != symbol_blocks_.end());
1166 : if (symbol_exist) {
1167 : GELOGD("Node io:%s symbol:%s block:%s", node_io.c_str(), symbol.c_str(), GetName(*(it_block->second)).c_str());
1168 : block = it_block->second;
1169 : }
1170 : return symbol_exist;
1171 : }
1172 :
1173 : /// @ingroup GE
1174 : /// @brief check if symbol of cur node_index_io has output description block
1175 : /// @param [in] node_index_io
1176 : /// @param [out] symbol
1177 : /// @return bool
1178 : bool BlockMemAssigner::IsSymbolDescBlockExist(const NodeIndexIO &node_index_io, std::string &symbol,
1179 : MemoryBlock *&block) const {
1180 : const auto node_io = node_index_io.ToString();
1181 : auto iter = anchor_to_symbol_.find(node_io);
1182 : if (iter == anchor_to_symbol_.end()) {
1183 : return false;
1184 : }
1185 :
1186 : symbol = iter->second;
1187 : GELOGD("Node io:%s symbol:%s", node_io.c_str(), symbol.c_str());
1188 : const auto it_block = symbol_desc_blocks_.find(iter->second);
1189 : if (it_block == symbol_desc_blocks_.cend()) {
1190 : return false;
1191 : }
1192 : block = it_block->second;
1193 : return true;
1194 : }
1195 :
1196 : /// @ingroup GE
1197 : /// @brief Print symbol
1198 : /// @return void
1199 : void BlockMemAssigner::PrintSymbolMap() {
1200 : if (!IsLogEnable(GE, DLOG_DEBUG)) {
1201 : return;
1202 : }
1203 :
1204 : for (const auto &pair : symbol_to_anchors_) {
1205 : GELOGD("symbol=%s, max_size=%zu, pre_reuse=%s, post_reuse=%s", pair.first.c_str(),
1206 : symbol_mem_reuse_info_[pair.first].size_,
1207 : symbol_mem_reuse_info_[pair.first].pre_reuse_flag_ ? "true" : "false",
1208 : symbol_mem_reuse_info_[pair.first].post_reuse_flag_ ? "true" : "false");
1209 : for (const auto &node_index_io : pair.second) {
1210 : GELOGD("anchor:%s id:%" PRId64 "", node_index_io.ToString().c_str(),
1211 : ((node_index_io.node_ptr_ != nullptr) && (node_index_io.node_ptr_->GetOpDescBarePtr() != nullptr))
1212 : ? node_index_io.node_ptr_->GetOpDescBarePtr()->GetId()
1213 : : 0);
1214 : }
1215 : }
1216 : }
1217 :
1218 : void BlockMemAssigner::GetSymbolMemType(const std::list<NodeIndexIO> &node_index_io_list, int64_t &memory_type) {
1219 : memory_type = RT_MEMORY_HBM;
1220 : std::vector<int64_t> memory_types;
1221 : for (auto &node_index_io : node_index_io_list) {
1222 : auto op_desc = node_index_io.node_ptr_->GetOpDescBarePtr();
1223 : GE_CHECK_NOTNULL_JUST_RETURN(op_desc);
1224 : if (node_index_io.io_type_ == kIn) {
1225 : std::vector<int64_t> input_memory_types;
1226 : (void)ge::AttrUtils::GetListInt(op_desc, ATTR_NAME_INPUT_MEM_TYPE_LIST, input_memory_types);
1227 : if (!input_memory_types.empty() && node_index_io.index_ < input_memory_types.size()) {
1228 : int64_t input_memory_type = input_memory_types[node_index_io.index_];
1229 : GELOGD("Node[%s]: the memory type of input index [%u] is [%" PRId64 "]].", op_desc->GetNamePtr(),
1230 : node_index_io.index_, input_memory_type);
1231 : memory_types.emplace_back(input_memory_type);
1232 : }
1233 : }
1234 : if (node_index_io.io_type_ == kOut) {
1235 : std::vector<int64_t> output_memory_types;
1236 : (void)ge::AttrUtils::GetListInt(op_desc, ATTR_NAME_OUTPUT_MEM_TYPE_LIST, output_memory_types);
1237 : if (!output_memory_types.empty() && node_index_io.index_ < output_memory_types.size()) {
1238 : int64_t output_memory_type = output_memory_types[node_index_io.index_];
1239 : GELOGD("Node[%s]: the memory type of output index [%u] is [%" PRId64 "].", op_desc->GetNamePtr(),
1240 : node_index_io.index_, output_memory_type);
1241 : memory_types.emplace_back(output_memory_type);
1242 : }
1243 : }
1244 : }
1245 :
1246 : // memory priority
1247 : for (auto node_memory_type : memory_types) {
1248 : if (node_memory_type > memory_type) {
1249 : memory_type = node_memory_type;
1250 : }
1251 : }
1252 : }
1253 :
1254 : void BlockMemAssigner::UpdateOpTensorMemType(const std::list<NodeIndexIO> &node_index_io_list, int64_t memory_type) {
1255 : for (const auto &node_index_io : node_index_io_list) {
1256 : auto op_desc = node_index_io.node_ptr_->GetOpDescBarePtr();
1257 : GE_CHECK_NOTNULL_JUST_RETURN(op_desc);
1258 : if (node_index_io.io_type_ == kIn) {
1259 : auto input_desc = op_desc->MutableInputDesc(node_index_io.index_);
1260 : int_attr_.emplace_back(input_desc.get(), op_desc, node_index_io.index_, ATTR_NAME_TENSOR_MEM_TYPE, memory_type);
1261 : }
1262 :
1263 : if (node_index_io.io_type_ == kOut) {
1264 : auto output_desc = op_desc->MutableOutputDesc(node_index_io.index_);
1265 : int_attr_.emplace_back(output_desc.get(), op_desc, node_index_io.index_, ATTR_NAME_TENSOR_MEM_TYPE, memory_type);
1266 : }
1267 : }
1268 : }
1269 :
1270 : bool BlockMemAssigner::IsZeroCopyBlock(const NodePtr &node, uint32_t output_index, bool continuous,
1271 : size_t output_size) const {
1272 : std::string op_type(node->GetTypePtr());
1273 :
1274 : // 现状:
1275 : // 动态shape静态子图的输入,按零拷贝处理(ge执行流程不支持输入做拷贝处理,搞成非零拷贝会有精度问题,输入数据错误)
1276 : // 动态shape静态子图的输出,按非零拷贝处理(搞成零拷贝需要hccl算子支持地址刷新,会导致性能劣化)
1277 : if (NodeUtils::IsDynamicShape(node)) {
1278 : if (compute_graph_.get() != node->GetOwnerComputeGraphBarePtr()) {
1279 : return false;
1280 : }
1281 :
1282 : if (OpTypeUtils::IsDataNode(op_type)) {
1283 : return (!continuous);
1284 : }
1285 : if (is_static_model_addr_fixed_ && (node->GetOpDesc()->GetOpKernelLibName() == ge::kEngineNameHccl)) {
1286 : return false;
1287 : }
1288 : return (GetOutputFlowToNetoutputNum(node, output_index, compute_graph_, symbol_to_anchors_, anchor_to_symbol_) >
1289 : 0U);
1290 : }
1291 :
1292 : if (is_io_alloc_by_ge_in_run_graph_ && (output_size > input_fusion_size_)) {
1293 : return false;
1294 : }
1295 :
1296 : if (continuous) { // Never zero copy for data flow to require-continuous-input node
1297 : GELOGD("Node %s output %u cannot zero copy as require continuous output", node->GetNamePtr(), output_index);
1298 : return false;
1299 : }
1300 :
1301 : if (op_type == NETOUTPUT) {
1302 : const auto owner = node->GetOwnerComputeGraphBarePtr();
1303 : bool ret = (owner != nullptr) && (owner->GetParentGraph() == nullptr);
1304 : GELOGD("Node %s output %u result %d", node->GetNamePtr(), output_index, ret);
1305 : return ret;
1306 : }
1307 :
1308 : if (OpTypeUtils::IsDataNode(op_type)) {
1309 : bool is_multi_batch_shape_data = false;
1310 : (void)AttrUtils::GetBool(node->GetOpDesc(), "_is_multi_batch_shape_data", is_multi_batch_shape_data);
1311 : std::string build_graph_mode;
1312 : const bool is_build_graph_offline =
1313 : ((ge::GetContext().GetOption(ge::OPTION_BUILD_GRAPH_MODE, build_graph_mode) == ge::GRAPH_SUCCESS) &&
1314 : (build_graph_mode.compare(kOffline) == 0));
1315 : if (!is_build_graph_offline && is_multi_batch_shape_data) {
1316 : GELOGI("Multi batch shape data node[%s] output memory no need zero copy.", node->GetName().c_str());
1317 : return false;
1318 : }
1319 : if (node->GetOpDescBarePtr()->HasAttr(ATTR_NAME_PARENT_NODE_INDEX)) { // Never zero copy for subgrapgh data
1320 : return false;
1321 : }
1322 : // Data flow to unsupported zero copy task type eg. memcpy, can never zero copied
1323 : return IsNodeAndPeerNodeTaskSupportZeroCopy(node, output_index);
1324 : }
1325 :
1326 : // Only node output that flow to sure one output maybe zero copied
1327 : if (GetOutputFlowToNetoutputNum(node, output_index, compute_graph_, symbol_to_anchors_, anchor_to_symbol_) ==
1328 : 1U) { // 1U means output to only one netoutput
1329 : // Output from unsupported task type eg. memcpy, can never zero copied
1330 : return IsNodeAndPeerNodeTaskSupportZeroCopy(node, output_index);
1331 : }
1332 :
1333 : return false;
1334 : }
1335 :
1336 : void BlockMemAssigner::AddMemoryStat(uint64_t memory_type, size_t real_size, bool is_reuse_memory) {
1337 : auto &memory_stat = memory_stat_[memory_type];
1338 : size_t align_size = real_size;
1339 : MemReuseUtils::AlignMemOffset(align_size);
1340 : memory_stat.total_memory_size_ += align_size;
1341 : if (is_reuse_memory) {
1342 : memory_stat.theory_memory_size_ += align_size;
1343 : } else {
1344 : memory_stat.theory_no_reuse_memory_size_ += align_size;
1345 : }
1346 :
1347 : if (memory_stat.theory_memory_size_ > memory_stat.theory_min_memory_size_) {
1348 : memory_stat.theory_min_memory_size_ = memory_stat.theory_memory_size_;
1349 : }
1350 : }
1351 :
1352 : MemoryBlock *BlockMemAssigner::ApplyMemory(const NodePtr &n, const std::vector<bool> &workspace_reuse_flag,
1353 : const ApplyMemoryParam ¶m) {
1354 : auto node_op_desc = n->GetOpDescBarePtr();
1355 : std::string batch_label;
1356 : (void)ge::AttrUtils::GetStr(node_op_desc, ATTR_NAME_BATCH_LABEL, batch_label);
1357 : MemoryBlock *reusable_block = nullptr;
1358 : auto stream_id = GetStreamId(node_op_desc);
1359 : bool is_reuse_memory = false;
1360 : GetRealStreamIdForParentNode(n, param.out_index, stream_id, is_reuse_memory);
1361 : bool no_reuse = false;
1362 : std::string symbol;
1363 : // model data output can't reuse other, but it can be reused
1364 : const bool pre_reuse = (param.mem_type == OpMemoryType::kOutput)
1365 : ? IsPreReuse(NodeIndexIO(n.get(), param.out_index, kOut), symbol)
1366 : : true;
1367 : const auto it_life_time_begin = cascade_min_life_time_.find(node_op_desc->GetNamePtr());
1368 : (void)ge::AttrUtils::GetBool(node_op_desc, kOpNoReuseMem, no_reuse);
1369 : if ((!no_reuse) && (param.mem_type == OpMemoryType::kWorkspace)) {
1370 : no_reuse = ((workspace_reuse_flag.size() > param.out_index) && !workspace_reuse_flag[param.out_index]);
1371 : }
1372 : const bool mod_life_begin =
1373 : ((param.mem_type != OpMemoryType::kWorkspace) && (it_life_time_begin != cascade_min_life_time_.end()) &&
1374 : (it_life_time_begin->second < life_begin_)) ||
1375 : no_reuse || (!pre_reuse);
1376 : if (mod_life_begin) {
1377 : // no pre reuse set life time begin to 1
1378 : life_begin_ = ((!pre_reuse) || no_reuse) ? kMinLifeTime : it_life_time_begin->second;
1379 : GELOGD("Node %s output %u life_begin_ change to %zu", n->GetNamePtr(), param.out_index, life_begin_);
1380 : }
1381 : bool diff_stream_prior = false;
1382 : bool post_reuse_flag = IsPostReuse(symbol, diff_stream_prior);
1383 : if ((param.mem_type == OpMemoryType::kOutput) && (!post_reuse_flag)) {
1384 : life_end_ = kMaxLifeTime;
1385 : }
1386 : const auto has_diff_stream_same_out =
1387 : (param.mem_type == OpMemoryType::kOutput) && HasSameOutAnchorWithDiffStream(n.get(), param.out_index);
1388 : is_reuse_memory = is_reuse_memory && is_ge_reuse_mem_ && (param.mem_type != OpMemoryType::kOutputDesc) &&
1389 : !node_op_desc->HasAttr(kL2FusionDynamicConvergeOp) && !no_reuse && param.is_op_reuse_mem;
1390 : auto &reusable_blocks = reusable_blocks_[param.memory_type][stream_id];
1391 : // continuous memory reuse in level2 reuse
1392 : bool do_reuse =
1393 : is_reuse_memory && pre_reuse && !param.continuous && (!param.is_zero_copy) && !has_diff_stream_same_out;
1394 : const NodeTypeIndex node_type_index{n.get(), param.mem_type, param.out_index, false, life_begin_, stream_id};
1395 : if (do_reuse) {
1396 : if (reuse_strategy_.reuse_first_release_) {
1397 : reusable_block = GetFirstReleaseBlock(param.block_size, batch_label, reusable_blocks, node_type_index);
1398 : } else {
1399 : reusable_block = GetLastReleaseBlock(param.block_size, batch_label, reusable_blocks, node_type_index);
1400 : }
1401 : }
1402 :
1403 : auto block = reusable_block;
1404 : if (block == nullptr) {
1405 : block = new (std::nothrow)
1406 : MemoryBlock(reuse_strategy_, param.block_size, stream_id, is_reuse_memory, param.memory_type);
1407 : GE_CHECK_NOTNULL_EXEC(block, return nullptr);
1408 : memory_blocks_.emplace_back(block);
1409 : // cause memory_blocks_ may reduce when swap after,
1410 : // create blocks_store_ to assure blocks deleted finally
1411 : blocks_store_.emplace_back(block);
1412 : GELOGD("Node %s create new block:%s", n->GetNamePtr(), GetName(*block).c_str());
1413 : } else {
1414 : GELOGD("Node %s reuse block:%s", n->GetNamePtr(), GetName(*block).c_str());
1415 : }
1416 :
1417 : if (param.mem_type == OpMemoryType::kOutput) {
1418 : block->AddSymbol(symbol);
1419 : block->post_reuse_flag_ = block->post_reuse_flag_ && post_reuse_flag;
1420 : block->diff_stream_prior_ = diff_stream_prior;
1421 : }
1422 : block->AddNodeTypeIndex(node_type_index, param.real_size, param.no_align_size, stream_id);
1423 : if (has_diff_stream_same_out) {
1424 : block->same_stream_ = false;
1425 : }
1426 : // model net output can reuse other, but it can't be reused
1427 : const bool post_reuse = IsPostReuse(block);
1428 : if (param.continuous) {
1429 : block->SetContinuousBlock();
1430 : }
1431 : block->batch_label_ = batch_label;
1432 : block->reuse_mem_ = block->reuse_mem_ && (post_reuse || pre_reuse);
1433 : if (life_end_ != 0U) {
1434 : block->SetLifeTimeEnd((life_end_ == kMaxLifeTime) ? kDefaultLifeTime : life_end_, stream_id);
1435 : block->SetSymbolLifeEnd(life_end_);
1436 : }
1437 : const bool cal_theory_size = (batch_label.empty() || (batch_label == max_batch_label_)) &&
1438 : (node_op_desc->GetType() != ge::PARTITIONEDCALL) && ((!block->reuse_mem_) || pre_reuse);
1439 : if (cal_theory_size) {
1440 : AddMemoryStat(param.memory_type, param.real_size, block->reuse_mem_);
1441 : }
1442 : return block;
1443 : }
1444 :
1445 : bool BlockMemAssigner::IsNodeOutputUseSameMemWithNetOutput(const ge::NodePtr &node, uint32_t out_index) const {
1446 : const auto cur_node_index_io = NodeIndexIO(node, out_index, kOut);
1447 : const auto &symbol_iter = anchor_to_symbol_.find(cur_node_index_io.ToString());
1448 : if (symbol_iter == anchor_to_symbol_.cend()) {
1449 : return false;
1450 : }
1451 : const auto &anchors_iter = symbol_to_anchors_.find(symbol_iter->second);
1452 : if (anchors_iter == symbol_to_anchors_.cend()) {
1453 : return false;
1454 : }
1455 : for (const auto &anchor : anchors_iter->second) {
1456 : if (anchor.node_->GetType() == NETOUTPUT) {
1457 : return true;
1458 : }
1459 : }
1460 : return false;
1461 : }
1462 :
1463 : MemoryBlock *BlockMemAssigner::GetFirstReleaseBlock(const size_t block_size, const std::string &batch_label,
1464 : std::vector<MemoryBlock *> &reusable_blocks,
1465 : const NodeTypeIndex &node_type_index) const {
1466 : for (auto it = reusable_blocks.begin(); it != reusable_blocks.end(); ++it) {
1467 : MemoryBlock *reusable_block = *it;
1468 : if ((reusable_block == nullptr) ||
1469 : (!ReuseBlock(*reusable_block, block_size, life_begin_, batch_label, node_type_index))) {
1470 : continue;
1471 : }
1472 : reusable_blocks.erase(it);
1473 : return reusable_block;
1474 : }
1475 : return nullptr;
1476 : }
1477 :
1478 : MemoryBlock *BlockMemAssigner::GetLastReleaseBlock(const size_t block_size, const std::string &batch_label,
1479 : std::vector<MemoryBlock *> &reusable_blocks,
1480 : const NodeTypeIndex &node_type_index) const {
1481 : for (auto it = reusable_blocks.rbegin(); it != reusable_blocks.rend(); ++it) {
1482 : MemoryBlock *reusable_block = *it;
1483 : if ((reusable_block == nullptr) ||
1484 : (!ReuseBlock(*reusable_block, block_size, life_begin_, batch_label, node_type_index))) {
1485 : continue;
1486 : }
1487 : reusable_blocks.erase((++it).base());
1488 : return reusable_block;
1489 : }
1490 : return nullptr;
1491 : }
1492 :
1493 : void BlockMemAssigner::ContinuousOutRefCheck(bool &is_all_output_ref, bool &is_output_has_ref, const NodePtr &n) {
1494 : const auto node_op_desc = n->GetOpDescBarePtr();
1495 : for (uint32_t index = 0U; index < static_cast<uint32_t>(node_op_desc->GetOutputsSize()); index++) {
1496 : if (!IsOutputIndexRef(node_op_desc, index)) {
1497 : is_all_output_ref = false;
1498 : break;
1499 : } else {
1500 : zero_memory_list_.emplace_back(n.get(), OpMemoryType::kOutput, index);
1501 : is_output_has_ref = true;
1502 : }
1503 : }
1504 : }
1505 :
1506 : Status BlockMemAssigner::ApplyContinuousMemory(const NodePtr &n, const std::vector<int64_t> &ranges,
1507 : const bool is_op_reuse_mem) {
1508 : auto node_op_desc = n->GetOpDescBarePtr();
1509 : GE_CHECK_NOTNULL(node_op_desc);
1510 : life_begin_ = node_op_desc->GetId();
1511 :
1512 : // continuous output support ref only when all output ref input
1513 : bool is_all_output_ref = true;
1514 : bool is_output_has_ref = false;
1515 :
1516 : ContinuousOutRefCheck(is_all_output_ref, is_output_has_ref, n);
1517 :
1518 : if (is_all_output_ref) {
1519 : GELOGI("continuous output node ref all input, skip continuous alloc, node_name:%s", n->GetNamePtr());
1520 : return SUCCESS;
1521 : }
1522 :
1523 : if (!is_all_output_ref && is_output_has_ref) {
1524 : REPORT_INNER_ERR_MSG("E19999", "continuous output node ref part input, not support now. node_name:%s",
1525 : n->GetNamePtr());
1526 : GELOGE(INTERNAL_ERROR, "[Check][OutRefStatus]continuous output node ref part input, not support, node_name:%s",
1527 : n->GetNamePtr());
1528 : return INTERNAL_ERROR;
1529 : }
1530 : MemoryBlock *block = nullptr;
1531 : size_t total_size = 0U;
1532 : uint64_t memory_type = RT_MEMORY_HBM;
1533 : int64_t max_life_time = 0;
1534 : std::string symbol;
1535 : std::set<int64_t> streams;
1536 : GetContinuousOutputMaxLife(n, symbol_to_anchors_, out_stream_edges_, max_life_time, streams);
1537 : for (uint32_t index = 0U; index < static_cast<uint32_t>(node_op_desc->GetOutputsSize()); index++) {
1538 : auto output_op_desc = node_op_desc->MutableOutputDesc(index);
1539 : if (CheckIsZeroMemNodeType(n->GetTypePtr()) || CheckIsZeroMemNodeOutputIndex(n, index)) {
1540 : zero_memory_list_.emplace_back(n.get(), OpMemoryType::kOutput, index);
1541 : continue;
1542 : }
1543 :
1544 : int64_t size = 0;
1545 : if (MemReuseUtils::GetTensorSize(*output_op_desc, size, MemReuseUtils::IsNeedSplitSize(n, index)) != SUCCESS) {
1546 : REPORT_INNER_ERR_MSG("E19999", "get tensor_size failed, node_name:%s, output_index:%u", n->GetNamePtr(), index);
1547 : GELOGE(INTERNAL_ERROR, "[Get][TensorSize]node_name:%s, output_index:%u", n->GetNamePtr(), index);
1548 : return INTERNAL_ERROR;
1549 : }
1550 : size_t align_size = static_cast<size_t>(size);
1551 : MemReuseUtils::AlignMemOffset(align_size);
1552 : total_size += align_size;
1553 :
1554 : // only apply total size in first output
1555 : if (index != 0U) {
1556 : zero_memory_list_.emplace_back(n.get(), OpMemoryType::kOutput, index);
1557 : continue;
1558 : }
1559 : NodeIndexIO node_index_io(n.get(), index, kOut);
1560 : auto iter = anchor_to_symbol_.find(node_index_io.ToString());
1561 : if (iter != anchor_to_symbol_.end()) {
1562 : symbol = iter->second;
1563 : std::map<std::string, MemoryReuseInfo>::const_iterator it = symbol_mem_reuse_info_.find(symbol);
1564 : if (it != symbol_mem_reuse_info_.cend()) {
1565 : memory_type = it->second.mem_type_;
1566 : GELOGD("Continuous out memory symbol is [%s], memory type is [%" PRId64 "]", symbol.c_str(), memory_type);
1567 : }
1568 : }
1569 : }
1570 :
1571 : if (total_size == 0U) {
1572 : return SUCCESS;
1573 : }
1574 :
1575 : auto block_size = GetBlockSize(total_size, ranges, reuse_strategy_.use_range_);
1576 : std::vector<bool> workspace_reuse_flag;
1577 : ApplyMemoryParam param = {block_size, total_size, total_size, OpMemoryType::kOutput, 0U, is_op_reuse_mem,
1578 : false, memory_type, false};
1579 : block = ApplyMemory(n, workspace_reuse_flag, param);
1580 : if (block != nullptr) {
1581 : // hccl task need align header and tail
1582 : block->SetFirstContinuousBlock();
1583 : block->SetLastContinuousBlock();
1584 : block->need_same_offset_in_batch_ = SizeIndependentOfBatch(n->GetTypePtr());
1585 : bool is_reuse_zero_copy = true;
1586 : NodeIndexIO node_index_io(n.get(), 0, kOut);
1587 : int32_t ref_count = GetAllRefCount(node_index_io, is_reuse_zero_copy);
1588 : block->ref_count_ += ref_count;
1589 : block->is_reuse_zero_copy_ = (block->is_reuse_zero_copy_) && (is_reuse_zero_copy);
1590 : max_life_time = (max_life_time == kMaxLifeTime) ? kDefaultLifeTime : max_life_time;
1591 : block->SetLifeTimeEnd(max_life_time, GetStreamId(node_op_desc));
1592 : block->SetOutStreamCount(streams.size());
1593 : GELOGI("Node[%s] continuous out memory size[%zu] block size[%zu] out stream count:%zu ref_count:%d",
1594 : node_op_desc->GetNamePtr(), total_size, block_size, streams.size(), block->ref_count_);
1595 : if (!symbol.empty()) {
1596 : symbol_blocks_[symbol] = block;
1597 : auto iter = symbol_mem_reuse_info_.find(symbol);
1598 : if (iter != symbol_mem_reuse_info_.end()) {
1599 : iter->second.size_ = total_size;
1600 : block->is_fixed_addr_prior_ = (block->is_fixed_addr_prior_ || iter->second.is_fixed_addr_prior_);
1601 : }
1602 :
1603 : GELOGD("Node io:%s add symbol:%s block:%s, fixed addr prior:%d", NodeIndexIO(n.get(), 0, kOut).ToString().c_str(),
1604 : symbol.c_str(), GetName(*block).c_str(), block->is_fixed_addr_prior_);
1605 : }
1606 : } else {
1607 : REPORT_INNER_ERR_MSG("E19999", "apply continuousMemory failed, node_name:%s, total_size:%" PRId64 "",
1608 : n->GetNamePtr(), total_size);
1609 : GELOGE(INTERNAL_ERROR, "[Apply][ContinuousMemory]node_name:%s, total_size:%" PRId64 "", n->GetNamePtr(),
1610 : total_size);
1611 : return INTERNAL_ERROR;
1612 : }
1613 : return SUCCESS;
1614 : }
1615 :
1616 : /*
1617 : * 根据continuous_mem_mng_中对节点输出的排布分配内存,要求IsFound必须返回true才调用该接口
1618 : */
1619 : Status BlockMemAssigner::ApplyContinuousMemWithMng(const NodePtr &n, int32_t idx, const std::vector<int64_t> &ranges) {
1620 : auto op_desc = n->GetOpDescBarePtr();
1621 : GE_CHECK_NOTNULL(op_desc);
1622 : life_begin_ = op_desc->GetId();
1623 :
1624 : GE_ASSERT_TRUE(continuous_mem_mng_.IsFound(n.get(), idx));
1625 : if (!continuous_mem_mng_.IsNeedAssignMemory(n.get(), idx)) {
1626 : zero_memory_list_.emplace_back(n.get(), OpMemoryType::kOutput, idx, false);
1627 : GELOGI("[ContinuousMem]node[%s] output %u, no need assign memory.", op_desc->GetNamePtr(), idx);
1628 : return SUCCESS;
1629 : }
1630 : size_t out_streams_cnt = 1U;
1631 : const auto &continuous_mem = continuous_mem_mng_.GetContinuousMem(n.get(), idx);
1632 : is_op_reuse_mem_ = is_op_reuse_mem_ && continuous_mem.IsReuse();
1633 : if (is_op_reuse_mem_) {
1634 : int64_t begin_time = 0;
1635 : int64_t out_time = 0;
1636 : int64_t end_time = 0;
1637 : GE_ASSERT_SUCCESS(GetContinuousMemLifeTime(continuous_mem, begin_time, out_time, end_time, out_streams_cnt));
1638 : (void)out_time;
1639 : life_begin_ = begin_time;
1640 : life_end_ = end_time;
1641 : }
1642 : uint64_t memory_type = RT_MEMORY_HBM;
1643 : GE_ASSERT_SUCCESS(GetContinuousMemType(continuous_mem, memory_type));
1644 : /*
1645 : * 对于连续输出-连续输入这种场景,不应该出现带有该属性的情况,明确报错不支持
1646 : */
1647 : for (const auto &continuous_node_out : continuous_mem.GetContinuousNodeOut()) {
1648 : std::vector<int64_t> offsets;
1649 : (void)AttrUtils::GetListInt(continuous_node_out.node_ptr_->GetOpDescBarePtr(),
1650 : ATTR_NAME_OUTPUT_OFFSET_FOR_BUFFER_FUSION, offsets);
1651 : GE_ASSERT_TRUE(offsets.empty(), "[ContinuousMem] check buffer fusion offset failed. node: %s has attr: %s",
1652 : continuous_node_out.node_ptr_->GetNamePtr(), ATTR_NAME_OUTPUT_OFFSET_FOR_BUFFER_FUSION.c_str());
1653 : }
1654 : const auto max_size = continuous_mem.GetTotalSize();
1655 : const auto no_align_size = max_size;
1656 :
1657 : MemoryBlock *block = nullptr;
1658 : NodeIndexIO node_index_io(n.get(), idx, kOut);
1659 : auto symbol_iter = anchor_to_symbol_.find(node_index_io.ToString());
1660 : GE_ASSERT_TRUE(symbol_iter != anchor_to_symbol_.end());
1661 : const auto symbol = symbol_iter->second;
1662 :
1663 : const auto iter = symbol_mem_reuse_info_.find(symbol);
1664 : if (iter != symbol_mem_reuse_info_.end()) {
1665 : iter->second.size_ = max_size;
1666 : }
1667 :
1668 : const auto block_size = GetBlockSize(max_size, ranges, reuse_strategy_.use_range_);
1669 : std::vector<bool> workspace_reuse_flag;
1670 : const auto is_zeor_copy = IsZeroCopyBlock(n, idx, true, no_align_size);
1671 : ApplyMemoryParam param = {
1672 : block_size, max_size, no_align_size, OpMemoryType::kOutput, static_cast<uint32_t>(idx),
1673 : is_op_reuse_mem_, false, memory_type, is_zeor_copy};
1674 : block = ApplyMemory(n, workspace_reuse_flag, param);
1675 : GE_ASSERT_NOTNULL(block);
1676 : GE_ASSERT_SUCCESS(continuous_mem_mng_.PushBackBlock(n.get(), idx, block));
1677 : block->need_same_offset_in_batch_ = SizeIndependentOfBatch(n->GetTypePtr());
1678 : if (continuous_mem.IsUseOneBlock()) {
1679 : block->SetFirstContinuousBlock();
1680 : block->SetLastContinuousBlock();
1681 : }
1682 : bool is_reuse_zero_copy = true;
1683 : const auto ref_count = GetAllRefCount(node_index_io, is_reuse_zero_copy);
1684 : block->is_reuse_zero_copy_ = (block->is_reuse_zero_copy_) && (is_reuse_zero_copy);
1685 : block->ref_count_ = block->ref_count_ + ref_count;
1686 :
1687 : if (iter != symbol_mem_reuse_info_.cend()) {
1688 : block->is_fixed_addr_prior_ = (block->is_fixed_addr_prior_ || iter->second.is_fixed_addr_prior_);
1689 : }
1690 : MarkReuseZeroCopyBlockFlag(n, block, idx);
1691 : MarkZeroCopyBlockAttr(bool_attr_, op_desc, block->is_zero_copy_, OpMemoryType::kOutput, idx);
1692 : GELOGI(
1693 : "[ContinuousMem]Node name: %s index:%u size:%zu ref count: %d, zero copy:%d, fixed addr prior: %d, "
1694 : "out_streams_cnt: %zu, memory_type: %d",
1695 : n->GetNamePtr(), idx, block_size, block->ref_count_, block->is_zero_copy_, block->is_fixed_addr_prior_,
1696 : out_streams_cnt, memory_type);
1697 :
1698 : block->SetOutStreamCount(out_streams_cnt);
1699 : block->is_reuse_zero_copy_ = (is_reuse_zero_copy && block->is_reuse_zero_copy_);
1700 : symbol_blocks_[symbol] = block;
1701 : // The output is suspended, and will be released in allocation of next node.
1702 : CheckAndReleaseSuspendedBlock(n, idx, block);
1703 : return SUCCESS;
1704 : }
1705 :
1706 : Status BlockMemAssigner::GetContinuousMemType(const ContinuousMem &continuous_mem, uint64_t &memory_type) const {
1707 : const auto &all_outs = continuous_mem.GetContinuousNodeOut();
1708 : memory_type = RT_MEMORY_HBM;
1709 : uint64_t first_special_type = RT_MEMORY_HBM;
1710 : for (size_t i = 0U; i < all_outs.size(); ++i) {
1711 : NodeIndexIO node_index_io(all_outs.at(i).node_ptr_, all_outs.at(i).index_, kOut);
1712 : auto symbol_iter = anchor_to_symbol_.find(node_index_io.ToString());
1713 : GE_ASSERT_TRUE(symbol_iter != anchor_to_symbol_.end());
1714 : const auto symbol = symbol_iter->second;
1715 :
1716 : const auto iter = symbol_mem_reuse_info_.find(symbol);
1717 : GE_ASSERT_TRUE(iter != symbol_mem_reuse_info_.end());
1718 : if (iter->second.mem_type_ > iter->second.mem_type_) {
1719 : memory_type = iter->second.mem_type_;
1720 : }
1721 :
1722 : if (MemTypeUtils::IsMemoryTypeSpecial(static_cast<int64_t>(iter->second.mem_type_))) {
1723 : // 记录第一个特殊类型内存
1724 : if (first_special_type == RT_MEMORY_HBM) {
1725 : first_special_type = memory_type;
1726 : } else {
1727 : GE_ASSERT_TRUE(first_special_type == memory_type,
1728 : "memory type conflict,"
1729 : " there are two different special memory type in one continuous memory block[%llu, %llu]",
1730 : first_special_type, memory_type);
1731 : }
1732 : }
1733 : }
1734 : return SUCCESS;
1735 : }
1736 :
1737 : /*
1738 : * begin_time: 符号最小的id
1739 : * out_time: 符号内最大的id
1740 : * end_time: 首节点的流和输出节点的流如果不一样,要计算回到首节点流的id. end_time大于等于out_time
1741 : */
1742 : Status BlockMemAssigner::GetContinuousMemLifeTime(const ContinuousMem &continuous_mem, int64_t &begin_time,
1743 : int64_t &out_time, int64_t &end_time, size_t &out_streams_cnt) const {
1744 : const auto &all_out = continuous_mem.GetContinuousNodeOut();
1745 : GE_ASSERT_TRUE(!all_out.empty());
1746 : const auto &first_node = all_out.front();
1747 : begin_time = first_node.node_ptr_->GetOpDescBarePtr()->GetId();
1748 : std::set<int64_t> streams;
1749 : for (const auto &node_out : all_out) {
1750 : begin_time = std::min(begin_time, node_out.node_ptr_->GetOpDescBarePtr()->GetId());
1751 : const auto ret = GetNodeMaxLifeBySymbol(symbol_to_anchors_, node_out.node_ptr_, node_out.index_, out_time, streams,
1752 : out_stream_edges_, GetStreamId(first_node.node_ptr_->GetOpDescBarePtr()));
1753 : end_time = std::max(end_time, ret);
1754 : }
1755 : out_streams_cnt = streams.size();
1756 : return SUCCESS;
1757 : }
1758 :
1759 : int32_t BlockMemAssigner::GetAllRefCount(const NodeIndexIO &out_node_index_io, bool &is_reuse_zero_copy) const {
1760 : int32_t ref_count = 0;
1761 : auto iter_symbol = anchor_to_symbol_.find(out_node_index_io.ToString());
1762 : if (iter_symbol == anchor_to_symbol_.end()) {
1763 : return ref_count;
1764 : }
1765 :
1766 : auto iter = symbol_to_anchors_.find(iter_symbol->second);
1767 : if (iter != symbol_to_anchors_.end()) {
1768 : for (const auto &node_index_io : iter->second) {
1769 : if (node_index_io.node_ptr_ == nullptr) {
1770 : continue;
1771 : }
1772 : is_reuse_zero_copy = (is_reuse_zero_copy && IsNodeSupportZeroCopy(node_index_io.node_));
1773 : if (node_index_io.io_type_ != kIn) {
1774 : continue;
1775 : }
1776 : if ((node_index_io.node_ptr_->GetInDataAnchor(node_index_io.index_) == nullptr) ||
1777 : (node_index_io.node_ptr_->GetInDataAnchor(node_index_io.index_)->GetPeerOutAnchor() == nullptr)) {
1778 : GELOGI("Node: %s has no input.", node_index_io.node_ptr_->GetNamePtr());
1779 : continue;
1780 : }
1781 : ref_count++;
1782 : }
1783 : GELOGD("symbol=%s, ref count is %d", out_node_index_io.ToString().c_str(), ref_count);
1784 : }
1785 : return ref_count;
1786 : }
1787 :
1788 : Status BlockMemAssigner::GetOutputTotalSizeAndOutCount(const NodePtr &n, uint32_t output_index, size_t &max_size,
1789 : size_t &no_align_size, int32_t &out_count,
1790 : bool is_separate_clean_continuous_inputs) const {
1791 : const auto node_op_desc = n->GetOpDescBarePtr();
1792 : GE_CHECK_NOTNULL(node_op_desc);
1793 : const auto out_data_anchor = n->GetOutDataAnchor(static_cast<int32_t>(output_index));
1794 : GE_CHECK_NOTNULL(out_data_anchor);
1795 : for (const auto in_anchor : out_data_anchor->GetPeerInDataAnchorsPtr()) {
1796 : auto owner_node = in_anchor->GetOwnerNodeBarePtr();
1797 : auto op_desc = owner_node->GetOpDescBarePtr();
1798 : if (op_desc == nullptr) {
1799 : continue;
1800 : }
1801 : Params *instance = Params::Instance();
1802 : GE_CHECK_NOTNULL(instance);
1803 : if (!((instance->GetTarget() == TARGET_TYPE_TINY) && (op_desc->GetType() == NETOUTPUT))) {
1804 : out_count++;
1805 : }
1806 : }
1807 :
1808 : if (!is_separate_clean_continuous_inputs) {
1809 : const auto output_op_desc = node_op_desc->MutableOutputDesc(output_index);
1810 : GE_CHECK_NOTNULL(output_op_desc);
1811 : int64_t size = 0;
1812 : GE_CHK_STATUS_RET(
1813 : MemReuseUtils::GetTensorSize(*output_op_desc, size, MemReuseUtils::IsNeedSplitSize(n, output_index)),
1814 : "Get node %s output %" PRId64 " size failed", node_op_desc->GetNamePtr(), output_index);
1815 : max_size = static_cast<size_t>(size);
1816 : GE_CHK_STATUS_RET(MemReuseUtils::GetOutputNoAlignSize(*node_op_desc, output_index, no_align_size),
1817 : "Get node_name:%s, output_index:%u no align size failed", n->GetNamePtr(), output_index);
1818 : return SUCCESS;
1819 : }
1820 :
1821 : for (const auto out_node_in_anchor : out_data_anchor->GetPeerInDataAnchorsPtr()) {
1822 : if (out_node_in_anchor == nullptr) {
1823 : continue;
1824 : }
1825 : const auto out_node = out_node_in_anchor->GetOwnerNodeBarePtr();
1826 : bool is_input_continuous = MemLayoutConflictUtil::IsContinuousInput(out_node);
1827 : if (!is_input_continuous) {
1828 : continue;
1829 : }
1830 : size_t total_size = 0U;
1831 : for (const auto input_anchor : out_node->GetAllInDataAnchorsPtr()) {
1832 : GE_CHECK_NOTNULL(input_anchor);
1833 : auto in_node_out_anchor = input_anchor->GetPeerOutAnchor();
1834 : if (in_node_out_anchor == nullptr) {
1835 : continue;
1836 : }
1837 : const auto in_node = in_node_out_anchor->GetOwnerNodeBarePtr();
1838 : const auto in_op_desc = in_node->GetOpDescBarePtr();
1839 : GE_CHECK_NOTNULL(in_op_desc);
1840 : const auto output_op_desc = in_op_desc->MutableOutputDesc(in_node_out_anchor->GetIdx());
1841 : int64_t size = 0;
1842 : GE_CHK_STATUS_RET(
1843 : MemReuseUtils::GetTensorSize(*output_op_desc, size, MemReuseUtils::IsNeedSplitSize(n, output_index)),
1844 : "Get node %s out %" PRId64 " size failed", in_node->GetNamePtr(), in_node_out_anchor->GetIdx());
1845 : size_t align_size = static_cast<size_t>(size);
1846 : MemReuseUtils::AlignMemOffset(align_size);
1847 : total_size += align_size;
1848 : }
1849 : max_size = max_size < total_size ? total_size : max_size;
1850 : }
1851 : no_align_size = max_size;
1852 :
1853 : return SUCCESS;
1854 : }
1855 :
1856 : void BlockMemAssigner::CalExitSymbolNodeLifeTime(const Node *const n, uint32_t out_index, size_t &max_life_time) {
1857 : max_life_time = life_time_;
1858 : bool is_cur_node_input_continuous = false;
1859 : (void)ge::AttrUtils::GetBool(n->GetOpDescBarePtr(), ATTR_NAME_NOPADDING_CONTINUOUS_INPUT,
1860 : is_cur_node_input_continuous);
1861 : if (!is_cur_node_input_continuous) {
1862 : is_cur_node_input_continuous = MemReuseUtils::IsSeparateCleanContinuousInputNode(n);
1863 : }
1864 : const auto &out_anchor = n->GetOutDataAnchor(out_index);
1865 : if (is_cur_node_input_continuous || (out_anchor == nullptr)) {
1866 : return;
1867 : }
1868 : std::set<int64_t> streams;
1869 : for (const auto &peer_in_anchor : out_anchor->GetPeerInDataAnchors()) {
1870 : if (peer_in_anchor == nullptr) {
1871 : continue;
1872 : }
1873 : const auto &out_node = peer_in_anchor->GetOwnerNodeBarePtr();
1874 : bool is_input_continuous = false;
1875 : (void)ge::AttrUtils::GetBool(out_node->GetOpDescBarePtr(), ATTR_NAME_NOPADDING_CONTINUOUS_INPUT,
1876 : is_input_continuous);
1877 : if (!is_input_continuous) {
1878 : is_input_continuous = MemReuseUtils::IsSeparateCleanContinuousInputNode(out_node);
1879 : }
1880 : if (!is_input_continuous) {
1881 : continue;
1882 : }
1883 : if (peer_in_anchor->GetIdx() != 0) {
1884 : continue;
1885 : }
1886 : int64_t node_max_life_time = 0;
1887 : CalNodeAsContinuousInputMaxLife(n, out_index, out_node, node_max_life_time, streams);
1888 : if (max_life_time < static_cast<size_t>(node_max_life_time)) {
1889 : max_life_time = static_cast<size_t>(node_max_life_time);
1890 : }
1891 : }
1892 : GELOGI("Node[%s:%u] has exit symbol, it's max_life_time:%zu stream count:%zu", n->GetNamePtr(), out_index,
1893 : max_life_time, streams.size());
1894 : }
1895 :
1896 : MemoryBlock *BlockMemAssigner::ApplyOutMemory(const NodePtr &n, uint32_t index, const std::vector<int64_t> &ranges,
1897 : const bool is_op_reuse_mem, const bool out_node_need_continuous_input) {
1898 : if (index >= n->GetAllOutDataAnchorsSize()) {
1899 : GELOGE(FAILED, "[Check][OutIndex]index:%u exceed out_size:%u, node_name:%s", index, n->GetAllOutDataAnchorsSize(),
1900 : n->GetNamePtr());
1901 : return nullptr;
1902 : }
1903 : const auto out_data_anchor = n->GetOutDataAnchor(index);
1904 : auto node_op_desc = n->GetOpDescBarePtr();
1905 : if ((out_data_anchor == nullptr) || (node_op_desc == nullptr)) {
1906 : GELOGE(FAILED, "[Check][OutAnchor]is null, index:%u, node_name:%s", index, n->GetNamePtr());
1907 : return nullptr;
1908 : }
1909 :
1910 : size_t size = 0U;
1911 : size_t no_align_size = 0U;
1912 : int32_t out_count = 0;
1913 : size_t block_size = 0U;
1914 : if (GetOutputTotalSizeAndOutCount(n, index, size, no_align_size, out_count, is_separate_clean_continuous_inputs_) !=
1915 : SUCCESS) {
1916 : GELOGE(FAILED, "Get output total size failed");
1917 : return nullptr;
1918 : }
1919 :
1920 : std::string symbol;
1921 : MemoryBlock *block = nullptr;
1922 : NodeIndexIO node_index_io(n.get(), index, kOut);
1923 : if (IsSymbolExist(node_index_io, symbol, block)) {
1924 : GE_IF_BOOL_EXEC(block == nullptr,
1925 : REPORT_INNER_ERR_MSG("E19999", "get ref block failed, node_name:%s, symbol:%s",
1926 : node_op_desc->GetNamePtr(), node_index_io.ToString().c_str());
1927 : GELOGE(FAILED, "[Get][RefBlock]node_name:%s, symbol:%s", node_op_desc->GetNamePtr(),
1928 : node_index_io.ToString().c_str());
1929 : return nullptr);
1930 :
1931 : const bool cal_theory_size =
1932 : (!block->RealSizeList().empty()) && (block->NodeTypeIndexList().back().node_ != nullptr) &&
1933 : (block->NodeTypeIndexList().back().node_->GetOpDescBarePtr() != nullptr) &&
1934 : (block->NodeTypeIndexList().back().node_->GetOpDescBarePtr()->GetType() == ge::PARTITIONEDCALL) &&
1935 : (node_op_desc->GetType() != ge::PARTITIONEDCALL);
1936 : if (cal_theory_size) {
1937 : AddMemoryStat(block->memory_type_, block->RealSizeList().back(), block->reuse_mem_);
1938 : }
1939 :
1940 : block_size = GetBlockSize(size, ranges, reuse_strategy_.use_range_);
1941 : block->SetSize(block_size);
1942 : size_t symbol_life_time = life_time_;
1943 : CalExitSymbolNodeLifeTime(node_index_io.node_ptr_, node_index_io.index_, symbol_life_time);
1944 : block->SetSymbolLifeEnd(symbol_life_time);
1945 : block->SetLifeTimeEnd(life_time_, block->stream_id_);
1946 : block->AddNodeTypeIndex({n.get(), OpMemoryType::kOutput, index, true, life_begin_, GetStreamId(node_op_desc), false,
1947 : block->GetSymbolLifeEnd()},
1948 : size, no_align_size, block->stream_id_);
1949 : block->has_sub_graph_in_out_node_ = block->has_sub_graph_in_out_node_ ||
1950 : MemReuseUtils::PeerIsSubGraphNetOutNode(n, out_data_anchor, compute_graph_) ||
1951 : MemReuseUtils::IsSubGraphInOrOutNode(n.get(), compute_graph_);
1952 : bool no_reuse_flag = false;
1953 : (void)ge::AttrUtils::GetBool(node_op_desc, kOpNoReuseMem, no_reuse_flag);
1954 : block->reuse_mem_ = block->reuse_mem_ && (!no_reuse_flag) && is_op_reuse_mem;
1955 : if (out_count == 0) {
1956 : block->ref_count_++;
1957 : }
1958 : } else {
1959 : // if ref input is variable or const(not alloc memory in reuse), cannot find ref block, must judge alone
1960 : // after unfolding dynamic shape graph, const or variable may be in root graph and cannot be ref.
1961 : if (IsOutputIndexRef(node_op_desc, index) ||
1962 : (IsSubgraphDataRefConstInput(n) && (!MemReuseUtils::IsDirectInputNode(n.get(), compute_graph_)))) {
1963 : zero_memory_list_.emplace_back(n.get(), OpMemoryType::kOutput, index, false);
1964 : GELOGI("ref mode skip out block assign. node_name: %s, index:%d", n->GetNamePtr(), index);
1965 : return nullptr;
1966 : }
1967 :
1968 : size_t max_size = size;
1969 : auto iter = symbol_mem_reuse_info_.find(symbol);
1970 : // In separate clean policy, node as continuous input, its output takes the largest continuous input size of its
1971 : // output nodes, which has been calculated above
1972 : if (iter != symbol_mem_reuse_info_.end()) {
1973 : if (!is_separate_clean_continuous_inputs_) {
1974 : max_size = iter->second.size_;
1975 : } else {
1976 : iter->second.size_ = max_size;
1977 : }
1978 : }
1979 :
1980 : uint64_t memory_type = RT_MEMORY_HBM;
1981 : if (iter != symbol_mem_reuse_info_.cend()) {
1982 : memory_type = iter->second.mem_type_;
1983 : }
1984 :
1985 : block_size = GetBlockSize(max_size, ranges, reuse_strategy_.use_range_);
1986 : std::vector<bool> workspace_reuse_flag;
1987 : bool as_input_continuous = is_separate_clean_continuous_inputs_ ? false : out_node_need_continuous_input;
1988 : bool is_zeor_copy = IsZeroCopyBlock(n, index, out_node_need_continuous_input, no_align_size);
1989 : ApplyMemoryParam param = {block_size, max_size, no_align_size, OpMemoryType::kOutput,
1990 : index, is_op_reuse_mem, as_input_continuous, memory_type,
1991 : is_zeor_copy};
1992 : block = ApplyMemory(n, workspace_reuse_flag, param);
1993 : GE_CHECK_NOTNULL_EXEC(block, return nullptr);
1994 : block->need_same_offset_in_batch_ = SizeIndependentOfBatch(n->GetTypePtr());
1995 : if (is_separate_clean_continuous_inputs_) {
1996 : // hccl task need align header and tail
1997 : block->SetFirstContinuousBlock();
1998 : block->SetLastContinuousBlock();
1999 : }
2000 : // Data and netoutput need zero copy block
2001 : block->is_zero_copy_ =
2002 : block->is_zero_copy_ ||
2003 : IsZeroCopyBlock(n, index, (out_node_need_continuous_input || block->GetContinuousFlag()), no_align_size);
2004 :
2005 : bool is_reuse_zero_copy = true;
2006 : int32_t ref_count = GetAllRefCount(node_index_io, is_reuse_zero_copy);
2007 : block->is_reuse_zero_copy_ = (block->is_reuse_zero_copy_) && (is_reuse_zero_copy);
2008 : // in case symbol ref ref_count is total input ref
2009 : if (ref_count > out_count) {
2010 : out_count = ref_count;
2011 : }
2012 : block->ref_count_ = block->ref_count_ + out_count;
2013 :
2014 : if (iter != symbol_mem_reuse_info_.cend()) {
2015 : block->is_fixed_addr_prior_ = (block->is_fixed_addr_prior_ || iter->second.is_fixed_addr_prior_);
2016 : }
2017 :
2018 : GELOGD("Node name: %s size:%zu ref count: %d, out count: %d zero copy:%d, fixed addr prior: %d", n->GetNamePtr(),
2019 : block_size, block->ref_count_, out_count, block->is_zero_copy_, block->is_fixed_addr_prior_);
2020 : }
2021 :
2022 : MarkReuseZeroCopyBlockFlag(n, block, index);
2023 : MarkZeroCopyBlockAttr(bool_attr_, node_op_desc, block->is_zero_copy_, OpMemoryType::kOutput, index);
2024 : GELOGI("Node name: %s index:%u size:%zu ref count: %d, out count: %d zero copy:%d, out node need continuous input %d",
2025 : n->GetNamePtr(), index, block_size, block->ref_count_, out_count, block->is_zero_copy_,
2026 : out_node_need_continuous_input);
2027 : return block;
2028 : }
2029 :
2030 : MemoryBlock *BlockMemAssigner::ApplyOutDescMemory(const NodePtr &n, uint32_t index,
2031 : const std::vector<int64_t> &ranges) {
2032 : GELOGI("Node[%s] tensor[%u] apply output desc memory.", n->GetNamePtr(), index);
2033 : size_t size = sizeof(RuntimeTensorDesc);
2034 : auto block_size = GetBlockSize(size, ranges, reuse_strategy_.use_range_);
2035 : std::vector<bool> workspace_reuse_flag;
2036 :
2037 : MemoryBlock *block = nullptr;
2038 : NodeIndexIO node_index_io(n.get(), index, kOut);
2039 : std::string symbol;
2040 : if (IsSymbolDescBlockExist(node_index_io, symbol, block)) {
2041 : GE_IF_BOOL_EXEC(
2042 : block == nullptr, REPORT_INNER_ERR_MSG("E19999", "get ref block failed, node_name:%s, symbol:%s",
2043 : n->GetNamePtr(), node_index_io.ToString().c_str());
2044 : GELOGE(FAILED, "[Get][RefBlock]node_name:%s, symbol:%s", n->GetNamePtr(), node_index_io.ToString().c_str());
2045 : return nullptr);
2046 :
2047 : block->AddNodeTypeIndex({n.get(), OpMemoryType::kOutputDesc, index, true, 0}, size, size,
2048 : GetStreamId(n->GetOpDescBarePtr()));
2049 : GELOGD("Ref tensor desc, symbol[%s], anchor[%s].", symbol.c_str(), node_index_io.ToString().c_str());
2050 : } else {
2051 : ApplyMemoryParam param = {block_size, size, size, OpMemoryType::kOutputDesc, index, false,
2052 : false, RT_MEMORY_HBM, false};
2053 : block = ApplyMemory(n, workspace_reuse_flag, param);
2054 : if (block == nullptr) {
2055 : REPORT_INNER_ERR_MSG("E19999", "apply out desc Memory failed, node_name:%s, block_size:%" PRId64 ", out_index:%u",
2056 : n->GetNamePtr(), block_size, index);
2057 : GELOGE(FAILED, "[Apply][Memory]node_name:%s, block_size:%" PRId64 ", out_index:%u", n->GetNamePtr(), block_size,
2058 : index);
2059 : return nullptr;
2060 : }
2061 :
2062 : bool is_fixed_addr_prior = false;
2063 : (void)ge::AttrUtils::GetBool(n->GetOpDesc(), ATTR_NAME_IS_FIXED_ADDR_PRIOR, is_fixed_addr_prior);
2064 : block->is_fixed_addr_prior_ = (block->is_fixed_addr_prior_ || is_fixed_addr_prior);
2065 : GELOGD("%s's output desc memory fixed addr prior:%d, index:%zu.", n->GetNamePtr(), block->is_fixed_addr_prior_,
2066 : index);
2067 : }
2068 : auto iter = anchor_to_symbol_.find(node_index_io.ToString());
2069 : if (iter != anchor_to_symbol_.end()) {
2070 : GELOGD("Add to ref tensor, symbol[%s], anchor[%s] ", iter->second.c_str(), node_index_io.ToString().c_str());
2071 : symbol_desc_blocks_[iter->second] = block;
2072 : }
2073 :
2074 : return block;
2075 : }
2076 :
2077 : // atomic out memory will be reassigned
2078 : bool BlockMemAssigner::IsAtomicOutputMemory(const ge::NodePtr &node, uint32_t output_index, bool is_atomic,
2079 : bool out_node_set_continuous_input) const {
2080 : auto op_desc = node->GetOpDescBarePtr();
2081 : if (op_desc == nullptr) {
2082 : return false;
2083 : }
2084 :
2085 : // if node need continue output, need assign memory.
2086 : bool is_output_continuous = ge::MemLayoutConflictUtil::IsContinuousOutput(node);
2087 : if (!is_output_continuous) {
2088 : (void)ge::AttrUtils::GetBool(op_desc, ge::ATTR_NAME_NOPADDING_CONTINUOUS_OUTPUT, is_output_continuous);
2089 : }
2090 :
2091 : if ((!out_node_set_continuous_input) && is_atomic && (!is_output_continuous)) {
2092 : if (IsZeroCopyBlock(node, output_index, is_output_continuous)) {
2093 : GELOGI("atomic clean and zero copy, need assign memory, node:%s(%s), output_index: %u", node->GetNamePtr(),
2094 : node->GetTypePtr(), output_index);
2095 : return false;
2096 : }
2097 : std::vector<int64_t> atomic_output_index;
2098 : // If GetListInt fail, atomic_output_index is empty.
2099 : (void)ge::AttrUtils::GetListInt(op_desc, ATOMIC_ATTR_OUTPUT_INDEX, atomic_output_index);
2100 : for (auto &index : atomic_output_index) {
2101 : if (static_cast<uint32_t>(index) == output_index) {
2102 : if (node->GetOwnerComputeGraphBarePtr() != nullptr) {
2103 : GELOGD("Atomic no assign %s name[%s] output[%" PRId64 "] streamid[%" PRId64 "].",
2104 : node->GetOwnerComputeGraphBarePtr()->GetName().c_str(), op_desc->GetNamePtr(), index,
2105 : GetStreamId(op_desc));
2106 : }
2107 : return true;
2108 : }
2109 : }
2110 : }
2111 : return false;
2112 : }
2113 :
2114 : void BlockMemAssigner::ReleaseMemory(MemoryBlock *const to_release, std::vector<MemoryBlock *> &reusable_memory,
2115 : int64_t stream_id, const std::string &symbol, bool no_release) {
2116 : if ((to_release == nullptr) || to_release->NodeTypeIndexList().empty()) {
2117 : GELOGE(FAILED, "[Check][Param] Input parameter to_release is null.");
2118 : return;
2119 : }
2120 : if (to_release->ref_count_ <= 0) {
2121 : GELOGI("[Check][Param] to_release->ref_count_ must greater than 0");
2122 : return;
2123 : }
2124 :
2125 : if (!to_release->reuse_mem_) {
2126 : GELOGI("[Check][Param] doesn't reuse memory");
2127 : return;
2128 : }
2129 : int64_t max_life_time = life_time_;
2130 : if (!to_release->used_by_diff_streams_) {
2131 : to_release->used_by_diff_streams_ = (to_release->stream_id_ != stream_id) &&
2132 : (to_release->NodeTypeIndexList().back().mem_type_ != OpMemoryType::kWorkspace);
2133 : }
2134 :
2135 : int64_t max_node_life_time_by_symbol = life_time_;
2136 : if (to_release->used_by_diff_streams_) {
2137 : const auto &node_type_index_list = to_release->NodeTypeIndexList();
2138 : const auto &node_type_index_iter =
2139 : std::find_if(node_type_index_list.rbegin(), node_type_index_list.rend(),
2140 : [](const NodeTypeIndex &node_type_index) { return !node_type_index.ref_input_; });
2141 : const auto &node_type_index = *node_type_index_iter;
2142 : if (node_type_index.node_ != nullptr) {
2143 : std::set<int64_t> streams;
2144 : max_life_time = GetNodeMaxLife(symbol_to_anchors_, out_stream_edges_, node_type_index.node_,
2145 : node_type_index.index_, max_node_life_time_by_symbol, streams);
2146 : if (node_type_index_list.back().ref_input_) {
2147 : to_release->SetOutStreamCount(streams.size());
2148 : }
2149 : GELOGI("Diff stream output node:%s max life time:%" PRId64 ", back is ref: %d, streams size: %zu",
2150 : node_type_index.node_->GetNamePtr(), max_life_time, node_type_index_list.back().ref_input_,
2151 : streams.size());
2152 : if (to_release->GetFirstContinuousFlag() && to_release->GetLastContinuousFlag()) {
2153 : GetContinuousOutputMaxLifeBySymbol(node_type_index.node_, symbol_to_anchors_, max_node_life_time_by_symbol,
2154 : out_stream_edges_);
2155 : }
2156 : GELOGI("Diff stream output node:%s max life time:%" PRId64 " by symbol", node_type_index.node_->GetNamePtr(),
2157 : max_node_life_time_by_symbol);
2158 : }
2159 : }
2160 : to_release->SetOutStreamLifeTime(max_node_life_time_by_symbol, max_life_time, stream_id);
2161 :
2162 : --to_release->ref_count_;
2163 : if (to_release->ref_count_ > 0) {
2164 : return;
2165 : }
2166 : if (to_release->reuse_mem_ && (!to_release->RealSizeList().empty()) &&
2167 : (to_release->batch_label_.empty() || (to_release->batch_label_ == max_batch_label_))) {
2168 : size_t align_size = to_release->RealSizeList().back();
2169 : if (!symbol.empty()) {
2170 : const auto it_size = symbol_mem_reuse_info_.find(symbol);
2171 : if (it_size != symbol_mem_reuse_info_.cend()) {
2172 : align_size = it_size->second.size_;
2173 : }
2174 : }
2175 : MemReuseUtils::AlignMemOffset(align_size);
2176 : if (memory_stat_[to_release->memory_type_].theory_memory_size_ >= align_size) {
2177 : memory_stat_[to_release->memory_type_].theory_memory_size_ -= align_size;
2178 : }
2179 : }
2180 : SetReleaseBlockLifeEnd(to_release, stream_id);
2181 : // model net output can reuse other, but it can't be reused
2182 : if (!IsPostReuse(to_release) || no_release) {
2183 : max_life_time = ge::kMaxLifeTime;
2184 : to_release->ClearDiffStreamLifeInfo();
2185 : to_release->SetLifeTimeEnd(ge::kMaxLifeTime, to_release->stream_id_);
2186 : }
2187 : reusable_memory.emplace_back(to_release);
2188 : to_release->ClearOutStreamLifeInfo();
2189 : to_release->used_by_diff_streams_ = false;
2190 : GELOGD("Put block:%s to pool stream:%" PRId64 " max life time:%" PRId64 "", GetName(*to_release, true).c_str(),
2191 : to_release->stream_id_, max_life_time);
2192 : }
2193 :
2194 : void BlockMemAssigner::ReleaseMemorys(StreamIdToBlocks &to_releases, StreamIdToBlocks &reusable_memory) {
2195 : // [stream id][blocks]
2196 : for (auto &stream_blocks : to_releases) {
2197 : for (auto mem_block : stream_blocks.second) {
2198 : if (mem_block != nullptr) { // mem_block 不可能为空
2199 : const bool output = (!mem_block->NodeTypeIndexList().empty()) &&
2200 : (mem_block->NodeTypeIndexList().back().mem_type_ == OpMemoryType::kOutput) &&
2201 : (!mem_block->SymbolList().empty());
2202 : ReleaseMemory(mem_block, reusable_memory[stream_blocks.first], mem_block->stream_id_,
2203 : output ? mem_block->SymbolList().back() : "", false);
2204 : }
2205 : }
2206 : GELOGD("Clear stream:%" PRId64 " workspace blocks", stream_blocks.first);
2207 : stream_blocks.second.clear();
2208 : }
2209 : }
2210 :
2211 : void BlockMemAssigner::ReleaseInputNodeOutMemory(const NodePtr &node) {
2212 : for (const auto &in_anchor : GetSortAllInDataAnchors(node, IsMemoryPriorityMode())) {
2213 : if ((node->GetOpDescBarePtr() == nullptr) || (in_anchor->GetPeerOutAnchor() == nullptr) ||
2214 : (in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr()->GetOpDescBarePtr() == nullptr)) {
2215 : continue;
2216 : }
2217 : GE_IF_BOOL_EXEC(IsOutputBlock(in_anchor), continue);
2218 :
2219 : std::string op_type(in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr()->GetTypePtr());
2220 : GE_IF_BOOL_EXEC((op_type == CONSTANT) || (op_type == FASTRCNNPREDICTIONS) || (op_type == CONSTANTOP), continue);
2221 : const bool is_no_release_node_out_block =
2222 : MemReuseUtils::IsNoReleaseNodeOutBlock(in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr());
2223 :
2224 : const auto in_data_node = in_anchor->GetPeerOutAnchor()->GetOwnerNodeBarePtr();
2225 : GE_CHECK_NOTNULL_JUST_RETURN(in_data_node);
2226 : const int32_t in_data_node_out_index = in_anchor->GetPeerOutAnchor()->GetIdx();
2227 : NodeIndexIO node_index_io(in_data_node, in_data_node_out_index, kOut);
2228 : MemoryBlock *block = nullptr;
2229 : std::string symbol;
2230 : if (!IsSymbolExist(node_index_io, symbol, block)) {
2231 : GELOGI(
2232 : "Block of peer out not find. Peer node:%s, output index:%d, "
2233 : "current node:%s, input index:%d",
2234 : in_data_node->GetNamePtr(), in_data_node_out_index, node->GetNamePtr(), in_anchor->GetIdx());
2235 : continue;
2236 : }
2237 : GE_CHECK_NOTNULL_JUST_RETURN(block);
2238 : auto reusable_blocks_iter = reusable_blocks_.find(block->memory_type_);
2239 : if (reusable_blocks_iter == reusable_blocks_.end() || block->NodeTypeIndexList().empty()) {
2240 : continue;
2241 : }
2242 :
2243 : GELOGI(
2244 : "Block of peer out is matched. Peer node:%s, output index:%d, "
2245 : "current node:%s, input index:%d, block ref_count:%d.",
2246 : in_data_node->GetNamePtr(), in_data_node_out_index, node->GetNamePtr(), in_anchor->GetIdx(), block->ref_count_);
2247 :
2248 : auto stream_id = GetStreamId(node->GetOpDescBarePtr());
2249 : StreamIdToBlocks &reusable_memory = reusable_blocks_iter->second;
2250 : ReleaseMemory(block, reusable_memory[block->stream_id_], stream_id, symbol, is_no_release_node_out_block);
2251 : if (block->ref_count_ == 0 && (stream_id == block->stream_id_)) {
2252 : SetLastUsedInputMemAttr(node, in_anchor->GetIdx(), bool_attr_);
2253 : }
2254 : }
2255 : }
2256 : void BlockMemAssigner::CheckAndReleaseSuspendedBlock(const NodePtr &node, uint32_t idx, MemoryBlock *block) {
2257 : if ((node == nullptr) || (block == nullptr)) {
2258 : return;
2259 : }
2260 : if (block->ref_count_ == 0) {
2261 : block->ref_count_ = 1;
2262 : stream_workspace_blocks_[block->memory_type_][block->stream_id_].emplace_back(block);
2263 : GELOGI(
2264 : "The output is suspended, and will be released in allocation of next node. Name:%s, index:%u, "
2265 : "size:%zu, ref_count:%d, batch:%s, stream:%" PRId64 ", reuse_mem:%d.",
2266 : node->GetNamePtr(), idx, block->Size(), block->ref_count_, block->batch_label_.c_str(), block->stream_id_,
2267 : block->reuse_mem_);
2268 : }
2269 : }
2270 :
2271 : Status BlockMemAssigner::AssignOutputMemoryWithReuse(const NodePtr &node, std::vector<int64_t> &ranges) {
2272 : auto op_desc = node->GetOpDescBarePtr();
2273 : std::vector<int64_t> memorys_type;
2274 : bool has_mem_type_attr = ge::AttrUtils::GetListInt(op_desc, ATTR_NAME_OUTPUT_MEM_TYPE_LIST, memorys_type);
2275 : GELOGD("Assign memory node[%s], output size[%zu], output memory type size[%zu]", op_desc->GetNamePtr(),
2276 : op_desc->GetOutputsSize(), memorys_type.size());
2277 : if (has_mem_type_attr && (memorys_type.size() != op_desc->GetOutputsSize())) {
2278 : REPORT_INNER_ERR_MSG("E19999", "Attr[%s] size:%zu not equal to node output size:%zu, node_name:%s",
2279 : ATTR_NAME_OUTPUT_MEM_TYPE_LIST.c_str(), memorys_type.size(), op_desc->GetOutputsSize(),
2280 : op_desc->GetNamePtr());
2281 : GELOGE(INTERNAL_ERROR, "[Check][MemTypeAttr]Attr %s size:%zu not equal to node output size:%zu, node_name:%s",
2282 : ATTR_NAME_OUTPUT_MEM_TYPE_LIST.c_str(), memorys_type.size(), op_desc->GetOutputsSize(),
2283 : op_desc->GetNamePtr());
2284 : return INTERNAL_ERROR;
2285 : }
2286 :
2287 : // restore node-level flags
2288 : is_op_reuse_mem_ = !(op_reuse_env_valid_ && ((op_no_reuse_mem_set_.count(op_desc->GetNamePtr()) > 0U) ||
2289 : (op_no_reuse_mem_set_.count(op_desc->GetTypePtr()) > 0U)));
2290 :
2291 : bool need_gentask_atomic = false;
2292 : (void)ge::AttrUtils::GetBool(op_desc, "need_gentask_atomic", need_gentask_atomic);
2293 :
2294 : bool is_atomic = false;
2295 : if (!need_gentask_atomic) {
2296 : // If GetBool fail, is_atomic is false.
2297 : (void)ge::AttrUtils::GetBool(op_desc, ATOMIC_ATTR_IS_ATOMIC_NODE, is_atomic);
2298 : }
2299 :
2300 : // Allocate memory for the current node and release node memory of the same size in the workspace
2301 : GE_IF_BOOL_EXEC(
2302 : is_ge_reuse_mem_, for (auto iter = stream_workspace_blocks_.begin(); iter != stream_workspace_blocks_.end();
2303 : ++iter) { ReleaseMemorys(iter->second, reusable_blocks_[iter->first]); });
2304 : // work space is same life time, so set life_time_ after ReleaseMemorys for workspace
2305 : life_time_ = op_desc->GetId();
2306 :
2307 : bool is_zero_mem_node = CheckIsZeroMemNodeType(node->GetTypePtr());
2308 : bool is_buffer_pool_mem_supported = (op_desc->HasAttr(ATTR_NAME_BUFFER_POOL_ID)) &&
2309 : (op_desc->HasAttr(ATTR_NAME_BUFFER_POOL_SIZE)) && (!root_unknown_shape_flag_);
2310 : bool need_apply_continuous_memory = MemReuseUtils::IsContinuousOutput(node);
2311 : // begin to assign memory for every output
2312 : for (uint32_t i = 0U; i < static_cast<uint32_t>(op_desc->GetOutputsSize()); i++) {
2313 : int64_t size = 0;
2314 : auto output_tensor_desc = op_desc->MutableOutputDesc(i);
2315 : if (output_tensor_desc == nullptr) {
2316 : GELOGW("op[%s] null output_tensor_desc, index[%u].", op_desc->GetNamePtr(), i);
2317 : continue;
2318 : }
2319 :
2320 : // every out need get again
2321 : life_begin_ = op_desc->GetId();
2322 : life_end_ = 0U;
2323 : is_separate_clean_continuous_inputs_ = false;
2324 :
2325 : GE_IF_BOOL_EXEC(
2326 : MemReuseUtils::GetTensorSize(*output_tensor_desc, size, MemReuseUtils::IsNeedSplitSize(node, i)) != SUCCESS,
2327 : GELOGI("Tensor has no size"));
2328 :
2329 : // fusion: other type's size not means malloc HBM memory
2330 : if (has_mem_type_attr && ((memorys_type[i] == RT_MEMORY_L1) || (memorys_type[i] == kRtMemoryUB))) {
2331 : GELOGI("fusion: node[%s], output[%s], output memory type [%" PRId64 "]", op_desc->GetNamePtr(),
2332 : op_desc->GetOutputNameByIndex(i).c_str(), memorys_type[i]);
2333 : size = 0; // no need assign block memory
2334 : }
2335 :
2336 : GE_IF_BOOL_EXEC((TensorUtils::IsMemorySizeCalcTypeAlwaysEmpty(*output_tensor_desc)), size = 0;);
2337 :
2338 : InDataAnchor *continuous_in_anchor = nullptr;
2339 : bool out_node_set_continuous_input = false;
2340 : bool no_need_assign_memory = (is_zero_mem_node || is_buffer_pool_mem_supported || (size == 0));
2341 : if ((!no_need_assign_memory) && continuous_mem_mng_.IsFound(node.get(), i)) {
2342 : return ApplyContinuousMemWithMng(node, i, ranges);
2343 : }
2344 : NodeIndexIO node_index_io(node.get(), i, kOut);
2345 : bool is_reuse_zero_copy = true;
2346 : std::set<int64_t> streams;
2347 : if (!no_need_assign_memory) {
2348 : out_node_set_continuous_input =
2349 : IsOutNodeSetContinuousInput(node, i, continuous_in_anchor, is_reuse_zero_copy, streams);
2350 : GE_ASSERT_SUCCESS(GetNoNeedAssignMemoryFlag(node, i, no_need_assign_memory));
2351 : no_need_assign_memory =
2352 : (no_need_assign_memory || IsAtomicOutputMemory(node, i, is_atomic, out_node_set_continuous_input) ||
2353 : IsNoNeedAssignMemory(node, node_index_io, i));
2354 : }
2355 : if (no_need_assign_memory) {
2356 : zero_memory_list_.emplace_back(node.get(), OpMemoryType::kOutput, i, false);
2357 : GELOGI("node[%s] output %u, no need assign memory.", op_desc->GetNamePtr(), i);
2358 : continue;
2359 : }
2360 :
2361 : if (need_apply_continuous_memory && (!out_node_set_continuous_input)) {
2362 : return ApplyContinuousMemory(node, ranges, is_op_reuse_mem_);
2363 : }
2364 :
2365 : // atomic can't be reused
2366 : bool need_change = is_op_reuse_mem_ && is_atomic && out_node_set_continuous_input;
2367 : GE_IF_BOOL_EXEC(need_change, is_op_reuse_mem_ = false);
2368 :
2369 : MemoryBlock *mem_block = ApplyOutMemory(node, i, ranges, is_op_reuse_mem_, out_node_set_continuous_input);
2370 : if (mem_block != nullptr) {
2371 : mem_block->SetOutStreamCount(streams.size());
2372 : mem_block->is_reuse_zero_copy_ = (is_reuse_zero_copy && mem_block->is_reuse_zero_copy_);
2373 : if (continuous_in_anchor != nullptr) {
2374 : const auto continuous_op_desc = continuous_in_anchor->GetOwnerNodeBarePtr()->GetOpDescBarePtr();
2375 : node_continuous_input_blocks_[continuous_op_desc->GetId()][continuous_in_anchor->GetIdx()] = mem_block;
2376 : }
2377 : auto iter = anchor_to_symbol_.find(node_index_io.ToString());
2378 : if (iter != anchor_to_symbol_.end()) {
2379 : symbol_blocks_[iter->second] = mem_block;
2380 : GELOGD("Node io:%s add symbol:%s block:%s", node_index_io.ToString().c_str(), iter->second.c_str(),
2381 : GetName(*mem_block).c_str());
2382 : // The output is suspended, and will be released in allocation of next node.
2383 : CheckAndReleaseSuspendedBlock(node, i, mem_block);
2384 : }
2385 : }
2386 :
2387 : bool is_tensor_desc_mem = false;
2388 : (void)AttrUtils::GetBool(output_tensor_desc, ATTR_NAME_TENSOR_NO_TILING_MEM_TYPE, is_tensor_desc_mem);
2389 : if (is_tensor_desc_mem) {
2390 : MemoryBlock *out_desc_mem_block = ApplyOutDescMemory(node, i, ranges);
2391 : GE_CHECK_NOTNULL(out_desc_mem_block);
2392 : }
2393 : }
2394 : return SUCCESS;
2395 : }
2396 :
2397 : Status BlockMemAssigner::AssignWorkSpaceMemoryWithReuse(const NodePtr &node, std::vector<int64_t> &ranges) {
2398 : auto node_op_desc = node->GetOpDescBarePtr();
2399 : std::vector<int64_t> temp;
2400 : int64_t tatal_size = 0;
2401 : GetNodeWorkSpaceSize(node, temp, tatal_size);
2402 :
2403 : std::vector<int64_t> workspace_type_list;
2404 : const bool has_workspace_type_list_attr =
2405 : ge::AttrUtils::GetListInt(node_op_desc, ATTR_NAME_WORKSPACE_TYPE_LIST, workspace_type_list);
2406 :
2407 : std::vector<int64_t> tvm_workspace_types;
2408 : const bool has_tvm_workspace_mem_type_attr =
2409 : ge::AttrUtils::GetListInt(node_op_desc, TVM_ATTR_NAME_WORKSPACE_TYPE, tvm_workspace_types);
2410 :
2411 : std::vector<int32_t> workspace_no_reuse_scope;
2412 : const bool has_workspace_no_reuse_scope =
2413 : ge::AttrUtils::GetListInt(node_op_desc, ATTR_NAME_WORKSPACE_MEMORY_NO_REUSE_SCOPE, workspace_no_reuse_scope);
2414 :
2415 : std::vector<bool> workspace_reuse_flag;
2416 : GE_IF_BOOL_EXEC(!ge::AttrUtils::GetListBool(node_op_desc, kAttrNameWorkspaceReuseFlag, workspace_reuse_flag),
2417 : GELOGD("OP %s does not have workspace_reuse_flag attr", node_op_desc->GetNamePtr()));
2418 : GELOGD("Assign memory node[%s], size [temp:%zu, tvm:%zu, list:%zu, no_reuse_scopes:%zu, reuse_flags:%zu.]",
2419 : node_op_desc->GetNamePtr(), temp.size(), tvm_workspace_types.size(), workspace_type_list.size(),
2420 : workspace_no_reuse_scope.size(), workspace_reuse_flag.size());
2421 :
2422 : if (((has_tvm_workspace_mem_type_attr) && (temp.size() != tvm_workspace_types.size())) ||
2423 : ((has_workspace_type_list_attr) && (temp.size() != workspace_type_list.size()))) {
2424 : REPORT_INNER_ERR_MSG("E19999",
2425 : "Attr:%s, memory_type.size:%zu and %s, memory_type.size:%zu and workspaces "
2426 : "num:%zu should be same, node_name:%s, check invalid",
2427 : TVM_ATTR_NAME_WORKSPACE_TYPE.c_str(), tvm_workspace_types.size(),
2428 : ATTR_NAME_WORKSPACE_TYPE_LIST.c_str(), workspace_type_list.size(), temp.size(),
2429 : node->GetNamePtr());
2430 : GELOGE(INTERNAL_ERROR,
2431 : "[Check][Param] Attr:%s, memory_type.size:%zu and %s, memory_type.size:%zu and workspaces "
2432 : "num:%zu should be same, node_name:%s, check invalid",
2433 : TVM_ATTR_NAME_WORKSPACE_TYPE.c_str(), tvm_workspace_types.size(), ATTR_NAME_WORKSPACE_TYPE_LIST.c_str(),
2434 : workspace_type_list.size(), temp.size(), node->GetNamePtr());
2435 : return INTERNAL_ERROR;
2436 : }
2437 : bool need_gentask_atomic = false;
2438 : (void)ge::AttrUtils::GetBool(node_op_desc, "need_gentask_atomic", need_gentask_atomic);
2439 : auto atomic_workspace_info =
2440 : node_op_desc->TryGetExtAttr(EXT_ATTR_ATOMIC_WORKSPACE_INFO, std::map<std::string, std::map<int64_t, int64_t>>{});
2441 : for (size_t i = 0UL; i < temp.size(); i++) {
2442 : // workspace's life time is self node
2443 : life_begin_ = node_op_desc->GetId();
2444 : life_end_ = 0U;
2445 : // fusion: other type's size not means malloc HBM memory
2446 : bool workspace_skip_flag = false;
2447 : if (has_tvm_workspace_mem_type_attr &&
2448 : ((tvm_workspace_types[i] == RT_MEMORY_L1) || (tvm_workspace_types[i] == kRtMemoryUB))) {
2449 : GELOGI(
2450 : "fusion:node[%s]workspace index[%zu] is not hbm type, add to zero_memory_list, workspace memory type "
2451 : "[%" PRId64 "]",
2452 : node_op_desc->GetNamePtr(), i, tvm_workspace_types[i]);
2453 : workspace_skip_flag = true;
2454 : }
2455 : if (temp[i] == 0 || workspace_skip_flag ||
2456 : (!need_gentask_atomic && MemReuseUtils::IsAtomicWorkSpace(static_cast<int64_t>(i), atomic_workspace_info))) {
2457 : zero_memory_list_.emplace_back(node.get(), OpMemoryType::kWorkspace, static_cast<uint32_t>(i), false);
2458 : continue;
2459 : }
2460 :
2461 : const bool session_scope_memory =
2462 : (has_workspace_no_reuse_scope) && (i < workspace_no_reuse_scope.size()) &&
2463 : (static_cast<MemoryNoReuseScope>(workspace_no_reuse_scope[i]) == MemoryNoReuseScope::kSessionNoReuse);
2464 : const bool is_p2p_memory =
2465 : (has_workspace_type_list_attr) && (static_cast<uint64_t>(workspace_type_list[i]) == RT_MEMORY_P2P_DDR);
2466 : uint64_t memory_type = GetWorkSpaceMemoryType(workspace_no_reuse_scope.size(), i, is_p2p_memory,
2467 : session_scope_memory, workspace_reuse_flag);
2468 : GELOGI("%s's workspace mem_type:%lu, index:%zu.", node->GetNamePtr(), memory_type, i);
2469 : ApplyMemoryParam param = {GetBlockSize(static_cast<size_t>(temp[i]), ranges, reuse_strategy_.use_range_),
2470 : static_cast<size_t>(temp[i]),
2471 : static_cast<size_t>(temp[i]),
2472 : OpMemoryType::kWorkspace,
2473 : static_cast<uint32_t>(i),
2474 : is_op_reuse_mem_,
2475 : false,
2476 : memory_type,
2477 : false};
2478 : MemoryBlock *mem_block = ApplyMemory(node, workspace_reuse_flag, param);
2479 : GE_CHECK_NOTNULL_EXEC(mem_block, continue);
2480 : mem_block->is_reuse_zero_copy_ = (mem_block->is_reuse_zero_copy_) && (IsNodeSupportZeroCopy(node));
2481 :
2482 : bool is_fixed_addr_prior = false;
2483 : (void)ge::AttrUtils::GetBool(node_op_desc, ATTR_NAME_IS_FIXED_ADDR_PRIOR, is_fixed_addr_prior);
2484 : mem_block->is_fixed_addr_prior_ = (mem_block->is_fixed_addr_prior_ || is_fixed_addr_prior);
2485 : GELOGI("%s's workspace fixed addr prior:%d, index:%zu.", node->GetNamePtr(), mem_block->is_fixed_addr_prior_, i);
2486 :
2487 : ++(mem_block->ref_count_);
2488 : CheckWorkspaceReuse(workspace_reuse_flag, i, GetStreamId(node_op_desc), mem_block, memory_type);
2489 : }
2490 : return SUCCESS;
2491 : }
2492 :
2493 : void BlockMemAssigner::ParseGraphIoAllocMode() {
2494 : // todo:临时方案,增加option控制静态子图hccl地址不支持刷新,待HCCL 1230正式方案上库后删除
2495 : constexpr const char_t *kStaticModelAddrFixed = "ge.exec.static_model_addr_fixed";
2496 : std::string is_addr_fixed_opt;
2497 : (void)ge::GetContext().GetOption(kStaticModelAddrFixed, is_addr_fixed_opt);
2498 : is_static_model_addr_fixed_ = !is_addr_fixed_opt.empty();
2499 :
2500 : if ((compute_graph_ == nullptr) || (compute_graph_->GetParentGraph() != nullptr)) {
2501 : return;
2502 : }
2503 :
2504 : std::string alloc_mode;
2505 : (void)ge::GetContext().GetOption(OPTION_GRAPH_IO_MEM_ALLOC_MODE, alloc_mode);
2506 : is_io_alloc_by_ge_in_run_graph_ = (alloc_mode == "ByGE");
2507 : GELOGI("io_alloc_mode:%s, graph:%s.", (is_io_alloc_by_ge_in_run_graph_ ? "ByGE" : "ByApp"),
2508 : compute_graph_->GetName().c_str());
2509 : }
2510 :
2511 : Status BlockMemAssigner::InitIoReuseFlag() {
2512 : GE_ASSERT_NOTNULL(compute_graph_);
2513 : const auto root_graph = GraphUtils::FindRootGraph(compute_graph_);
2514 : GE_ASSERT_NOTNULL(root_graph);
2515 :
2516 : // 动态shape静态子图输出内存复用
2517 : if (root_graph->GetGraphUnknownFlag()) {
2518 : const auto netoutput_node = compute_graph_->FindFirstNodeMatchType(NETOUTPUT);
2519 : GE_ASSERT_NOTNULL(netoutput_node);
2520 : const auto &netoutput_op_desc = netoutput_node->GetOpDesc();
2521 : GE_ASSERT_NOTNULL(netoutput_op_desc);
2522 : const size_t inputs_size = netoutput_op_desc->GetAllInputsSize();
2523 : output_index_to_reuse_mem_flag_.resize(inputs_size, true);
2524 : return SUCCESS;
2525 : }
2526 : ParseIoReuseMemOption();
2527 : return SUCCESS;
2528 : }
2529 :
2530 : void BlockMemAssigner::ParseIoReuseMemOption() {
2531 : if ((compute_graph_ == nullptr) || (compute_graph_->GetParentGraph() != nullptr)) {
2532 : return;
2533 : }
2534 :
2535 : const auto &graph_option = GetThreadLocalContext().GetAllGraphOptions();
2536 : const auto it_input_indexes = graph_option.find(OPTION_INPUT_REUSE_MEM_INDEXES);
2537 : if (it_input_indexes != graph_option.end()) {
2538 : GELOGI("[io_reuse_mem_option] option_input_indexes:%s", it_input_indexes->second.c_str());
2539 : const size_t inputs_size = compute_graph_->GetInputNodes().size();
2540 : input_index_to_reuse_mem_flag_.resize(inputs_size, false);
2541 : std::vector<std::string> in_index_vec;
2542 : SplitStringByComma(it_input_indexes->second, in_index_vec);
2543 : for (const auto &str : in_index_vec) {
2544 : const int32_t in_index = std::stoi(str);
2545 : if ((in_index < 0) || (static_cast<size_t>(in_index) >= inputs_size)) {
2546 : GELOGW(
2547 : "[Check][Option]Check failed because option(%s=%s) is invalid. Input_index must be in the "
2548 : "range of [0, %zu)",
2549 : OPTION_INPUT_REUSE_MEM_INDEXES, it_input_indexes->second.c_str(), inputs_size);
2550 : continue;
2551 : }
2552 : input_index_to_reuse_mem_flag_[in_index] = true;
2553 : }
2554 : }
2555 :
2556 : const auto it_output_indexes = graph_option.find(OPTION_OUTPUT_REUSE_MEM_INDEXES);
2557 : if (it_output_indexes != graph_option.end()) {
2558 : GELOGI("[io_reuse_mem_option] option_output_indexes:%s", it_output_indexes->second.c_str());
2559 : const auto netoutput_node = compute_graph_->FindFirstNodeMatchType(NETOUTPUT);
2560 : GE_CHECK_NOTNULL_EXEC(netoutput_node, return);
2561 : const auto &netoutput_op_desc = netoutput_node->GetOpDesc();
2562 : GE_CHECK_NOTNULL_EXEC(netoutput_op_desc, return);
2563 : const size_t inputs_size = netoutput_op_desc->GetAllInputsSize();
2564 : output_index_to_reuse_mem_flag_.resize(inputs_size, false);
2565 : std::vector<std::string> out_index_vec;
2566 : SplitStringByComma(it_output_indexes->second, out_index_vec);
2567 : for (const auto &str : out_index_vec) {
2568 : const int32_t out_index = std::stoi(str);
2569 : if ((out_index < 0) || (static_cast<size_t>(out_index) >= inputs_size)) {
2570 : GELOGW(
2571 : "[Check][Option]Check failed because option(%s=%s) is invalid. Output_index must be in the "
2572 : "range of [0, %zu)",
2573 : OPTION_OUTPUT_REUSE_MEM_INDEXES, it_output_indexes->second.c_str(), inputs_size);
2574 : continue;
2575 : }
2576 : output_index_to_reuse_mem_flag_[out_index] = true;
2577 : }
2578 : }
2579 :
2580 : return;
2581 : }
2582 :
2583 : /// @ingroup domi
2584 : /// @brief traverse all nodes outputs and workspace in need, apply memory block considering memory reuse
2585 : /// @param [in/out] ranges memory size provided
2586 : /// @return Status result
2587 : Status BlockMemAssigner::AssignMemoryWithReuse(std::vector<int64_t> &ranges) {
2588 : // init global flags
2589 : std::string ge_disable_reuse_mem;
2590 : (void)ge::GetContext().GetOption(OPTION_EXEC_DISABLE_REUSED_MEMORY, ge_disable_reuse_mem);
2591 : GEEVENT("Reuse memory %s, memory_priority_mode is %s.", ge_disable_reuse_mem == "1" ? "close" : "open",
2592 : memory_priority_mode_ ? "true" : "false");
2593 : is_ge_reuse_mem_ = (ge_disable_reuse_mem != "1");
2594 :
2595 : const char_t *op_no_reuse_mem = nullptr;
2596 : MM_SYS_GET_ENV(MM_ENV_OP_NO_REUSE_MEM, op_no_reuse_mem);
2597 : if (op_no_reuse_mem != nullptr) {
2598 : std::string op_no_reuse_mem_str = op_no_reuse_mem;
2599 : CheckAndGetOpReuseEnv(op_no_reuse_mem_str, op_no_reuse_mem_set_, op_reuse_env_valid_);
2600 : }
2601 :
2602 : auto root_graph = GraphUtils::FindRootGraph(compute_graph_);
2603 : GE_ASSERT_NOTNULL(root_graph, "[Check][RootGraph]Root graph is nullptr, graph:%s.",
2604 : compute_graph_->GetName().c_str());
2605 : root_unknown_shape_flag_ = root_graph->GetGraphUnknownFlag();
2606 :
2607 : (void)AttrUtils::GetBool(compute_graph_, ATTR_NAME_MEM_RELEASE_FIRST_REUSE_FIRST,
2608 : reuse_strategy_.reuse_first_release_);
2609 : if (reuse_strategy_.reuse_first_release_) {
2610 : GELOGI("The block usage strategy: first release, first reuse.");
2611 : } else {
2612 : GELOGI("The block usage strategy: first release, last reuse.");
2613 : }
2614 :
2615 : // assign memory for every op
2616 : for (NodePtr &n : compute_graph_->GetAllNodes()) {
2617 : auto node_op_desc = n->GetOpDescBarePtr();
2618 : GE_IF_BOOL_EXEC(node_op_desc == nullptr, continue);
2619 : GE_ASSERT_SUCCESS(AssignOutputMemoryWithReuse(n, ranges), "node: %s(%s) assign output memory failed.",
2620 : n->GetNamePtr(), n->GetTypePtr());
2621 :
2622 : GE_ASSERT_SUCCESS(AssignWorkSpaceMemoryWithReuse(n, ranges), "node: %s(%s) assign workspace memory failed.",
2623 : n->GetNamePtr(), n->GetTypePtr());
2624 : ReleaseInputNodeOutMemory(n);
2625 : }
2626 :
2627 : for (const auto &block_pair : reusable_blocks_) {
2628 : memory_stat_[block_pair.first].stream_count_ = block_pair.second.size();
2629 : }
2630 :
2631 : GELOGD("Assigned memory blocks:");
2632 : PrintMemBlock();
2633 :
2634 : GE_IF_BOOL_EXEC(is_ge_reuse_mem_, ReuseBlocksByLifeTime());
2635 : AssignContinuousBlocks();
2636 : GE_ASSERT_SUCCESS(ResizeMemoryBlocks(), "resize memory block failed");
2637 :
2638 : GELOGD("Memory blocks after resize:");
2639 : PrintMemBlock();
2640 : return SUCCESS;
2641 : }
2642 :
2643 : void BlockMemAssigner::PrintMemBlock() {
2644 : for (auto mem_block : memory_blocks_) {
2645 : if (mem_block == nullptr) {
2646 : continue;
2647 : }
2648 : mem_block->SetRefLifeTimeEnd();
2649 : GELOGD("%s", mem_block->String().c_str());
2650 : }
2651 : }
2652 :
2653 : void BlockMemAssigner::CheckWorkspaceReuse(const std::vector<bool> &workspace_reuse_flag, uint32_t index,
2654 : int64_t stream_id, MemoryBlock *const mem_block, uint64_t memory_type) {
2655 : bool reuse_mem_flag = ((workspace_reuse_flag.size() > index) && (!workspace_reuse_flag[index])) ? false : true;
2656 : if (reuse_mem_flag) {
2657 : stream_workspace_blocks_[memory_type][stream_id].emplace_back(mem_block);
2658 : }
2659 : }
2660 :
2661 : void BlockMemAssigner::GetNodeWorkSpaceSize(const NodePtr &node, std::vector<int64_t> &workspace_memory,
2662 : int64_t &total_size) const {
2663 : if (node->GetOpDescBarePtr() == nullptr) {
2664 : REPORT_INNER_ERR_MSG("E19999", "param node opdesc is nullptr, check invalid.");
2665 : GELOGE(FAILED, "[Check][Param] Op desc is null.");
2666 : return;
2667 : }
2668 : std::vector<int64_t> workspace_byte_nums = node->GetOpDescBarePtr()->GetWorkspaceBytes();
2669 : for (int64_t byte_size : workspace_byte_nums) {
2670 : if ((byte_size < 0) && (byte_size != -1)) {
2671 : // 后面校验range时会返回流程失败,这里只是补充日志
2672 : GELOGE(FAILED,
2673 : "[Check][Workspace]workspace_size:%" PRId64
2674 : " is invalid, "
2675 : "maybe it is unknown shape node, Node_name:%s",
2676 : byte_size, node->GetOpDescBarePtr()->GetNamePtr());
2677 : REPORT_INNER_ERR_MSG("E19999",
2678 : "workspace_size:%" PRId64
2679 : " is invalid, "
2680 : "maybe it is unknown shape node, Node_name:%s",
2681 : byte_size, node->GetOpDescBarePtr()->GetNamePtr());
2682 : workspace_memory.emplace_back(byte_size);
2683 : return;
2684 : }
2685 : byte_size = (byte_size < 0) ? 0 : byte_size;
2686 : workspace_memory.emplace_back(byte_size);
2687 : total_size += byte_size;
2688 : GELOGI("node[%s] workspace_byte_nums:%zu, push back size:%" PRId64 "", node->GetOpDescBarePtr()->GetNamePtr(),
2689 : workspace_byte_nums.size(), byte_size);
2690 : }
2691 : }
2692 :
2693 : /// @ingroup domi
2694 : /// @brief order blocks by continuous input index
2695 : /// @param [in] blocks need be processed
2696 : /// @param [in] input blocks need continuous
2697 : /// @param [out] blocks after continuous order
2698 : /// @param [in/out] blocks ordered
2699 : /// @param [in] input or output
2700 : void ReAssignContinuousBlocks(const std::vector<MemoryBlock *> &org_blocks,
2701 : const std::map<MemoryBlock *, uint32_t> &block_map,
2702 : std::vector<MemoryBlock *> &dest_blocks, std::vector<MemoryBlock *> &continuous_blocks,
2703 : const std::string &type) {
2704 : for (auto &memory_block : org_blocks) {
2705 : if (memory_block == nullptr || memory_block->child_block_) {
2706 : continue;
2707 : }
2708 : if (block_map.find(memory_block) != block_map.end()) {
2709 : continue;
2710 : }
2711 : dest_blocks.emplace_back(memory_block);
2712 : }
2713 :
2714 : // add continuous block
2715 : std::sort(continuous_blocks.begin(), continuous_blocks.end(), CompareBlockIndex);
2716 : size_t count = 0UL;
2717 : for (auto &memory_block : continuous_blocks) {
2718 : GE_IF_BOOL_EXEC(memory_block == nullptr, continue);
2719 :
2720 : GELOGI("Block continuous %s index:%d", type.c_str(), memory_block->input_index_);
2721 : count++;
2722 : if (count == 1U) {
2723 : memory_block->SetFirstContinuousBlock();
2724 : }
2725 : if (count == continuous_blocks.size()) {
2726 : memory_block->SetLastContinuousBlock();
2727 : }
2728 : dest_blocks.emplace_back(memory_block);
2729 : }
2730 : }
2731 :
2732 : void BlockMemAssigner::AssignContinuousBlocks() {
2733 : for (const auto &block_map : node_continuous_input_blocks_) {
2734 : std::vector<MemoryBlock *> dest_memory_blocks;
2735 : std::map<MemoryBlock *, uint32_t> continuous_block_map;
2736 : std::vector<MemoryBlock *> continuous_blocks;
2737 : const auto it = node_continuous_input_counts_.find(block_map.first);
2738 : GE_IF_BOOL_EXEC(it == node_continuous_input_counts_.end(), continue);
2739 : const bool size_independent = SizeIndependentOfBatch(it->second.first);
2740 : GELOGI("Node %" PRId64 " continuous input block count:%zu input count:%u, size_independent:%d", block_map.first,
2741 : block_map.second.size(), it->second.second, static_cast<int32_t>(size_independent));
2742 : GE_IF_BOOL_EXEC(it->second.second != block_map.second.size(), continue);
2743 :
2744 : for (auto &iter : block_map.second) {
2745 : if (iter.second != nullptr) {
2746 : iter.second->need_same_offset_in_batch_ = size_independent;
2747 : continuous_block_map[iter.second] = iter.first;
2748 : iter.second->input_index_ = iter.first;
2749 : continuous_blocks.emplace_back(iter.second);
2750 : }
2751 : }
2752 : if (continuous_block_map.size() != continuous_blocks.size()) {
2753 : GELOGW("Node %" PRId64 " continuous input map size:%zu vector size:%zu", block_map.first,
2754 : continuous_block_map.size(), continuous_blocks.size());
2755 : continue;
2756 : }
2757 : ReAssignContinuousBlocks(memory_blocks_, continuous_block_map, dest_memory_blocks, continuous_blocks, "input");
2758 : memory_blocks_.swap(dest_memory_blocks);
2759 : }
2760 : }
2761 :
2762 : void BlockMemAssigner::ReuseBlocksByLifeTime() {
2763 : if (!NeedLevel2Reuse()) {
2764 : return;
2765 : }
2766 : CompareLifeInterval cmp(reuse_strategy_);
2767 : std::sort(memory_blocks_.begin(), memory_blocks_.end(), cmp);
2768 : for (size_t i = 0UL; i < memory_blocks_.size(); ++i) {
2769 : auto parent = memory_blocks_[i];
2770 : GE_IF_BOOL_EXEC((parent == nullptr || parent->child_block_), continue);
2771 : for (size_t j = i + 1; j < memory_blocks_.size(); ++j) {
2772 : auto child = memory_blocks_[j];
2773 : GE_IF_BOOL_EXEC((child == nullptr), continue);
2774 :
2775 : // If node is before atomic_addr_clean node, the continuous memory can't be reused, its out put will be cleared.
2776 : if (!child->NodeTypeIndexList().empty() && parent->GetContinuousFlag()) {
2777 : auto node = child->NodeTypeIndexList()[0].node_;
2778 : bool before_atomic_clean = ((node == nullptr) || (node->GetOpDescBarePtr() == nullptr) ||
2779 : (node->GetOpDescBarePtr()->GetId() < GetAtomicAddrCleanId()));
2780 : GE_IF_BOOL_EXEC(before_atomic_clean, continue);
2781 : }
2782 : std::vector<MemoryBlock *> clone_blocks;
2783 : parent->AddLifeReuseBlock(this, child, clone_blocks, 0, in_stream_edges_);
2784 : GE_IF_BOOL_EXEC(clone_blocks.empty(), continue);
2785 :
2786 : // insert after this child block
2787 : memory_blocks_.insert(memory_blocks_.cbegin() + j + 1, clone_blocks.cbegin(), clone_blocks.cend());
2788 : blocks_store_.insert(blocks_store_.cend(), clone_blocks.cbegin(), clone_blocks.cend());
2789 : // if clone block's align size is less than nex block's size need sort again
2790 : size_t min_block_align_size = parent->AlignSize();
2791 : for (const auto block : clone_blocks) {
2792 : min_block_align_size = ((block != nullptr) && (block->AlignSize() < min_block_align_size))
2793 : ? block->AlignSize()
2794 : : min_block_align_size;
2795 : }
2796 : const size_t next_index = j + 1 + clone_blocks.size();
2797 : // Sorting will increase processing time, but it can obtain smaller memory
2798 : const bool need_sort_again = ((next_index < memory_blocks_.size()) && (memory_blocks_[next_index] != nullptr) &&
2799 : (min_block_align_size < memory_blocks_[next_index]->AlignSize())) ||
2800 : memory_priority_mode_;
2801 : if (need_sort_again) {
2802 : std::sort(memory_blocks_.begin() + j + 1, memory_blocks_.end(), cmp);
2803 : }
2804 : }
2805 : }
2806 : }
2807 :
2808 : /// @ingroup domi_omg
2809 : /// @brief traverse memory size, resize, calculate offset
2810 : /// @param [in&out] memory_blocks_ memory block, after calculating offset
2811 : /// |-not dynamic batch block-||-dynamic batch block batch1| |-zero copy block-|
2812 : /// |-not dynamic batch block-||-dynamic batch block batch2----||-zero copy block-|
2813 : /// |-not dynamic batch block-||-dynamic batch block batch3--| |-zero copy block-|
2814 : /// 和里理论值有偏差原因,batch内外没有复用,batch间有对齐策略
2815 : Status BlockMemAssigner::ResizeMemoryBlocks() {
2816 : DynamicBatchMemAssigner dynamic_batch_mem_assigner(reuse_strategy_, memory_blocks_, blocks_store_);
2817 : dynamic_batch_mem_assigner.ResizeDynamicBatchBlocks();
2818 : for (auto &memory_block : memory_blocks_) {
2819 : if (memory_block == nullptr || memory_block->child_block_ || memory_block->is_zero_copy_) {
2820 : continue;
2821 : }
2822 : GE_ASSERT_SUCCESS(AddBlockMemOffset(mem_offsets_, *memory_block));
2823 : }
2824 :
2825 : for (auto &it : memory_stat_) {
2826 : it.second.theory_min_memory_size_ += it.second.theory_no_reuse_memory_size_;
2827 : }
2828 :
2829 : for (const auto &it : mem_offsets_) {
2830 : GELOGI("Reuse result:%s memory type:%lu mem_offset exclude zero_copy_memory:%zu.",
2831 : (compute_graph_ != nullptr) ? compute_graph_->GetName().c_str() : "", it.first, it.second);
2832 : }
2833 : return SUCCESS;
2834 : }
2835 :
2836 : /// @ingroup domi
2837 : /// @brief given NodeTypeIndex, set offset in Op's OpDef
2838 : /// @param [in&out] node_type_index <node, memory type, id>
2839 : /// @param [in] offset offset to be set
2840 : /// @param [in] size memory size
2841 : /// @param [in] real_size memory size in need
2842 : /// @return Status result
2843 : void BlockMemAssigner::SetOffsetSize(const NodeTypeIndex &node_type, const MemoryBlock &block, size_t real_size,
2844 : size_t no_align_size, int32_t child_block_level) const {
2845 : GE_CHECK_NOTNULL_EXEC(node_type.node_, return);
2846 : auto op_desc = node_type.node_->GetOpDescBarePtr();
2847 : GE_CHECK_NOTNULL_EXEC(op_desc, return);
2848 : std::string graph_name = compute_graph_->GetName();
2849 : std::vector<int64_t> memorys_type;
2850 : int64_t offset = block.HeadOffset();
2851 : bool has_mem_type_attr = ge::AttrUtils::GetListInt(op_desc, ATTR_NAME_OUTPUT_MEM_TYPE_LIST, memorys_type);
2852 : if (node_type.mem_type_ == OpMemoryType::kOutput) {
2853 : std::vector<int64_t> output_list = op_desc->GetOutputOffset();
2854 : for (auto i = static_cast<uint32_t>(output_list.size()); i < node_type.index_ + 1; i++) {
2855 : output_list.emplace_back(kInvalidOffset);
2856 : }
2857 : if (output_list.empty()) {
2858 : GELOGW("Empty output");
2859 : return;
2860 : }
2861 :
2862 : static const std::set<std::string> kSetOffsetTypes = {DATA_TYPE, REFDATA, AIPP_DATA_TYPE, MULTISHAPE, NETOUTPUT};
2863 : if ((kSetOffsetTypes.count(op_desc->GetTypePtr()) > 0) && !IsKnownSubgraphData(node_type.node_)) {
2864 : if ((output_list[node_type.index_] == kInvalidOffset) || (output_list[node_type.index_] < offset)) {
2865 : output_list.at(node_type.index_) = offset;
2866 : }
2867 : } else {
2868 : // fusion: keep the original other type offset value from op_desc
2869 : bool set_out_offset = (!has_mem_type_attr) ||
2870 : (memorys_type.size() > node_type.index_ && memorys_type[node_type.index_] != RT_MEMORY_L1 &&
2871 : (memorys_type[node_type.index_] != kRtMemoryUB));
2872 : if (set_out_offset) {
2873 : output_list.at(node_type.index_) = offset;
2874 : }
2875 : }
2876 : op_desc->SetOutputOffset(output_list);
2877 : } else if ((node_type.mem_type_ == OpMemoryType::kWorkspace) && (!node_type.is_subgraph_workspace_)) {
2878 : std::vector<int64_t> workspace_list;
2879 : workspace_list = op_desc->GetWorkspace();
2880 : for (auto i = static_cast<uint32_t>(workspace_list.size()); i < node_type.index_ + 1; i++) {
2881 : workspace_list.emplace_back(kInvalidOffset);
2882 : }
2883 : std::vector<int64_t> workspace_mem_type;
2884 : bool has_workspace_mem_type = ge::AttrUtils::GetListInt(op_desc, TVM_ATTR_NAME_WORKSPACE_TYPE, workspace_mem_type);
2885 : // fusion: keep the original other type offset value from op_desc
2886 : bool set_workspace_offset = (!has_workspace_mem_type) || (workspace_mem_type.size() > node_type.index_ &&
2887 : workspace_mem_type[node_type.index_] != RT_MEMORY_L1 &&
2888 : (workspace_mem_type[node_type.index_] != kRtMemoryUB));
2889 : if (set_workspace_offset) {
2890 : workspace_list.at(node_type.index_) = offset;
2891 : }
2892 : op_desc->SetWorkspace(workspace_list);
2893 : } else if (node_type.mem_type_ == OpMemoryType::kOutputDesc) {
2894 : auto tensor = op_desc->MutableOutputDesc(node_type.index_);
2895 : GE_IF_BOOL_EXEC(tensor != nullptr, (void)AttrUtils::SetInt(tensor, ATTR_NAME_TENSOR_DESC_MEM_OFFSET, offset));
2896 : }
2897 : GELOGI("[IMAS]Set %s name[%s] optype[%s] %s[%u] offset to [%" PRId64
2898 : "] streamid[%s] memtype[%lu] size[%zu] realsize[%zu] "
2899 : "noalignsize[%zu] life time begin[%s] life time end[%s] child[%d:%d:%d:%d:%d] isref[%d] batch[%s], "
2900 : "block_type[%s]",
2901 : MemReuseUtils::GetGraphNameId(compute_graph_.get()).c_str(), op_desc->GetName().substr(0, kMaxLogLen).c_str(),
2902 : node_type.node_->GetTypePtr(), node_type.GetMemType().c_str(), node_type.index_, offset,
2903 : GetStreamIdDesc(op_desc).c_str(), block.memory_type_, block.Size(), real_size, no_align_size,
2904 : node_type.GetLifeBeginDesc().c_str(), node_type.GetLifeEndDesc().c_str(), child_block_level, block.reuse_mem_,
2905 : block.GetContinuousFlag(), block.is_zero_copy_, block.same_stream_, node_type.ref_input_,
2906 : block.batch_label_.c_str(), NodeMemAttrUtils::GetAttrStr(node_type).c_str());
2907 :
2908 : if ((!node_type.ref_input_) && (real_size != 0U)) {
2909 : size_t life_end = node_type.GetLifeEnd().back();
2910 : life_end = (life_end == kDefaultLifeTime) ? kMaxLifeTime : life_end; // trans default life end to max life time
2911 : CANN_PROFILING_REPORT_STATIC_OP_MEM_INFO(compute_graph_, node_type.node_->GetOpDesc(), real_size,
2912 : node_type.GetLifeBegin(), life_end);
2913 : }
2914 : }
2915 :
2916 : void BlockMemAssigner::SetBlockOpMemOffset(const MemoryBlock *const block, int32_t child_block_level,
2917 : bool &is_fixed_addr_prior) const {
2918 : if (block == nullptr) {
2919 : return;
2920 : }
2921 : size_t index = 0UL;
2922 : size_t real_size = 0UL;
2923 : size_t no_align_size = 0UL;
2924 : auto real_size_list_size = block->RealSizeList().size();
2925 : for (const NodeTypeIndex &node_type_index : block->NodeTypeIndexList()) {
2926 : if (index < real_size_list_size) {
2927 : real_size = block->RealSizeList()[index];
2928 : no_align_size = block->NoAlignSizeList()[index];
2929 : }
2930 : SetOffsetSize(node_type_index, *block, real_size, no_align_size, child_block_level);
2931 : index++;
2932 : }
2933 :
2934 : is_fixed_addr_prior = (is_fixed_addr_prior || block->is_fixed_addr_prior_);
2935 :
2936 : child_block_level++;
2937 : if (!block->ChildSubGraphBlockList().empty()) {
2938 : for (MemoryBlock *child_block : block->ChildSubGraphBlockList()) {
2939 : SetBlockOpMemOffset(child_block, child_block_level, is_fixed_addr_prior);
2940 : }
2941 : }
2942 :
2943 : for (auto &blocks : block->BatchBlockList()) {
2944 : for (auto child_block : blocks.second) {
2945 : SetBlockOpMemOffset(child_block, child_block_level, is_fixed_addr_prior);
2946 : }
2947 : }
2948 :
2949 : for (MemoryBlock *child_block : block->ChildBlockList()) {
2950 : SetBlockOpMemOffset(child_block, child_block_level, is_fixed_addr_prior);
2951 : }
2952 : }
2953 :
2954 : void BlockMemAssigner::SetOpMemOffset(bool is_zero_copy) const {
2955 : if (!is_zero_copy) {
2956 : for (const auto &attr : bool_attr_) {
2957 : if (!ge::AttrUtils::SetBool(attr.ptr_, attr.name_, attr.value_)) {
2958 : GELOGW("Set %s input[%d] %s to %s failed.", attr.desc_->GetNamePtr(), attr.index_, attr.name_.c_str(),
2959 : attr.value_ ? "true" : "false");
2960 : continue;
2961 : }
2962 : GELOGD("Set %s input[%d] %s to %s success.", attr.desc_->GetNamePtr(), attr.index_, attr.name_.c_str(),
2963 : attr.value_ ? "true" : "false");
2964 : }
2965 :
2966 : for (const auto &attr : int_attr_) {
2967 : if (!ge::AttrUtils::SetInt(attr.ptr_, attr.name_, attr.value_)) {
2968 : GELOGW("Set %s attr %s to %" PRId64 " failed.", attr.desc_->GetNamePtr(), attr.name_.c_str(), attr.value_);
2969 : continue;
2970 : }
2971 : GELOGD("Set %s attr %s to %" PRId64 " success.", attr.desc_->GetNamePtr(), attr.name_.c_str(), attr.value_);
2972 : }
2973 : }
2974 : for (MemoryBlock *memory_block : memory_blocks_) {
2975 : if (memory_block == nullptr || memory_block->child_block_) {
2976 : continue;
2977 : }
2978 :
2979 : if ((is_zero_copy && !memory_block->is_zero_copy_) || (!is_zero_copy && memory_block->is_zero_copy_)) {
2980 : continue;
2981 : }
2982 :
2983 : bool is_fixed_addr_prior = false;
2984 : SetBlockOpMemOffset(memory_block, 0, is_fixed_addr_prior);
2985 : memory_block->is_fixed_addr_prior_ = (memory_block->is_fixed_addr_prior_ || is_fixed_addr_prior);
2986 : }
2987 :
2988 : const auto var_mng = VarManager::Instance(compute_graph_->GetSessionID());
2989 : if (!is_zero_copy) {
2990 : for (const NodeTypeIndex &node_type_index : zero_memory_list_) {
2991 : if (var_mng->IsVarExist(VarMemAssignUtil::GetNameForVarManager(node_type_index.node_->GetOpDesc()))) {
2992 : continue;
2993 : }
2994 : MemoryBlock block(reuse_strategy_, 0, 0);
2995 : SetOffsetSize(node_type_index, block, 0UL, 0UL, 0);
2996 : }
2997 : }
2998 : SetOffsetForContinuousMem();
2999 : }
3000 :
3001 : void BlockMemAssigner::SetOffsetForContinuousMem() const {
3002 : for (const auto &continuous_mem : continuous_mem_mng_.GetAllContinuousMem()) {
3003 : const auto &blocks = continuous_mem.GetBlocks();
3004 : if (!blocks.empty()) {
3005 : const auto block = blocks.front();
3006 : auto offset = block->HeadOffset();
3007 : for (size_t i = 0U; i < continuous_mem.GetContinuousNodeOut().size(); ++i) {
3008 : const auto &node_index = continuous_mem.GetContinuousNodeOut().at(i);
3009 : auto op_desc = node_index.node_ptr_->GetOpDescBarePtr();
3010 : auto out_offsets = op_desc->GetOutputOffset();
3011 : while (out_offsets.size() < node_index.index_ + 1U) {
3012 : out_offsets.emplace_back(kInvalidOffset);
3013 : }
3014 : out_offsets[node_index.index_] = offset;
3015 : op_desc->SetOutputOffset(out_offsets);
3016 : const auto align_size = continuous_mem.GetAlignedSizes().at(i);
3017 : GELOGI("[ContinuousMem][IMAS]Continuous input : Set %s name[%s] optype[%s] output[%d] offset to [%" PRId64
3018 : "] "
3019 : "stream_id[%" PRId64 "] memtype[%" PRId64 "] size[%zu] realsize[%" PRId64
3020 : "] nopadding[%d], block_type[%s]",
3021 : MemReuseUtils::GetGraphNameId(compute_graph_.get()).c_str(),
3022 : op_desc->GetName().substr(0, kMaxLogLen).c_str(), op_desc->GetType().c_str(), node_index.index_, offset,
3023 : op_desc->GetStreamId(), block->memory_type_, 0UL, align_size, false,
3024 : NodeMemAttrUtils::GetAttrStr({node_index.node_ptr_, OpMemoryType::kOutput, node_index.index_}).c_str());
3025 : offset += align_size;
3026 : }
3027 : }
3028 : }
3029 : }
3030 :
3031 : void BlockMemAssigner::SetOpMemOffset(const std::vector<MemoryBlock *> &zero_copy_blocks) const {
3032 : for (const auto memory_block : zero_copy_blocks) {
3033 : if ((memory_block != nullptr) && (!memory_block->child_block_)) {
3034 : bool is_fixed_addr_prior = false;
3035 : SetBlockOpMemOffset(memory_block, 0, is_fixed_addr_prior);
3036 : memory_block->is_fixed_addr_prior_ = (memory_block->is_fixed_addr_prior_ || is_fixed_addr_prior);
3037 : }
3038 : }
3039 : }
3040 :
3041 : Status BlockMemAssigner::Assign() {
3042 : return SUCCESS;
3043 : }
3044 :
3045 : bool BlockMemAssigner::CheckIsZeroMemNodeOutputIndex(const NodePtr &n, uint32_t index) const {
3046 : const auto op_desc = n->GetOpDescBarePtr();
3047 : GE_ASSERT_NOTNULL(op_desc);
3048 : const auto output_tensor_desc = op_desc->MutableOutputDesc(index);
3049 : if (output_tensor_desc == nullptr) {
3050 : GELOGW("op[%s] null output_tensor_desc, index[%u]", op_desc->GetName().c_str(), index);
3051 : return false;
3052 : }
3053 : int32_t tensor_type = 0;
3054 : const bool ret = ge::AttrUtils::GetInt(output_tensor_desc, ATTR_NAME_TENSOR_MEMORY_SCOPE, tensor_type);
3055 : if (ret && tensor_type == kOutputMemoryGlobalType) {
3056 : GELOGD("node[%s] output[%u] is zero memory", n->GetName().c_str(), index);
3057 : return true;
3058 : }
3059 : return false;
3060 : }
3061 :
3062 : } // namespace ge
|