已合并
【PR】: [feat] 支持设置L2CacheHint #1538
【PR】: [feat] 支持设置L2CacheHint #1538
已合并
xchu42创建于 7月28日
11 个文件变更+786-4
Mautofuse/codegen/codegen_kernel.cpp+79-0
@@ -29,6 +29,7 @@
29#include "optimize/platform/platform_factory.h"29#include "optimize/platform/platform_factory.h"
30#include "optimize/schedule_utils.h"30#include "optimize/schedule_utils.h"
31#include "common/platform_context.h"31#include "common/platform_context.h"
32+#include "common/l2_cache_hint_manager.h"
32#include "codegen_graph_check.h"33#include "codegen_graph_check.h"
33 34 
34using namespace std;35using namespace std;
@@ -1944,6 +1945,7 @@ Status Kernel::GlobalTensorInit(std::string &result, const std::string &workspac
1944 GE_CHK_STATUS_RET(AppendConstTensorInit(ss));1945 GE_CHK_STATUS_RET(AppendConstTensorInit(ss));
1945 GE_CHK_STATUS_RET(AppendUbScalarTensorInit(ss));1946 GE_CHK_STATUS_RET(AppendUbScalarTensorInit(ss));
1946 GE_CHK_STATUS_RET(AppendWorkspaceTensorInit(ss, workspace_buffer_arg_override));1947 GE_CHK_STATUS_RET(AppendWorkspaceTensorInit(ss, workspace_buffer_arg_override));
1948+ GE_CHK_STATUS_RET(GenL2CacheHintCode(ss));
1947 result = ss.str();1949 result = ss.str();
1948 return af::SUCCESS;1950 return af::SUCCESS;
1949}1951}
@@ -1997,6 +1999,57 @@ Status Kernel::AppendOutputGlobalTensorInit(std::stringstream &ss) const {
1997 return af::SUCCESS;1999 return af::SUCCESS;
1998}2000}
1999 2001 
2002+Status Kernel::GenL2CacheHintCode(std::stringstream &ss) const {
2003+ GE_CHK_BOOL_RET_SPECIAL_STATUS(gm_tensor_sizes_ == nullptr, af::SUCCESS, "no need to gen code for L2 cache hint");
2004+ ss << "const int64_t kL2CacheSize = " << l2_size_ << ";" << std::endl;
2005+ if (gm_tensor_sizes_->min_total_size > l2_size_) {
2006+ ss << "constexpr bool enable_l2_cache_hint = true;" << std::endl;
2007+ } else {
2008+ ss << "const int64_t total_gm_size = " << this->tiler.Size(gm_tensor_sizes_->total_size) << ";" << std::endl;
2009+ ss << "const bool enable_l2_cache_hint = total_gm_size >= kL2CacheSize;" << std::endl;
2010+ }
2011+ std::vector<std::string> input_names;
2012+ for (std::size_t i = 0; i < this->inputs.size(); i++) {
2013+ const auto &tensor = this->tpipe.tensors.at(this->input_tensors[i]);
2014+ if (tensor.is_constant) {
2015+ continue;
2016+ }
2017+ const size_t input_index = input_name_to_index_.at(this->inputs[i].Str());
2018+ if (skip_l2_cache_hint_input_indices_.find(input_index) == skip_l2_cache_hint_input_indices_.end()) {
2019+ input_names.push_back(tensor.name);
2020+ }
2021+ }
2022+ if (!input_names.empty()) {
2023+ ss << "if (enable_l2_cache_hint) {" << std::endl;
2024+ for (const auto &input_name : input_names) {
2025+ ss << " " << input_name << ".SetL2CacheHint(AscendC::CacheMode::CACHE_MODE_DISABLE);" << std::endl;
2026+ }
2027+ ss << "}" << std::endl;
2028+ }
2029+ for (size_t i = 0; i < this->outputs.size(); i++) {
2030+ const auto &tensor = this->tpipe.tensors.at(this->output_tensors[i]);
2031+ size_t output_index = output_name_to_index_.at(this->outputs[i].Str());
2032+ GE_ASSERT_TRUE(output_index < gm_tensor_sizes_->output_sizes.size(), "output_index[%zu] out of range[%zu]",
2033+ output_index, gm_tensor_sizes_->output_sizes.size());
2034+ const auto &output_size = gm_tensor_sizes_->output_sizes[output_index];
2035+ if (output_size.IsConstExpr()) {
2036+ int64_t output_size_value = 0;
2037+ GE_ASSERT_TRUE(output_size.GetConstValue(output_size_value));
2038+ constexpr int64_t kL2SizeScaleThreshold = 2;
2039+ if (output_size_value > (l2_size_ * kL2SizeScaleThreshold)) {
2040+ ss << tensor.name << ".SetL2CacheHint(AscendC::CacheMode::CACHE_MODE_DISABLE);" << std::endl;
2041+ } else {
2042+ GELOGD("outputs[%zu] is static-shaped, size = %ld, skip gen hint code", output_index, output_size_value);
2043+ }
2044+ } else {
2045+ ss << "if (" << this->tiler.Size(output_size) << " > kL2CacheSize * 2) {" << std::endl;
2046+ ss << " " << tensor.name << ".SetL2CacheHint(AscendC::CacheMode::CACHE_MODE_DISABLE);" << std::endl;
2047+ ss << "}" << std::endl;
2048+ }
2049+ }
2050+ return af::SUCCESS;
2051+}
2052+ 
2000Status Kernel::AppendConstTensorInit(std::stringstream &ss) const {2053Status Kernel::AppendConstTensorInit(std::stringstream &ss) const {
2001 for (std::size_t i = 0; i < this->constant_tensors.size(); i++) {2054 for (std::size_t i = 0; i < this->constant_tensors.size(); i++) {
2002 auto tensor = this->tpipe.tensors.find(this->constant_tensors[i]);2055 auto tensor = this->tpipe.tensors.find(this->constant_tensors[i]);
@@ -2311,6 +2364,7 @@ Status Kernel::ParseGraph(const ascir::ImplGraph &graph, const ascir::FusedSched
2311 uint32_t total_blk_num = 0U;2364 uint32_t total_blk_num = 0U;
2312 GetApiExtractDupSet(graph, kernel.pre_api_extract_dup, total_blk_num);2365 GetApiExtractDupSet(graph, kernel.pre_api_extract_dup, total_blk_num);
2313 kernel.SetEnableParallelCompile((!has_gather));2366 kernel.SetEnableParallelCompile((!has_gather));
2367+ GE_CHK_STATUS_RET(kernel.InitL2CacheHintInfo(fused_schedule_result, graph));
2314 if (IsCVFusionUBGraph(graph, kernel.tpipe.cv_fusion_type)) {2368 if (IsCVFusionUBGraph(graph, kernel.tpipe.cv_fusion_type)) {
2315 GE_CHK_STATUS_RET(kernel.tpipe.GetCVFusionCubeOutputUBTensorIdAndQueId(graph), "get cube output tensor id failed");2369 GE_CHK_STATUS_RET(kernel.tpipe.GetCVFusionCubeOutputUBTensorIdAndQueId(graph), "get cube output tensor id failed");
2316 }2370 }
@@ -3445,6 +3499,31 @@ bool Kernel::GetEnableParallelCompile() const {
3445 return enable_parallel_compile_;3499 return enable_parallel_compile_;
3446}3500}
3447 3501 
3502+Status Kernel::InitL2CacheHintInfo(const ascir::FusedScheduledResult &fused_scheduled_result,
3503+ const ascir::ImplGraph &graph) {
3504+ GE_CHK_BOOL_RET_SPECIAL_STATUS(!tpipe.is_inductor, af::SUCCESS, "not inductor");
3505+ const auto &sizes = fused_scheduled_result.gm_tensor_sizes;
3506+ if (sizes.input_sizes.empty() || sizes.output_sizes.empty()) {
3507+ return af::SUCCESS;
3508+ }
3509+ GE_ASSERT_SUCCESS(optimize::L2CacheHintManager::GetL2Size(l2_size_), "Get l2_size failed");
3510+ if (sizes.total_size.IsConstExpr()) {
3511+ int64_t total_size_value = 0;
3512+ GE_ASSERT_TRUE(sizes.total_size.GetConstValue(total_size_value));
3513+ GE_CHK_BOOL_RET_SPECIAL_STATUS(total_size_value <= l2_size_, af::SUCCESS,
3514+ "InitL2CacheHintInfo skip: total_size[%lld] <= l2_size[%lld].", total_size_value,
3515+ l2_size_);
3516+ }
3517+ const auto symbol_absent = !optimize::L2CacheHintManager::AllExprSymbolsInGraph(sizes, graph);
3518+ GE_CHK_BOOL_RET_SPECIAL_STATUS(symbol_absent, af::SUCCESS,
3519+ "InitL2CacheHintInfo skip: expressions contain symbols not in current graph: %s.",
3520+ graph.GetName().c_str());
3521+ gm_tensor_sizes_ = &fused_scheduled_result.gm_tensor_sizes;
3522+ skip_l2_cache_hint_input_indices_ = optimize::L2CacheHintManager::CollectSkipL2CacheHintIndices(graph);
3523+ GELOGD("InitL2CacheHintInfo success");
3524+ return af::SUCCESS;
3525+}
3526+ 
3448void Kernel::AppendFuncCall(std::stringstream &ss, std::vector<std::vector<std::string>>::const_iterator begin,3527void Kernel::AppendFuncCall(std::stringstream &ss, std::vector<std::vector<std::string>>::const_iterator begin,
3449 std::vector<std::vector<std::string>>::const_iterator end) {3528 std::vector<std::vector<std::string>>::const_iterator end) {
3450 for (auto it = begin; it != end; ++it) {3529 for (auto it = begin; it != end; ++it) {
Mautofuse/codegen/codegen_kernel.h+5-0
@@ -446,6 +446,7 @@ class Kernel {
446 void SetUsingAttCalcQBTSizeConfig(bool using_att_calc_qbt_size);446 void SetUsingAttCalcQBTSizeConfig(bool using_att_calc_qbt_size);
447 void SetEnableParallelCompile(bool enable_parallel_compile);447 void SetEnableParallelCompile(bool enable_parallel_compile);
448 bool GetEnableParallelCompile() const;448 bool GetEnableParallelCompile() const;
449+ Status InitL2CacheHintInfo(const ascir::FusedScheduledResult &fused_scheduled_result, const ascir::ImplGraph &graph);
449 Status GenerateVecFuncOfCVFusion(std::stringstream &result, bool vector_no_db_flag, bool is_conv2d,450 Status GenerateVecFuncOfCVFusion(std::stringstream &result, bool vector_no_db_flag, bool is_conv2d,
450 bool is_dynamic = false, bool is_inductor = false);451 bool is_dynamic = false, bool is_inductor = false);
451 Status InitCVFusionAddr(std::stringstream &result, bool vector_no_db_flag, bool is_dynamic = false,452 Status InitCVFusionAddr(std::stringstream &result, bool vector_no_db_flag, bool is_dynamic = false,
@@ -515,6 +516,7 @@ class Kernel {
515 Status AppendConstTensorInit(std::stringstream &ss) const;516 Status AppendConstTensorInit(std::stringstream &ss) const;
516 Status AppendUbScalarTensorInit(std::stringstream &ss) const;517 Status AppendUbScalarTensorInit(std::stringstream &ss) const;
517 Status AppendWorkspaceTensorInit(std::stringstream &ss, const std::string &workspace_buffer_arg_override) const;518 Status AppendWorkspaceTensorInit(std::stringstream &ss, const std::string &workspace_buffer_arg_override) const;
519+ Status GenL2CacheHintCode(std::stringstream &ss) const;
518 static Status GenCVKernelFuncWithMulGroup(const ascir::FusedScheduledResult &fused_schedule_result,520 static Status GenCVKernelFuncWithMulGroup(const ascir::FusedScheduledResult &fused_schedule_result,
519 const CodegenConfig &config, std::stringstream &ss, std::stringstream &ss1,521 const CodegenConfig &config, std::stringstream &ss, std::stringstream &ss1,
520 bool use_list_tensor);522 bool use_list_tensor);
@@ -563,6 +565,9 @@ class Kernel {
563 std::map<std::string, size_t> output_name_to_index_;565 std::map<std::string, size_t> output_name_to_index_;
564 bool use_list_tensor_ = false;566 bool use_list_tensor_ = false;
565 bool enable_parallel_compile_ = true;567 bool enable_parallel_compile_ = true;
568+ const ascir::GmTensorSizes *gm_tensor_sizes_{};
569+ std::set<size_t> skip_l2_cache_hint_input_indices_;
570+ int64_t l2_size_ = 0;
566};571};
567} // namespace codegen572} // namespace codegen
568 573 
Aautofuse/common/l2_cache_hint_manager.cpp+421-0
@@ -0,0 +1,421 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "l2_cache_hint_manager.h"
12+ 
13+#include "attr_utils.h"
14+#include "ascir_ops.h"
15+#include "ascgen_log.h"
16+#include "ascir_ops_utils.h"
17+#include "common_utils.h"
18+#include "common/platform_context.h"
19+#include "fusion/autofuse_attrs.h"
20+ 
21+using namespace ascir;
22+using namespace optimize;
23+using namespace af::ascir_op;
24+using namespace af::ops;
25+ 
26+namespace {
27+constexpr char const kAscBackendType[] = "AscBackend";
28+constexpr char const kSkipL2CacheHintAttr[] = "_skip_l2_cache_hint";
29+ 
30+std::string ExprToStr(const af::Expression &expr) {
31+ if (!expr.IsValid()) {
32+ return "<invalid>";
33+ }
34+ const auto expr_str_ptr = expr.Str();
35+ return (expr_str_ptr == nullptr) ? std::string("<null>") : std::string(expr_str_ptr.get());
36+}
37+ 
38+bool IsSmallTensor(const af::AscNodePtr &node) {
39+ if (node == nullptr || node->outputs().empty()) {
40+ return false;
41+ }
42+ af::Expression output_size = af::sym::kSymbolOne;
43+ for (const auto &repeat : node->outputs[0].attr.repeats) {
44+ output_size = output_size * repeat;
45+ }
46+ output_size = output_size * af::Symbol(static_cast<int64_t>(ge::GetSizeByDataType(node->outputs[0].attr.dtype)));
47+ int64_t output_size_value = 0;
48+ bool is_small = output_size.IsConstExpr() && output_size.GetConstValue(output_size_value) &&
49+ (output_size_value <= 2L * 1024L * 1024L);
50+ if (is_small) {
51+ GELOGD("IsSmallTensor node[%s] output_size[%lld] <= 2MB.", node->GetNamePtr(), output_size_value);
52+ }
53+ return is_small;
54+}
55+ 
56+// 按index属性建立的IO节点映射: index -> 节点
57+struct IoNodeIndexMap {
58+ std::map<int64_t, af::NodePtr> inputs;
59+ std::map<int64_t, af::NodePtr> outputs;
60+};
61+ 
62+// 优先从AscNode的ir_attr读取index; 否则回退到op_desc的AscNodeAttr(参考FusedGraphModifier::ProcessDataNodes)
63+bool GetNodeIndex(const af::NodePtr &node, int64_t &index) {
64+ const auto asc_node = std::dynamic_pointer_cast<af::AscNode>(node);
65+ if (asc_node != nullptr && asc_node->attr.ir_attr != nullptr) {
66+ return asc_node->attr.ir_attr->GetAttrValue("index", index) == af::GRAPH_SUCCESS;
67+ }
68+ const auto op_desc = node->GetOpDescBarePtr();
69+ GE_ASSERT_NOTNULL(op_desc);
70+ const auto node_attr = op_desc->GetAttrsGroup<af::AscNodeAttr>();
71+ GE_WARN_ASSERT(node_attr != nullptr);
72+ GE_WARN_ASSERT(node_attr->ir_attr != nullptr);
73+ return node_attr->ir_attr->GetAttrValue("index", index) == af::GRAPH_SUCCESS;
74+}
75+ 
76+// 遍历ComputeGraph,按index属性建立input(Data)/output(Output)节点映射
77+IoNodeIndexMap CollectIoNodesByIndex(const af::ComputeGraph &graph) {
78+ IoNodeIndexMap index_map;
79+ for (const auto &node : graph.GetAllNodes()) {
80+ GE_ASSERT_NOTNULL(node);
81+ const bool is_input = af::ops::IsOps<af::ascir_op::Data>(node);
82+ const bool is_output = af::ops::IsOps<af::ascir_op::Output>(node);
83+ if (!is_input && !is_output) {
84+ continue;
85+ }
86+ int64_t index = -1;
87+ if (!GetNodeIndex(node, index)) {
88+ GELOGW("L2Ctrl skip node[%s] without index attr.", node->GetNamePtr());
89+ continue;
90+ }
91+ if (is_input) {
92+ index_map.inputs[index] = node;
93+ } else {
94+ index_map.outputs[index] = node;
95+ }
96+ }
97+ return index_map;
98+}
99+ 
100+// 计算节点输出tensor大小(字节, 符号化):
101+// total_size为outputs[0].attr.repeats中所有轴(含非常量符号)维度相乘再乘以dtype size的Expression;
102+// min_size为仅常量轴相乘(跳过非常量符号)再乘以dtype size的int64估算
103+af::Status CalcOutputTensorSizeExpr(const af::AscNodePtr &node, af::Expression &total_size, int64_t &min_size) {
104+ total_size = af::sym::kSymbolOne;
105+ min_size = 1;
106+ GE_ASSERT_NOTNULL(node, "CalcOutputTensorSizeExpr node is nullptr.");
107+ GE_ASSERT_TRUE(!node->outputs().empty(), "CalcOutputTensorSizeExpr node[%s] has no output.", node->GetNamePtr());
108+ const auto &repeats = node->outputs[0].attr.repeats;
109+ for (const auto &repeat : repeats) {
110+ total_size = total_size * repeat;
111+ if (repeat.IsConstExpr()) {
112+ int64_t dim = 0;
113+ GE_ASSERT_TRUE(repeat.GetConstValue(dim), "CalcOutputTensorSizeExpr node[%s] get const value failed.",
114+ node->GetNamePtr());
115+ min_size *= dim;
116+ }
117+ }
118+ const auto dtype_size = ge::GetSizeByDataType(node->outputs[0].attr.dtype);
119+ total_size = total_size * af::Symbol(static_cast<int64_t>(dtype_size));
120+ min_size *= static_cast<int64_t>(dtype_size);
121+ GELOGD("CalcOutputTensorSizeExpr node:%s, total_size:%s, min_size:%lld.", node->GetNamePtr(),
122+ ExprToStr(total_size).c_str(), min_size);
123+ return af::SUCCESS;
124+}
125+ 
126+// 从AscBackend后继的子图中, 查找index匹配的Data节点, 累加其后继的output size
127+af::Status CalcInputL2SizeFromAscBackend(const af::NodePtr &backend_node, int64_t input_index,
128+ af::Expression &input_size, int64_t &min_size) {
129+ const auto op_desc = backend_node->GetOpDesc();
130+ GE_ASSERT_NOTNULL(op_desc, "AscBackend node[%s] has no OpDesc.", backend_node->GetNamePtr());
131+ const auto fuse_attr = op_desc->GetAttrsGroup<af::AutoFuseAttrs>();
132+ GE_ASSERT_NOTNULL(fuse_attr, "AscBackend node[%s] has no AutoFuseAttrs.", backend_node->GetNamePtr());
133+ const auto &sub_asc_graph = fuse_attr->GetAscGraph();
134+ GE_ASSERT_NOTNULL(sub_asc_graph, "AscBackend node[%s] has no AscGraph.", backend_node->GetNamePtr());
135+ bool found = false;
136+ for (const auto &sub_node : sub_asc_graph->GetAllNodes()) {
137+ GE_ASSERT_NOTNULL(sub_node);
138+ if (!af::ops::IsOps<af::ascir_op::Data>(sub_node)) {
139+ continue;
140+ }
141+ int64_t sub_index = -1;
142+ if (!GetNodeIndex(sub_node, sub_index) || sub_index != input_index) {
143+ continue;
144+ }
145+ found = true;
146+ for (const auto &sub_successor : sub_node->GetOutDataNodes()) {
147+ const auto sub_successor_asc = std::dynamic_pointer_cast<af::AscNode>(sub_successor);
148+ if (sub_successor_asc == nullptr) {
149+ continue;
150+ }
151+ af::Expression sub_size = af::sym::kSymbolOne;
152+ int64_t sub_min = 0;
153+ GE_ASSERT_SUCCESS(CalcOutputTensorSizeExpr(sub_successor_asc, sub_size, sub_min));
154+ input_size = input_size + sub_size;
155+ min_size += sub_min;
156+ }
157+ GELOGD("L2Ctrl input[%lld] match AscBackend[%s] sub Data[%s].", input_index, backend_node->GetNamePtr(),
158+ sub_node->GetNamePtr());
159+ break;
160+ }
161+ if (!found) {
162+ GELOGW("L2Ctrl input[%lld] not found in AscBackend[%s] AscGraph.", input_index, backend_node->GetNamePtr());
163+ }
164+ return af::SUCCESS;
165+}
166+ 
167+// 计算单个input节点的GM大小(符号化): 遍历其后继节点累加size;
168+// 若后继为AscBackend节点, 则进入其关联AscGraph中找index相同的Data节点, 改用该Data的后继累加
169+af::Status CalcInputL2SizeExpr(const af::NodePtr &input_node, int64_t input_index, af::Expression &input_size,
170+ int64_t &min_size) {
171+ input_size = af::sym::kSymbolZero;
172+ min_size = 0;
173+ for (const auto &successor : input_node->GetOutDataNodes()) {
174+ if (successor == nullptr) {
175+ continue;
176+ }
177+ if (successor->GetType() == kAscBackendType) {
178+ GE_ASSERT_SUCCESS(CalcInputL2SizeFromAscBackend(successor, input_index, input_size, min_size));
179+ } else {
180+ const auto successor_asc = std::dynamic_pointer_cast<af::AscNode>(successor);
181+ if (successor_asc != nullptr) {
182+ af::Expression successor_size = af::sym::kSymbolOne;
183+ int64_t successor_min = 0;
184+ GE_ASSERT_SUCCESS(CalcOutputTensorSizeExpr(successor_asc, successor_size, successor_min));
185+ input_size = input_size + successor_size;
186+ min_size += successor_min;
187+ }
188+ }
189+ }
190+ return af::SUCCESS;
191+}
192+ 
193+// 计算单个output节点的GM大小(符号化): 取其前驱计算size;
194+// 若前驱为AscBackend节点, 则进入其关联AscGraph中找index相同的Output节点, 改用该Output的前驱计算
195+af::Status CalcOutputL2SizeExpr(const af::NodePtr &output_node, int64_t output_index, af::Expression &output_size,
196+ int64_t &min_size) {
197+ output_size = af::sym::kSymbolZero;
198+ min_size = 0;
199+ const auto &predecessors = output_node->GetInDataNodes();
200+ if (predecessors.empty()) {
201+ return af::SUCCESS;
202+ }
203+ const auto &predecessor = predecessors.at(0UL);
204+ GE_ASSERT_NOTNULL(predecessor, "CalcOutputL2SizeExpr predecessor is nullptr.");
205+ if (predecessor->GetType() != kAscBackendType) {
206+ const auto predecessor_asc = std::dynamic_pointer_cast<af::AscNode>(predecessor);
207+ if (predecessor_asc != nullptr) {
208+ GE_ASSERT_SUCCESS(CalcOutputTensorSizeExpr(predecessor_asc, output_size, min_size));
209+ }
210+ return af::SUCCESS;
211+ }
212+ const auto op_desc = predecessor->GetOpDesc();
213+ GE_ASSERT_NOTNULL(op_desc, "AscBackend node[%s] has no OpDesc.", predecessor->GetNamePtr());
214+ const auto fuse_attr = op_desc->GetAttrsGroup<af::AutoFuseAttrs>();
215+ GE_ASSERT_NOTNULL(fuse_attr, "AscBackend node[%s] has no AutoFuseAttrs.", predecessor->GetNamePtr());
216+ const auto &sub_asc_graph = fuse_attr->GetAscGraph();
217+ GE_ASSERT_NOTNULL(sub_asc_graph, "AscBackend node[%s] has no AscGraph.", predecessor->GetNamePtr());
218+ for (const auto &sub_node : sub_asc_graph->GetAllNodes()) {
219+ GE_ASSERT_NOTNULL(sub_node);
220+ if (!af::ops::IsOps<af::ascir_op::Output>(sub_node)) {
221+ continue;
222+ }
223+ int64_t sub_index = -1;
224+ if (!GetNodeIndex(sub_node, sub_index) || sub_index != output_index) {
225+ continue;
226+ }
227+ const auto &sub_predecessors = sub_node->GetInDataNodes();
228+ if (!sub_predecessors.empty()) {
229+ const auto sub_predecessor_asc = std::dynamic_pointer_cast<af::AscNode>(sub_predecessors.at(0UL));
230+ if (sub_predecessor_asc != nullptr) {
231+ GE_ASSERT_SUCCESS(CalcOutputTensorSizeExpr(sub_predecessor_asc, output_size, min_size));
232+ }
233+ }
234+ GELOGD("L2Ctrl output[%lld] match AscBackend[%s] sub Output[%s].", output_index, predecessor->GetNamePtr(),
235+ sub_node->GetNamePtr());
236+ break;
237+ }
238+ return af::SUCCESS;
239+}
240+ 
241+bool ExprInGraph(const af::Expression &expr, const std::set<std::string> &graph_size_var_names) {
242+ if (expr.IsConstExpr()) {
243+ return true;
244+ }
245+ for (const auto &sym : expr.FreeSymbols()) {
246+ auto str_ptr = sym.Str();
247+ GE_WARN_ASSERT(str_ptr != nullptr);
248+ std::string sym_name(str_ptr.get());
249+ if (graph_size_var_names.find(sym_name) == graph_size_var_names.end()) {
250+ GELOGI("ExprInGraph: symbol[%s] not found in current graph.", sym_name.c_str());
251+ return false;
252+ }
253+ }
254+ return true;
255+}
256+} // namespace
257+ 
258+namespace optimize {
259+af::Status L2CacheHintManager::GetL2Size(int64_t &l2_size) {
260+ ge::PlatformInfo platform_info;
261+ GE_ASSERT_SUCCESS(ge::PlatformContext::GetInstance().GetPlatformInfo(platform_info));
262+ l2_size = platform_info.l2_size;
263+ return af::SUCCESS;
264+}
265+ 
266+std::set<size_t> L2CacheHintManager::CollectSkipL2CacheHintIndices(const ascir::ImplGraph &graph) {
267+ std::set<size_t> skip_indices;
268+ for (const auto &node : graph.GetAllNodes()) {
269+ if (!af::ops::IsOps<af::ascir_op::Data>(node)) {
270+ continue;
271+ }
272+ if (!af::AttrUtils::HasAttr(node->GetOpDesc(), kSkipL2CacheHintAttr)) {
273+ continue;
274+ }
275+ int64_t index = -1;
276+ (void)node->attr.ir_attr->GetAttrValue("index", index);
277+ GELOGD("skip input index: %ld", index);
278+ skip_indices.insert(static_cast<size_t>(index));
279+ }
280+ return skip_indices;
281+}
282+ 
283+bool L2CacheHintManager::AllExprSymbolsInGraph(const ascir::GmTensorSizes &sizes, const ascir::ImplGraph &graph) {
284+ std::set<std::string> graph_size_var_names;
285+ for (const auto &size_var : graph.GetAllSizeVar()) {
286+ graph_size_var_names.insert(std::string(size_var->expr.Str().get()));
287+ }
288+ if (!ExprInGraph(sizes.total_size, graph_size_var_names)) {
289+ return false;
290+ }
291+ for (const auto &output_size : sizes.output_sizes) {
292+ if (!ExprInGraph(output_size, graph_size_var_names)) {
293+ return false;
294+ }
295+ }
296+ return true;
297+}
298+ 
299+af::Status L2CacheHintManager::ParseGraph(const af::ComputeGraph &graph,
300+ ::ascir::FusedScheduledResult &fused_scheduled_result) {
301+ // 暂不支持多个AscBackend的场景
302+ GE_CHK_BOOL_RET_SPECIAL_STATUS(fused_scheduled_result.node_idx_to_scheduled_results.size() > 1, af::SUCCESS,
303+ "ParseGraph skip: multiple AscBackend nodes");
304+ GE_ASSERT_SUCCESS(CalcTensorSizes(graph, fused_scheduled_result, fused_scheduled_result.gm_tensor_sizes));
305+ const auto &sizes = fused_scheduled_result.gm_tensor_sizes;
306+ int64_t total_size_value = 0;
307+ if (sizes.total_size.IsConstExpr() && sizes.total_size.GetConstValue(total_size_value)) {
308+ int64_t l2_size = -1;
309+ GE_ASSERT_SUCCESS(GetL2Size(l2_size));
310+ if (total_size_value <= l2_size) {
311+ GELOGD("ParseGraph skip: total_size[%lld] <= l2_size[%lld].", total_size_value, l2_size);
312+ return af::SUCCESS;
313+ }
314+ }
315+ GE_ASSERT_SUCCESS(MarkInputsNeedSkipL2CacheHint(fused_scheduled_result));
316+ return af::SUCCESS;
317+}
318+ 
319+af::Status L2CacheHintManager::CalcTensorSizes(const af::ComputeGraph &graph, const ::ascir::FusedScheduledResult &fsr,
320+ GmTensorSizes &global_tensor_sizes) {
321+ GELOGI("CalcTensorSizes start, graph:%s, input_nodes_num:%zu, output_nodes_num:%zu.", graph.GetName().c_str(),
322+ fsr.input_nodes.size(), fsr.output_nodes.size());
323+ global_tensor_sizes.total_size = af::sym::kSymbolZero;
324+ global_tensor_sizes.min_total_size = 0;
325+ global_tensor_sizes.input_sizes.clear();
326+ global_tensor_sizes.output_sizes.clear();
327+ const auto index_map = CollectIoNodesByIndex(graph);
328+ 
329+ global_tensor_sizes.input_sizes.reserve(fsr.input_nodes.size());
330+ for (size_t i = 0; i < fsr.input_nodes.size(); ++i) {
331+ af::Expression input_size = af::sym::kSymbolZero;
332+ int64_t min_size = 0;
333+ const auto it = index_map.inputs.find(static_cast<int64_t>(i));
334+ if (it != index_map.inputs.end()) {
335+ const auto &input_node = it->second;
336+ GE_ASSERT_SUCCESS(CalcInputL2SizeExpr(input_node, static_cast<int64_t>(i), input_size, min_size));
337+ GELOGD("CalcTensorSizes input[%zu] node:%s, total_size:%s, min_size:%lld.", i, input_node->GetNamePtr(),
338+ ExprToStr(input_size).c_str(), min_size);
339+ } else {
340+ GELOGW("CalcTensorSizes input[%zu] not found in graph, tensor_size set to 0.", i);
341+ }
342+ global_tensor_sizes.input_sizes.push_back(input_size);
343+ global_tensor_sizes.total_size = global_tensor_sizes.total_size + input_size;
344+ global_tensor_sizes.min_total_size += min_size;
345+ }
346+ 
347+ global_tensor_sizes.output_sizes.reserve(fsr.output_nodes.size());
348+ for (size_t i = 0UL; i < fsr.output_nodes.size(); ++i) {
349+ af::Expression output_size = af::sym::kSymbolZero;
350+ int64_t min_size = 0;
351+ const auto it = index_map.outputs.find(static_cast<int64_t>(i));
352+ if (it != index_map.outputs.end()) {
353+ const auto &output_node = it->second;
354+ GE_ASSERT_SUCCESS(CalcOutputL2SizeExpr(output_node, static_cast<int64_t>(i), output_size, min_size));
355+ GELOGD("CalcTensorSizes output[%zu] node:%s, total_size:%s, min_size:%lld.", i, output_node->GetNamePtr(),
356+ ExprToStr(output_size).c_str(), min_size);
357+ } else {
358+ GELOGW("CalcTensorSizes output[%zu] not found in graph, tensor_size set to 0.", i);
359+ }
360+ global_tensor_sizes.output_sizes.push_back(output_size);
361+ global_tensor_sizes.total_size = global_tensor_sizes.total_size + output_size;
362+ global_tensor_sizes.min_total_size += min_size;
363+ }
364+ 
365+ GELOGI("CalcTensorSizes end, total_size:%s, min_total_size:%lld.", ExprToStr(global_tensor_sizes.total_size).c_str(),
366+ global_tensor_sizes.min_total_size);
367+ return af::SUCCESS;
368+}
369+ 
370+af::Status L2CacheHintManager::MarkInternal(af::AscGraph &impl_graph) {
371+ GELOGD("MarkInternal start, graph:%s.", impl_graph.GetName().c_str());
372+ std::map<int64_t, std::vector<af::AscNodePtr>> index_to_data_nodes;
373+ for (const auto &node : impl_graph.GetAllNodes()) {
374+ GE_ASSERT_NOTNULL(node, "MarkInternal node is nullptr.");
375+ if (!af::ops::IsOps<af::ascir_op::Data>(node)) {
376+ continue;
377+ }
378+ int64_t index = -1;
379+ GE_ASSERT_TRUE(GetNodeIndex(node, index), "MarkInternal get index attr failed, node[%s].", node->GetNamePtr());
380+ index_to_data_nodes[index].emplace_back(std::dynamic_pointer_cast<af::AscNode>(node));
381+ }
382+ 
383+ // 再次遍历所有Data节点: 若该节点的index对应多个Data节点, 或该节点有多个OutDataNodes, 则标记_skip_l2_cache_hint
384+ for (const auto &node : impl_graph.GetAllNodes()) {
385+ GE_ASSERT_NOTNULL(node, "MarkInternal node is nullptr.");
386+ if (!af::ops::IsOps<af::ascir_op::Data>(node)) {
387+ continue;
388+ }
389+ int64_t index = -1;
390+ GE_ASSERT_TRUE(GetNodeIndex(node, index), "MarkInternal get index attr failed, node[%s].", node->GetNamePtr());
391+ const auto &out_data_nodes = node->GetOutDataNodes();
392+ if (out_data_nodes.empty()) {
393+ continue;
394+ }
395+ const auto out_node = std::dynamic_pointer_cast<af::AscNode>(out_data_nodes.at(0));
396+ const bool need_skip = (index_to_data_nodes[index].size() > 1UL) || (out_data_nodes.size() > 1UL) ||
397+ ascgen_utils::IsNodeCacheable(out_data_nodes.at(0)) || IsSmallTensor(out_node);
398+ if (!need_skip) {
399+ continue;
400+ }
401+ GE_ASSERT_TRUE(af::AttrUtils::SetBool(node->GetOpDesc(), kSkipL2CacheHintAttr, true),
402+ "MarkInternal set _skip_l2_cache_hint failed, node[%s].", node->GetNamePtr());
403+ GELOGD("MarkInternal set _skip_l2_cache_hint on Data node[%s], index:%lld.", node->GetNamePtr(), index);
404+ }
405+ GELOGD("MarkInternal end, graph:%s.", impl_graph.GetName().c_str());
406+ return af::SUCCESS;
407+}
408+ 
409+af::Status L2CacheHintManager::MarkInputsNeedSkipL2CacheHint(::ascir::FusedScheduledResult &fused_scheduled_result) {
410+ for (auto &scheduled_results : fused_scheduled_result.node_idx_to_scheduled_results) {
411+ for (auto &scheduled_result : scheduled_results) {
412+ for (auto &schedule_group : scheduled_result.schedule_groups) {
413+ for (auto &impl_graph : schedule_group.impl_graphs) {
414+ GE_ASSERT_SUCCESS(MarkInternal(impl_graph));
415+ }
416+ }
417+ }
418+ }
419+ return af::SUCCESS;
420+}
421+} // namespace optimize
Aautofuse/common/l2_cache_hint_manager.h+33-0
@@ -0,0 +1,33 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef COMMON_L2_CACHE_HINT_MANAGER_H_
12+#define COMMON_L2_CACHE_HINT_MANAGER_H_
13+ 
14+#include "schedule_result.h"
15+#include "ascir/meta/ascir.h"
16+ 
17+namespace optimize {
18+class L2CacheHintManager {
19+ public:
20+ static af::Status ParseGraph(const af::ComputeGraph &graph, ::ascir::FusedScheduledResult &fused_scheduled_result);
21+ static bool AllExprSymbolsInGraph(const ascir::GmTensorSizes &sizes, const ascir::ImplGraph &graph);
22+ static std::set<size_t> CollectSkipL2CacheHintIndices(const ascir::ImplGraph &graph);
23+ static af::Status GetL2Size(int64_t &l2_size);
24+ 
25+ private:
26+ static af::Status CalcTensorSizes(const af::ComputeGraph &graph, const ::ascir::FusedScheduledResult &fsr,
27+ ascir::GmTensorSizes &global_tensor_sizes);
28+ static af::Status MarkInputsNeedSkipL2CacheHint(::ascir::FusedScheduledResult &fused_scheduled_result);
29+ static af::Status MarkInternal(af::AscGraph &impl_graph);
30+};
31+} // namespace optimize
32+ 
33+#endif // COMMON_L2_CACHE_HINT_MANAGER_H_
Mautofuse/common/platform_context.cpp+10-4
@@ -19,6 +19,7 @@ const char *kSocInfo = "SoCInfo";
19const char *kAICoreSpec = "AICoreSpec";19const char *kAICoreSpec = "AICoreSpec";
20const char *kVectorCoreCnt = "vector_core_cnt";20const char *kVectorCoreCnt = "vector_core_cnt";
21const char *kUbSize = "ub_size";21const char *kUbSize = "ub_size";
22+const char *kL2Size = "l2_size";
22 23 
23bool ParseInt64(const char *value, const char *key_name, int64_t &result) {24bool ParseInt64(const char *value, const char *key_name, int64_t &result) {
24 try {25 try {
@@ -56,8 +57,8 @@ void PlatformContext::SetPlatformInfo(const PlatformInfo &platform_info) {
56 if (!platform_info.soc_ver.empty()) {57 if (!platform_info.soc_ver.empty()) {
57 platform_info_ = platform_info;58 platform_info_ = platform_info;
58 initialized_ = true;59 initialized_ = true;
59- GELOGI("Set platform info: soc_ver=%s, aiv_num=%lld, ub_size=%lld", platform_info_.soc_ver.c_str(),60+ GELOGI("Set platform info: soc_ver=%s, aiv_num=%lld, ub_size=%lld, l2_size=%lld", platform_info_.soc_ver.c_str(),
60- platform_info_.aiv_num, platform_info_.ub_size);61+ platform_info_.aiv_num, platform_info_.ub_size, platform_info_.l2_size);
61 }62 }
62}63}
63 64 
@@ -102,9 +103,14 @@ af::Status PlatformContext::InitPlatformInfo() {
102 res = rtGetSocSpec(kAICoreSpec, kUbSize, ub_size_str, kMaxValueLen);103 res = rtGetSocSpec(kAICoreSpec, kUbSize, ub_size_str, kMaxValueLen);
103 GE_ASSERT_TRUE(res == RT_ERROR_NONE, "Failed to get ub_size.");104 GE_ASSERT_TRUE(res == RT_ERROR_NONE, "Failed to get ub_size.");
104 GE_ASSERT_TRUE(ParseInt64(ub_size_str, "ub_size", platform_info_.ub_size), "Failed to parse ub_size.");105 GE_ASSERT_TRUE(ParseInt64(ub_size_str, "ub_size", platform_info_.ub_size), "Failed to parse ub_size.");
106+ 
107+ char l2_size_str[kMaxValueLen] = {};
108+ res = rtGetSocSpec(kSocInfo, kL2Size, l2_size_str, kMaxValueLen);
109+ GE_ASSERT_TRUE(res == RT_ERROR_NONE, "Failed to get l2_size.");
110+ GE_ASSERT_TRUE(ParseInt64(l2_size_str, "l2_size", platform_info_.l2_size), "Failed to parse l2_size.");
105 initialized_ = true;111 initialized_ = true;
106- GELOGI("Platform info: soc_ver=%s, aiv_num=%lld, ub_size=%lld", platform_info_.soc_ver.c_str(),112+ GELOGI("Platform info: soc_ver=%s, aiv_num=%lld, ub_size=%lld, l2_size=%lld", platform_info_.soc_ver.c_str(),
107- platform_info_.aiv_num, platform_info_.ub_size);113+ platform_info_.aiv_num, platform_info_.ub_size, platform_info_.l2_size);
108 114 
109 return af::SUCCESS;115 return af::SUCCESS;
110}116}
Mautofuse/common/schedule_result.h+8-0
@@ -45,6 +45,13 @@ struct ScheduledResult {
45 CubeTemplateType cube_type{CubeTemplateType::kDefault};45 CubeTemplateType cube_type{CubeTemplateType::kDefault};
46};46};
47 47 
48+struct GmTensorSizes {
49+ af::Expression total_size;
50+ int64_t min_total_size;
51+ std::vector<af::Expression> input_sizes;
52+ std::vector<af::Expression> output_sizes;
53+};
54+ 
48struct FusedScheduledResult {55struct FusedScheduledResult {
49 ge::AscendString fused_graph_name;56 ge::AscendString fused_graph_name;
50 std::vector<af::AscNodePtr> input_nodes;57 std::vector<af::AscNodePtr> input_nodes;
@@ -52,6 +59,7 @@ struct FusedScheduledResult {
52 std::vector<af::AscNodePtr> workspace_nodes;59 std::vector<af::AscNodePtr> workspace_nodes;
53 std::vector<af::Expression> origin_vars;60 std::vector<af::Expression> origin_vars;
54 std::vector<std::vector<ScheduledResult>> node_idx_to_scheduled_results;61 std::vector<std::vector<ScheduledResult>> node_idx_to_scheduled_results;
62+ GmTensorSizes gm_tensor_sizes;
55};63};
56 64 
57enum class TemplateId : int64_t {65enum class TemplateId : int64_t {
Mautofuse/inc/common/platform_context.h+2-0
@@ -20,6 +20,7 @@ struct PlatformInfo {
20 std::string soc_ver;20 std::string soc_ver;
21 int64_t aiv_num = 0;21 int64_t aiv_num = 0;
22 int64_t ub_size = 0;22 int64_t ub_size = 0;
23+ int64_t l2_size = 0;
23};24};
24 25 
25class PlatformContext {26class PlatformContext {
@@ -46,6 +47,7 @@ class PlatformContext {
46 platform_info_.soc_ver = "";47 platform_info_.soc_ver = "";
47 platform_info_.aiv_num = 0;48 platform_info_.aiv_num = 0;
48 platform_info_.ub_size = 0;49 platform_info_.ub_size = 0;
50+ platform_info_.l2_size = 0;
49 ub_size_override_ = 0;51 ub_size_override_ = 0;
50 has_ub_size_override_ = false;52 has_ub_size_override_ = false;
51 }53 }
Mautofuse/optimize/optimize.cpp+5-0
@@ -33,6 +33,7 @@
33#include "optimize/graph_completeness/dtype_consistency.h"33#include "optimize/graph_completeness/dtype_consistency.h"
34#include "pre_process/pre_process.h"34#include "pre_process/pre_process.h"
35#include "static_ub_template_filter.h"35#include "static_ub_template_filter.h"
36+#include "common/l2_cache_hint_manager.h"
36 37 
37using namespace ascir;38using namespace ascir;
38using namespace optimize;39using namespace optimize;
@@ -644,6 +645,7 @@ Status Optimizer::OptimizeFusedAscBackend(const af::ComputeGraphPtr &fused_graph
644 GE_CHK_STATUS_RET(allocator.PrepareImplGraphMemoryPlan(fused_scheduled_result));645 GE_CHK_STATUS_RET(allocator.PrepareImplGraphMemoryPlan(fused_scheduled_result));
645 GE_CHK_STATUS_RET(StaticUbTemplateFilter().Filter(fused_scheduled_result));646 GE_CHK_STATUS_RET(StaticUbTemplateFilter().Filter(fused_scheduled_result));
646 GE_CHK_STATUS_RET(allocator.CollectFusedIoNodes(fused_scheduled_result));647 GE_CHK_STATUS_RET(allocator.CollectFusedIoNodes(fused_scheduled_result));
648+ GE_CHK_STATUS_RET(optimize::L2CacheHintManager::ParseGraph(*fused_graph, fused_scheduled_result));
647 GELOGI("AllocBufQue end");649 GELOGI("AllocBufQue end");
648 TryEnableGroupParallel(fused_scheduled_result);650 TryEnableGroupParallel(fused_scheduled_result);
649 for (auto &scheduled_results : fused_scheduled_result.node_idx_to_scheduled_results) {651 for (auto &scheduled_results : fused_scheduled_result.node_idx_to_scheduled_results) {
@@ -978,6 +980,9 @@ Status Optimizer::Optimize(af::AscGraph &hint_graph, FusedScheduledResult &fused
978 }980 }
979 GE_CHK_STATUS_RET(StaticUbTemplateFilter().Filter(fused_scheduled_result));981 GE_CHK_STATUS_RET(StaticUbTemplateFilter().Filter(fused_scheduled_result));
980 GE_CHK_STATUS_RET(allocator.CollectFusedIoNodes(fused_scheduled_result));982 GE_CHK_STATUS_RET(allocator.CollectFusedIoNodes(fused_scheduled_result));
983+ const auto compute_graph = af::AscGraphUtils::GetComputeGraph(hint_graph);
984+ GE_ASSERT_NOTNULL(compute_graph);
985+ GE_CHK_STATUS_RET(optimize::L2CacheHintManager::ParseGraph(*compute_graph, fused_scheduled_result));
981 GELOGI("AllocBufQue end");986 GELOGI("AllocBufQue end");
982 TryEnableGroupParallel(fused_scheduled_result);987 TryEnableGroupParallel(fused_scheduled_result);
983 ExecSeqAdvancedOfLoad(fused_scheduled_result);988 ExecSeqAdvancedOfLoad(fused_scheduled_result);
Mautofuse/tests/depends/runtime/src/runtime_stub.cc+4-0
@@ -55,6 +55,10 @@ rtError_t RuntimeStub::rtGetSocSpec(const char *label, const char *key, char *va
55 (void)strcpy_s(val, maxLen, "245760");55 (void)strcpy_s(val, maxLen, "245760");
56 return RT_ERROR_NONE;56 return RT_ERROR_NONE;
57 }57 }
58+ if (strcmp(key, "l2_size") == 0) {
59+ (void)strcpy_s(val, maxLen, "134217728");
60+ return RT_ERROR_NONE;
61+ }
58 // 返回 padding_size = 32 (兼容旧平台)62 // 返回 padding_size = 32 (兼容旧平台)
59 if (strcmp(key, "padding_size") == 0) {63 if (strcmp(key, "padding_size") == 0) {
60 (void)strcpy_s(val, maxLen, "32");64 (void)strcpy_s(val, maxLen, "32");
Mautofuse/tests/depends/runtime/src/runtime_stub.h+1-0
@@ -39,6 +39,7 @@ inline rtError_t CopyRuntimeSocSpecValue(const char *label, const char *key, cha
39 {"NpuArch", defaults.npu_arch},39 {"NpuArch", defaults.npu_arch},
40 {"vector_core_cnt", "48"},40 {"vector_core_cnt", "48"},
41 {"ub_size", "245760"},41 {"ub_size", "245760"},
42+ {"l2_size", "134217728"},
42 };43 };
43 for (const auto &spec : specs) {44 for (const auto &spec : specs) {
44 if (std::strcmp(key, spec.key) == 0) {45 if (std::strcmp(key, spec.key) == 0) {
Aautofuse/tests/ut/optimize/test_l2_cache_hint_manager.cpp+218-0
@@ -0,0 +1,218 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "gtest/gtest.h"
12+ 
13+#include "ascendc_ir.h"
14+#include "ascir_ops.h"
15+#include "ascir_utils.h"
16+#include "runtime_stub.h"
17+#include "graph_utils_ex.h"
18+ 
19+#define private public
20+#include "buffer_allocate/buf_que_allocator.h"
21+#include "asc_graph_builder.h"
22+#include "ascgraph_info_complete.h"
23+#include "common/l2_cache_hint_manager.h"
24+#undef private
25+#include "schedule_utils.h"
26+#include "platform_context.h"
27+ 
28+using namespace std;
29+using namespace ascir;
30+using namespace ge;
31+using namespace af::ops;
32+using namespace af::ascir_op;
33+using namespace optimize;
34+using af::testing::AscGraphBuilder;
35+ 
36+namespace optimize {
37+class SetL2CtrlTest : public ::testing::Test {
38+ protected:
39+ void SetUp() override {
40+ ge::PlatformContext::GetInstance().Reset();
41+ auto stub_v1 = std::make_shared<RuntimeStub>();
42+ RuntimeStub::SetInstance(stub_v1);
43+ }
44+ void TearDown() override {
45+ ge::PlatformContext::GetInstance().Reset();
46+ }
47+};
48+} // namespace optimize
49+ 
50+namespace {
51+af::AscGraph MakeStaticLoadStoreGraph(const std::string &name, int64_t size, int64_t data_index = 0) {
52+ auto graph = AscGraphBuilder(name)
53+ .Loops({size})
54+ .Data("data" + std::to_string(data_index), data_index, af::DT_UINT8)
55+ .Load("load", "data" + std::to_string(data_index))
56+ .Store("store", "load")
57+ .Output("output" + std::to_string(data_index), "store", data_index, af::DT_UINT8)
58+ .Build();
59+ AscGraphInfoComplete::CompleteApiInfo(graph);
60+ return graph;
61+}
62+ 
63+ascir::FusedScheduledResult MakeFusedScheduledResultWithGraphs(std::vector<af::AscGraph> &&impl_graphs) {
64+ ascir::FusedScheduledResult fused_result{};
65+ fused_result.node_idx_to_scheduled_results.resize(1UL);
66+ auto &scheduled_result = fused_result.node_idx_to_scheduled_results[0].emplace_back();
67+ auto &group = scheduled_result.schedule_groups.emplace_back();
68+ group.impl_graphs = std::move(impl_graphs);
69+ return fused_result;
70+}
71+ 
72+struct L2CtrlTestResult {
73+ af::AscGraph hint_graph;
74+ ascir::FusedScheduledResult fused_result;
75+};
76+L2CtrlTestResult BuildResult(std::vector<af::AscGraph> &&impl_graphs) {
77+ af::AscGraph hint_graph = impl_graphs.front();
78+ auto fused_result = MakeFusedScheduledResultWithGraphs(std::move(impl_graphs));
79+ BufQueAllocator allocator;
80+ EXPECT_EQ(allocator.PrepareImplGraphMemoryPlan(fused_result), af::SUCCESS);
81+ EXPECT_EQ(allocator.CollectFusedIoNodes(fused_result), af::SUCCESS);
82+ return {std::move(hint_graph), std::move(fused_result)};
83+}
84+ 
85+af::ComputeGraphPtr ToComputeGraph(const af::AscGraph &hint_graph) {
86+ auto compute_graph = af::AscGraphUtils::GetComputeGraph(hint_graph);
87+ EXPECT_NE(compute_graph, nullptr);
88+ return compute_graph;
89+}
90+ 
91+bool GetDataSkipHintFromGraph(const af::AscGraph &impl_graph, const std::string &data_name) {
92+ for (const auto &node : impl_graph.GetAllNodes()) {
93+ if (node != nullptr && node->GetName() == data_name && af::ops::IsOps<af::ascir_op::Data>(node)) {
94+ bool value = false;
95+ af::AttrUtils::GetBool(node->GetOpDesc(), "_skip_l2_cache_hint", value);
96+ return value;
97+ }
98+ }
99+ return false;
100+}
101+ 
102+bool GetDataSkipHintFromScheduledResult(const ascir::ScheduledResult &scheduled_result, const std::string &data_name) {
103+ for (const auto &schedule_group : scheduled_result.schedule_groups) {
104+ for (const auto &impl_graph : schedule_group.impl_graphs) {
105+ if (GetDataSkipHintFromGraph(impl_graph, data_name)) {
106+ return true;
107+ }
108+ }
109+ }
110+ return false;
111+}
112+ 
113+bool GetDataSkipHint(ascir::FusedScheduledResult &fsr, const std::string &data_name) {
114+ for (auto &scheduled_results : fsr.node_idx_to_scheduled_results) {
115+ for (auto &scheduled_result : scheduled_results) {
116+ if (GetDataSkipHintFromScheduledResult(scheduled_result, data_name)) {
117+ return true;
118+ }
119+ }
120+ }
121+ return false;
122+}
123+ 
124+af::AscGraph MakeMultiSuccessorGraph(const std::string &name, int64_t size) {
125+ auto graph = AscGraphBuilder(name)
126+ .Loops({size})
127+ .Data("data0", 0, af::DT_UINT8)
128+ .Load("load0", "data0")
129+ .Store("store0", "load0")
130+ .Output("output0", "store0", 0, af::DT_UINT8)
131+ .Load("load1", "data0")
132+ .Store("store1", "load1")
133+ .Output("output1", "store1", 1, af::DT_UINT8)
134+ .Build();
135+ AscGraphInfoComplete::CompleteApiInfo(graph);
136+ return graph;
137+}
138+ 
139+af::AscGraph MakeMultiDataSameIndexGraph(const std::string &name, int64_t size) {
140+ auto graph = AscGraphBuilder(name)
141+ .Loops({size})
142+ .Data("data0", 0, af::DT_UINT8)
143+ .Load("load0", "data0")
144+ .Store("store0", "load0")
145+ .Output("output0", "store0", 0, af::DT_UINT8)
146+ .Data("data1", 0, af::DT_UINT8)
147+ .Load("load1", "data1")
148+ .Store("store1", "load1")
149+ .Output("output1", "store1", 1, af::DT_UINT8)
150+ .Build();
151+ AscGraphInfoComplete::CompleteApiInfo(graph);
152+ return graph;
153+}
154+} // namespace
155+ 
156+TEST_F(SetL2CtrlTest, CalcTensorSizesBasic) {
157+ constexpr int64_t kInputSize = 16;
158+ auto [hint_graph, fused_result] = BuildResult({MakeStaticLoadStoreGraph("g0", kInputSize, 0)});
159+ 
160+ ascir::GmTensorSizes sizes;
161+ EXPECT_EQ(optimize::L2CacheHintManager::CalcTensorSizes(*ToComputeGraph(hint_graph), fused_result, sizes),
162+ af::SUCCESS);
163+ ASSERT_EQ(sizes.input_sizes.size(), 1UL);
164+ ASSERT_EQ(sizes.output_sizes.size(), 1UL);
165+ EXPECT_TRUE(sizes.total_size.IsValid());
166+ EXPECT_TRUE(sizes.input_sizes[0].IsValid());
167+ EXPECT_TRUE(sizes.output_sizes[0].IsValid());
168+ EXPECT_EQ(sizes.min_total_size, 2LL * kInputSize);
169+ EXPECT_TRUE(sizes.total_size.IsConstExpr());
170+ EXPECT_EQ(sizes.input_sizes[0].IsConstExpr(), true);
171+ EXPECT_EQ(sizes.output_sizes[0].IsConstExpr(), true);
172+}
173+ 
174+TEST_F(SetL2CtrlTest, CalcTensorSizesWithSymbolicDim) {
175+ auto graph = AscGraphBuilder("sym_g")
176+ .Loops({af::testing::Sym("sym_n")})
177+ .Data("sym_data", 0, af::DT_UINT8)
178+ .Load("sym_load", "sym_data")
179+ .Store("sym_store", "sym_load")
180+ .Output("sym_output", "sym_store", 0, af::DT_UINT8)
181+ .Build();
182+ AscGraphInfoComplete::CompleteApiInfo(graph);
183+ 
184+ af::AscGraph hint_graph = graph;
185+ auto fused_result = MakeFusedScheduledResultWithGraphs({graph});
186+ BufQueAllocator allocator;
187+ EXPECT_EQ(allocator.PrepareImplGraphMemoryPlan(fused_result), af::SUCCESS);
188+ EXPECT_EQ(allocator.CollectFusedIoNodes(fused_result), af::SUCCESS);
189+ 
190+ ascir::GmTensorSizes sizes;
191+ EXPECT_EQ(optimize::L2CacheHintManager::CalcTensorSizes(*ToComputeGraph(hint_graph), fused_result, sizes),
192+ af::SUCCESS);
193+ ASSERT_EQ(sizes.input_sizes.size(), 1UL);
194+ ASSERT_EQ(sizes.output_sizes.size(), 1UL);
195+ EXPECT_FALSE(sizes.input_sizes[0].IsConstExpr());
196+ EXPECT_FALSE(sizes.output_sizes[0].IsConstExpr());
197+ EXPECT_FALSE(sizes.total_size.IsConstExpr());
198+ EXPECT_EQ(sizes.min_total_size, 2);
199+}
200+ 
201+TEST_F(SetL2CtrlTest, MarkSkipHintMultiSuccessor) {
202+ auto fused_result = MakeFusedScheduledResultWithGraphs({MakeMultiSuccessorGraph("g0", 16)});
203+ EXPECT_EQ(optimize::L2CacheHintManager::MarkInputsNeedSkipL2CacheHint(fused_result), af::SUCCESS);
204+ EXPECT_TRUE(GetDataSkipHint(fused_result, "data0"));
205+}
206+ 
207+TEST_F(SetL2CtrlTest, MarkSkipHintNormalNotSet) {
208+ auto fused_result = MakeFusedScheduledResultWithGraphs({MakeStaticLoadStoreGraph("g0", 3 * 1024 * 1024, 0)});
209+ EXPECT_EQ(optimize::L2CacheHintManager::MarkInputsNeedSkipL2CacheHint(fused_result), af::SUCCESS);
210+ EXPECT_FALSE(GetDataSkipHint(fused_result, "data0"));
211+}
212+ 
213+TEST_F(SetL2CtrlTest, MarkSkipHintMultiDataSameIndex) {
214+ auto fused_result = MakeFusedScheduledResultWithGraphs({MakeMultiDataSameIndexGraph("g0", 16)});
215+ EXPECT_EQ(optimize::L2CacheHintManager::MarkInputsNeedSkipL2CacheHint(fused_result), af::SUCCESS);
216+ EXPECT_TRUE(GetDataSkipHint(fused_result, "data0"));
217+ EXPECT_TRUE(GetDataSkipHint(fused_result, "data1"));
218+}