已合并
【PR】: [feat] 支持设置L2CacheHint #1538
xchu42创建于 7月28日
【PR】: [feat] 支持设置L2CacheHint #1538
已合并
共 11 个文件变更+786-4
| @@ -29,6 +29,7 @@ | |||
| 29 | 29 | ||
| 30 | 30 | ||
| 31 | 31 | ||
| 32 | + | ||
| 32 | 33 | ||
| 33 | 34 | ||
| 34 | using namespace std; | 35 | using 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 | + | ||
| 2000 | Status Kernel::AppendConstTensorInit(std::stringstream &ss) const { | 2053 | Status 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 | + | ||
| 3448 | void Kernel::AppendFuncCall(std::stringstream &ss, std::vector<std::vector<std::string>>::const_iterator begin, | 3527 | void 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) { |
| @@ -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 codegen | 572 | } // namespace codegen |
| 568 | 573 | ||
| @@ -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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 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 | ||
| @@ -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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 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 | + | ||
| @@ -19,6 +19,7 @@ const char *kSocInfo = "SoCInfo"; | |||
| 19 | const char *kAICoreSpec = "AICoreSpec"; | 19 | const char *kAICoreSpec = "AICoreSpec"; |
| 20 | const char *kVectorCoreCnt = "vector_core_cnt"; | 20 | const char *kVectorCoreCnt = "vector_core_cnt"; |
| 21 | const char *kUbSize = "ub_size"; | 21 | const char *kUbSize = "ub_size"; |
| 22 | +const char *kL2Size = "l2_size"; | ||
| 22 | 23 | ||
| 23 | bool ParseInt64(const char *value, const char *key_name, int64_t &result) { | 24 | bool 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 | } |
| @@ -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 | + | ||
| 48 | struct FusedScheduledResult { | 55 | struct 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 | ||
| 57 | enum class TemplateId : int64_t { | 65 | enum class TemplateId : int64_t { |
| @@ -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 | ||
| 25 | class PlatformContext { | 26 | class 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 | } |
| @@ -33,6 +33,7 @@ | |||
| 33 | 33 | ||
| 34 | 34 | ||
| 35 | 35 | ||
| 36 | + | ||
| 36 | 37 | ||
| 37 | using namespace ascir; | 38 | using namespace ascir; |
| 38 | using namespace optimize; | 39 | using 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); |
| @@ -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"); |
| @@ -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) { |
| @@ -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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 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 | +} | ||