已合并
feat: 支持vectorfunc动态stride性能公式 #1208
feat: 支持vectorfunc动态stride性能公式 #1208
已合并
gaoxin创建于 7月6日
共 18 个文件变更+599-114
@@ -22,30 +22,6 @@ Expr GetDataTypeSize(const std::string &data_type) {
22 }22 }
23 return iter->second;23 return iter->second;
24}24}
25-bool IsLoadStoreVfApi(const std::string &micro_api_type) {
26- const std::set<std::string> kLoadStoreVfType{kMoveGmToUb, kMoveUbToGm};
27- return kLoadStoreVfType.find(micro_api_type) != kLoadStoreVfType.end();
28-}
29- 
30-// 获取vf api的基础latency和throughput
31-af::Status GetVFNodePerf(const NodePerfInfo &node_info, const uint32_t micro_api_len, Expr &latency, Expr &throughput) {
32- GE_ASSERT_SUCCESS(VfPerfUtils::GetVfInstructPerf(node_info.optype, node_info.input_dtype, latency, throughput));
33- Expr dim_product = std::accumulate(node_info.dims.begin(), node_info.dims.end(), CreateExpr(1),
34- [](const Expr &a, const Expr &b) { return a * b; });
35- // 简化计算,后续进一步考虑每个op的latency和throughput的掩盖
36- Expr shape_size = dim_product * GetDataTypeSize(node_info.input_dtype);
37- Expr api_count = af::sym::Ceiling(shape_size / CreateExpr(micro_api_len));
38- api_count = api_count.Simplify();
39- throughput = throughput * api_count;
40- throughput = throughput.Simplify();
41- GELOGD(
42- "[VF_PERF_DFX] node[%s], input_dtype[%s], dims[%s], dim_product[%s], shape_size[%s], micro_api_len[%u], "
43- "api_count[%s], latency[%s], throughput[%s]",
44- node_info.optype.c_str(), node_info.input_dtype.c_str(), GetVecString(node_info.dims).c_str(),
45- dim_product.Serialize().get(), shape_size.Serialize().get(), micro_api_len, api_count.Serialize().get(),
46- latency.Serialize().get(), throughput.Serialize().get());
47- return af::SUCCESS;
48-}
49 25 
50const PerfParamTable *GetParamPerfTable() {26const PerfParamTable *GetParamPerfTable() {
51 const auto default_impl = ascgen_utils::GetAscIrAttImpl(kDefaultApi);27 const auto default_impl = ascgen_utils::GetAscIrAttImpl(kDefaultApi);
@@ -56,6 +32,65 @@ const PerfParamTable *GetParamPerfTable() {
56 GE_ASSERT_NOTNULL(api_perf);32 GE_ASSERT_NOTNULL(api_perf);
57 return api_perf->GetPerfParam();33 return api_perf->GetPerfParam();
58}34}
35+ 
36+Expr GetVFApiCount(const NodePerfInfo &node_info, const uint32_t micro_api_len, bool strides_equal,
37+ const ascir_param::VectorFuncNodeParams &vector_func_params) {
38+ const Expr data_type_size = GetDataTypeSize(node_info.input_dtype);
39+ if (vector_func_params.is_double_loop && strides_equal) {
40+ const Expr element_count = vector_func_params.all_strides[0] * vector_func_params.output_dims[0];
41+ return af::sym::Ceiling(element_count * data_type_size / CreateExpr(micro_api_len));
42+ }
43+ if (vector_func_params.is_double_loop && !strides_equal) {
44+ GE_ASSERT_TRUE(vector_func_params.output_dims.size() >= 2U,
45+ "VectorFunc double loop with unequal strides requires output_dims size >= 2, but got %zu.",
46+ vector_func_params.output_dims.size());
47+ return vector_func_params.output_dims[0] *
Z

直接访问 all_strides[0]、output_dims[0/1],建议在进入 double-loop 分支前显式校验 output_dims.size() >= 2,equal 分支校验 !all_strides.empty()

likedislike
gaoxin
7月8日 评论:
48+ af::sym::Ceiling(vector_func_params.output_dims[1] * data_type_size / CreateExpr(micro_api_len));
49+ }
50+ Expr dim_product = std::accumulate(node_info.dims.begin(), node_info.dims.end(), CreateExpr(1),
51+ [](const Expr &a, const Expr &b) { return a * b; });
52+ return af::sym::Ceiling(dim_product * data_type_size / CreateExpr(micro_api_len));
53+}
54+ 
55+// 获取vf api的基础latency和throughput
56+af::Status GetVFNodePerf(const NodePerfInfo &node_info, const uint32_t micro_api_len, bool strides_equal,
57+ const ascir_param::VectorFuncNodeParams &vector_func_params, Expr &latency, Expr &throughput) {
58+ GE_ASSERT_SUCCESS(VfPerfUtils::GetVfInstructPerf(node_info.optype, node_info.input_dtype, latency, throughput));
59+ // 简化计算,后续进一步考虑每个op的latency和throughput的掩盖
60+ Expr api_count = GetVFApiCount(node_info, micro_api_len, strides_equal, vector_func_params);
61+ api_count = api_count.Simplify();
62+ throughput = throughput * api_count;
63+ throughput = throughput.Simplify();
64+ GELOGD("Got node %s input %s reg base latency %s, throughput %s, api_count %s", node_info.optype.c_str(),
65+ node_info.input_dtype.c_str(), latency.Serialize().get(), throughput.Serialize().get(),
66+ api_count.Serialize().get());
67+ return af::SUCCESS;
68+}
69+ 
70+af::Status GetVectorFunctionPerfByStrideStatus(const std::vector<NodePerfInfo> &node_perf_infos,
71+ const uint32_t micro_api_len, bool strides_equal,
72+ const ascir_param::VectorFuncNodeParams &vector_func_params, Expr &res) {
73+ Expr all_micro_api_cost = CreateExpr(0);
74+ Expr max_latency = CreateExpr(0);
75+ for (const auto &node_info : node_perf_infos) {
76+ Expr latency = CreateExpr(0);
77+ Expr throughput = CreateExpr(0);
78+ GE_ASSERT_SUCCESS(GetVFNodePerf(node_info, micro_api_len, strides_equal, vector_func_params, latency, throughput));
79+ all_micro_api_cost = af::sym::Add(all_micro_api_cost, throughput);
80+ max_latency = af::sym::Max(max_latency, latency);
81+ }
82+ max_latency = max_latency.Simplify();
83+ all_micro_api_cost = all_micro_api_cost.Simplify();
84+ res = af::sym::Add(all_micro_api_cost, max_latency);
85+ const auto vector_function_head_cost = GetParamPerfTable()->GetVectorFunctionHeadCost();
86+ res = af::sym::Add(res, vector_function_head_cost);
87+ res = res.Simplify();
88+ GELOGD("Got vector function perf %s, vector_function_head_cost %s, max_latency %s, all_micro_api_cost %s",
89+ res.Serialize().get(), vector_function_head_cost.Serialize().get(), max_latency.Serialize().get(),
90+ all_micro_api_cost.Serialize().get());
91+ return af::SUCCESS;
92+}
93+ 
59} // namespace94} // namespace
60 95 
61af::Status VfPerfUtils::GetVfInstructPerf(const std::string &micro_api_type, const std::string &data_type,96af::Status VfPerfUtils::GetVfInstructPerf(const std::string &micro_api_type, const std::string &data_type,
@@ -106,35 +141,40 @@ Expr VfPerfUtils::GetVFHeadCost() {
106// 5.Micro Api的并发度141// 5.Micro Api的并发度
107// 第一版建模简化模型,仅考虑1,2,3,每个op的latency求最大值,throughput求和142// 第一版建模简化模型,仅考虑1,2,3,每个op的latency求最大值,throughput求和
108// 后续建模考虑4,5143// 后续建模考虑4,5
109-af::Status VfPerfUtils::GetVectorFunctionPerf(const std::vector<NodePerfInfo> &node_perf_infos, Expr &res) {144+af::Status VfPerfUtils::GetVectorFunctionPerf(const std::vector<NodePerfInfo> &node_perf_infos,
145+ const ascir_param::VectorFuncNodeParams &vector_func_params,
146+ std::map<Expr, TernaryOp, ExprCmp> &ternary_ops, Expr &res) {
110 const auto param_table = GetParamPerfTable();147 const auto param_table = GetParamPerfTable();
111 GE_ASSERT_NOTNULL(param_table);148 GE_ASSERT_NOTNULL(param_table);
112 const uint32_t micro_api_len = param_table->GetMicroApiLen();149 const uint32_t micro_api_len = param_table->GetMicroApiLen();
113- Expr all_micro_api_cost = CreateExpr(0);150+ const auto &all_strides = vector_func_params.all_strides;
114- Expr max_latency = CreateExpr(0);151+ if (!vector_func_params.is_double_loop) {
115- for (const auto &node_info : node_perf_infos) {152+ return GetVectorFunctionPerfByStrideStatus(node_perf_infos, micro_api_len, false, vector_func_params, res);
116- Expr latency = CreateExpr(0);
117- Expr throughput = CreateExpr(0);
118- if (IsLoadStoreVfApi(node_info.optype)) {
119- // Load Store VF API暂认为非主导因素,待后续支持搬运类算子或Brc算子时再进行建模
120- continue;
121- }
122- // 在vf function内的必须支持vector, 待校验
123- GE_ASSERT_SUCCESS(GetVFNodePerf(node_info, micro_api_len, latency, throughput));
124- // 每个op的latency相加, throughput求最大值
125- all_micro_api_cost = af::sym::Add(all_micro_api_cost, throughput);
126- max_latency = af::sym::Max(max_latency, latency);
127 }153 }
128- max_latency = max_latency.Simplify();154+ if (all_strides.empty()) {
129- all_micro_api_cost = all_micro_api_cost.Simplify();155+ return GetVectorFunctionPerfByStrideStatus(node_perf_infos, micro_api_len, false, vector_func_params, res);
130- // 加上最大的latency156+ }
131- res = af::sym::Add(all_micro_api_cost, max_latency);157+ if (all_strides.size() == 1U) {
132- const auto vector_function_head_cost = param_table->GetVectorFunctionHeadCost();158+ return GetVectorFunctionPerfByStrideStatus(node_perf_infos, micro_api_len, true, vector_func_params, res);
133- res = af::sym::Add(res, vector_function_head_cost);159+ }
134- res = res.Simplify();160+ Expr strides_equal_perf;
135- GELOGD("Got vector function perf %s, vector_function_head_cost %s, max_latency %s, all_micro_api_cost %s",161+ Expr strides_unequal_perf;
136- res.Serialize().get(), vector_function_head_cost.Serialize().get(), max_latency.Serialize().get(),162+ GE_ASSERT_SUCCESS(GetVectorFunctionPerfByStrideStatus(node_perf_infos, micro_api_len, true, vector_func_params,
137- all_micro_api_cost.Serialize().get());163+ strides_equal_perf));
164+ GE_ASSERT_SUCCESS(GetVectorFunctionPerfByStrideStatus(node_perf_infos, micro_api_len, false, vector_func_params,
165+ strides_unequal_perf));
166+ res = CreateExpr("vf_dynamic_perf");
167+ GetPerfVar("vf_dynamic_perf", res, ternary_ops);
168+ std::shared_ptr<IfCase> equal_case = std::make_shared<IfCase>(strides_equal_perf);
169+ for (size_t i = all_strides.size() - 1U; i > 0U; --i) {
170+ equal_case = std::make_shared<IfCase>(CondType::K_EQ, all_strides[0], all_strides[i], std::move(equal_case),
171+ std::make_shared<IfCase>(strides_unequal_perf));
172+ }
173+ std::vector<Expr> related_vars;
174+ equal_case->GetUsedArgs(related_vars);
175+ ternary_ops[res] = TernaryOp(res, std::move(equal_case), related_vars);
176+ ternary_ops[res].SetVariable(res);
177+ ternary_ops[res].SetDescription("vf_dynamic_perf");
138 return af::SUCCESS;178 return af::SUCCESS;
139}179}
140} // namespace att180} // namespace att
@@ -12,7 +12,9 @@
12#define AUTOFUSE_VF_PERF_UTILS_H12#define AUTOFUSE_VF_PERF_UTILS_H
13 13 
14#include "base/att_const_values.h"14#include "base/att_const_values.h"
15+#include "ascir_node_param/ascir_node_param.h"
15#include "gen_model_info/api_perf_register/perf_param.h"16#include "gen_model_info/api_perf_register/perf_param.h"
17+#include "util/ternary_op.h"
16namespace att {18namespace att {
17class VfPerfUtils {19class VfPerfUtils {
18 public:20 public:
@@ -26,7 +28,9 @@ class VfPerfUtils {
26 // 获取vf头开销28 // 获取vf头开销
27 static Expr GetVFHeadCost();29 static Expr GetVFHeadCost();
28 // 根据vf function子图解析的结果获取vf function的性能30 // 根据vf function子图解析的结果获取vf function的性能
29- static af::Status GetVectorFunctionPerf(const std::vector<NodePerfInfo> &node_perf_infos, Expr &res);31+ static af::Status GetVectorFunctionPerf(const std::vector<NodePerfInfo> &node_perf_infos,
32+ const ascir_param::VectorFuncNodeParams &vector_func_params,
33+ std::map<Expr, TernaryOp, ExprCmp> &ternary_ops, Expr &res);
30};34};
31} // namespace att35} // namespace att
32 36 
@@ -587,7 +587,7 @@ af::Status PipePerfExpr::GetPerfExpr(std::map<PipeType, Expr> &pipe_costs,
587// 获取节点性能(内部方法,包含VectorFunc特殊处理)587// 获取节点性能(内部方法,包含VectorFunc特殊处理)
588af::Status PipePerfExpr::GetNodePerfInternal(const NodeInfo &node, std::map<PipeType, Expr> &node_perf,588af::Status PipePerfExpr::GetNodePerfInternal(const NodeInfo &node, std::map<PipeType, Expr> &node_perf,
589 std::map<Expr, TernaryOp, ExprCmp> &ternary_ops,589 std::map<Expr, TernaryOp, ExprCmp> &ternary_ops,
590- std::vector<PerfBreakdownGroup> &perf_breakdowns) const {590+ std::vector<PerfBreakdownGroup> &perf_breakdowns, bool tail_shape) const {
591 if (node.node_type == kVectorFunc) {591 if (node.node_type == kVectorFunc) {
592 // VectorFunc节点特殊处理592 // VectorFunc节点特殊处理
593 std::vector<NodePerfInfo> node_perf_infos;593 std::vector<NodePerfInfo> node_perf_infos;
@@ -597,7 +597,7 @@ af::Status PipePerfExpr::GetNodePerfInternal(const NodeInfo &node, std::map<Pipe
597 GE_ASSERT_SUCCESS(ConvertToPerfInfo(node.sub_nodes_infos, node_perf_infos, vector_func_dims),597 GE_ASSERT_SUCCESS(ConvertToPerfInfo(node.sub_nodes_infos, node_perf_infos, vector_func_dims),
598 "Convert to perf info failed, node = %s %s", node.name.c_str(), node.node_type.c_str());598 "Convert to perf info failed, node = %s %s", node.name.c_str(), node.node_type.c_str());
599 Expr res;599 Expr res;
600- GE_ASSERT_SUCCESS(VfPerfUtils::GetVectorFunctionPerf(node_perf_infos, res),600+ GE_ASSERT_SUCCESS(VfPerfUtils::GetVectorFunctionPerf(node_perf_infos, node.vector_func_params, ternary_ops, res),
601 "Get vector function perf failed, node = %s %s.", node.name.c_str(), node.node_type.c_str());601 "Get vector function perf failed, node = %s %s.", node.name.c_str(), node.node_type.c_str());
602 602 
603 // 为VectorFunc节点创建ternary_ops条目603 // 为VectorFunc节点创建ternary_ops条目
@@ -620,8 +620,8 @@ af::Status PipePerfExpr::GetNodePerfInternal(const NodeInfo &node, std::map<Pipe
620 }620 }
621 } else {621 } else {
622 // 普通节点622 // 普通节点
623- GE_ASSERT_SUCCESS(GetNodePerf(node, node_perf, ternary_ops, perf_breakdowns), "Get node [%s][%s] perf failed.",623+ GE_ASSERT_SUCCESS(GetNodePerf(node, node_perf, ternary_ops, perf_breakdowns, tail_shape),
624- node.name.c_str(), node.node_type.c_str());624+ "Get node [%s][%s] perf failed.", node.name.c_str(), node.node_type.c_str());
625 }625 }
626 return af::SUCCESS;626 return af::SUCCESS;
627}627}
@@ -639,7 +639,7 @@ af::Status PipePerfExpr::AddNodePerfToPipeCost(const NodeInfo &node, const Expr
639 GELOGD("Node with single cut, add tail perf.");639 GELOGD("Node with single cut, add tail perf.");
640 Expr tail_exe_time;640 Expr tail_exe_time;
641 std::map<PipeType, Expr> node_tail_perf;641 std::map<PipeType, Expr> node_tail_perf;
642- GE_ASSERT_SUCCESS(GetNodePerf(node, node_tail_perf, ternary_ops, perf_breakdowns, true),642+ GE_ASSERT_SUCCESS(GetNodePerfInternal(node, node_tail_perf, ternary_ops, perf_breakdowns, true),
643 "Get node [%s] tail perf failed.", node.name.c_str());643 "Get node [%s] tail perf failed.", node.name.c_str());
644 GE_ASSERT_SUCCESS(GetTailExeTime(node, exe_var, tail_exe_time));644 GE_ASSERT_SUCCESS(GetTailExeTime(node, exe_var, tail_exe_time));
645 GE_ASSERT_SUCCESS(AddTailPerf(tail_exe_time, exe_var, node_tail_perf, ctx));645 GE_ASSERT_SUCCESS(AddTailPerf(tail_exe_time, exe_var, node_tail_perf, ctx));
@@ -74,7 +74,7 @@ class PipePerfExpr {
74 // 获取节点性能(内部方法,包含VectorFunc特殊处理)74 // 获取节点性能(内部方法,包含VectorFunc特殊处理)
75 af::Status GetNodePerfInternal(const NodeInfo &node, std::map<PipeType, Expr> &node_perf,75 af::Status GetNodePerfInternal(const NodeInfo &node, std::map<PipeType, Expr> &node_perf,
76 std::map<Expr, TernaryOp, ExprCmp> &ternary_ops,76 std::map<Expr, TernaryOp, ExprCmp> &ternary_ops,
77- std::vector<PerfBreakdownGroup> &perf_breakdowns) const;77+ std::vector<PerfBreakdownGroup> &perf_breakdowns, bool tail_shape = false) const;
78 // 添加节点性能到pipe_costs78 // 添加节点性能到pipe_costs
79 af::Status AddNodePerfToPipeCost(const NodeInfo &node, const Expr &exe_var, const std::map<PipeType, Expr> &node_perf,79 af::Status AddNodePerfToPipeCost(const NodeInfo &node, const Expr &exe_var, const std::map<PipeType, Expr> &node_perf,
80 std::map<PipeType, Expr> &pipe_costs,80 std::map<PipeType, Expr> &pipe_costs,
@@ -21,9 +21,10 @@
21#include "util/thread_local_context.h"21#include "util/thread_local_context.h"
22#include "att_utils.h"22#include "att_utils.h"
23#include "ascendc_ir/ascendc_ir_core/ascendc_ir.h"23#include "ascendc_ir/ascendc_ir_core/ascendc_ir.h"
24+#include "ascir_node_param/ascir_node_param.h"
24#include "base_types_printer.h"25#include "base_types_printer.h"
25#include "common_utils.h"26#include "common_utils.h"
26-#include "reduce_specific_params_builder.h"27+#include "specific_params_builder.h"
27#include "vector_function_graph_parser.h"28#include "vector_function_graph_parser.h"
28 29 
29namespace att {30namespace att {
@@ -770,7 +771,7 @@ af::Status AscendGraphParser::ConvertNodeInfos(const af::AscNodePtr &ge_node, co
770 }771 }
771 }772 }
772 node_info.node_ptr = ge_node;773 node_info.node_ptr = ge_node;
773- GE_ASSERT_SUCCESS(FillReduceSpecificParams(ge_node, node_info));774+ GE_ASSERT_SUCCESS(FillSpecificParams(ge_node, node_info));
774 auto vectorized_axis_ids = GetNodeVectorizedAxis(ge_node, loop_axis);775 auto vectorized_axis_ids = GetNodeVectorizedAxis(ge_node, loop_axis);
775 for (const auto &axis_info : vectorized_axis_ids) {776 for (const auto &axis_info : vectorized_axis_ids) {
776 auto sub_axis_iter = sub_axes_info_.find(axis_info);777 auto sub_axis_iter = sub_axes_info_.find(axis_info);
Rautofuse/att/gen_model_info/parser/reduce_specific_params_builder.cpp→autofuse/att/gen_model_info/parser/specific_params_builder.cpp+26-5
@@ -8,13 +8,31 @@
8 * See LICENSE in the root of the software repository for the full text of the License.8 * See LICENSE in the root of the software repository for the full text of the License.
9 */9 */
10 10 
11-#include "reduce_specific_params_builder.h"11+#include "specific_params_builder.h"
12 12 
13#include "ascir_node_param/ascir_node_param.h"13#include "ascir_node_param/ascir_node_param.h"
14+#include "ascir_node_param/ascir_param_builder.h"
15+#include "base/att_const_values.h"
14#include "common/checker.h"16#include "common/checker.h"
15 17 
16namespace att {18namespace att {
17-af::Status FillReduceSpecificParams(const af::AscNodePtr &ge_node, NodeInfo &node_info) {19+namespace {
20+af::Status FillReduceParams(const ascir_param::AscirNodeParams &params, NodeInfo &node_info) {
21+ const auto *reduce = ascir_param::GetSpecificParams<ascir_param::ReduceNodeParams>(params);
22+ GE_ASSERT_NOTNULL(reduce, "Reduce specific params is null, node[%s].", node_info.name.c_str());
23+ node_info.reduce_specific_params = *reduce;
24+ return af::SUCCESS;
25+}
26+ 
27+af::Status FillVectorFuncParams(const ascir_param::AscirNodeParams &params, NodeInfo &node_info) {
28+ const auto *vector_func_params = ascir_param::GetSpecificParams<ascir_param::VectorFuncNodeParams>(params);
29+ GE_ASSERT_NOTNULL(vector_func_params, "VectorFunc specific params is null, node[%s].", node_info.name.c_str());
30+ node_info.vector_func_params = *vector_func_params;
31+ return af::SUCCESS;
32+}
33+} // namespace
34+ 
35+af::Status FillSpecificParams(const af::AscNodePtr &ge_node, NodeInfo &node_info) {
18 const auto params = ascir_param::GetAscirNodeParams(ge_node);36 const auto params = ascir_param::GetAscirNodeParams(ge_node);
19 if (params == nullptr) {37 if (params == nullptr) {
20 return af::SUCCESS;38 return af::SUCCESS;
@@ -24,9 +42,12 @@ af::Status FillReduceSpecificParams(const af::AscNodePtr &ge_node, NodeInfo &nod
24 }42 }
25 GE_ASSERT_TRUE(params->status == ascir_param::ParamBuildStatus::kBuilt, "Build ascir node params failed, node[%s].",43 GE_ASSERT_TRUE(params->status == ascir_param::ParamBuildStatus::kBuilt, "Build ascir node params failed, node[%s].",
26 node_info.name.c_str());44 node_info.name.c_str());
27- const auto *reduce = ascir_param::GetSpecificParams<ascir_param::ReduceNodeParams>(*params);45+ if (ascir_param::IsReduceParamSupported(node_info.node_type)) {
28- GE_ASSERT_NOTNULL(reduce, "Reduce specific params is null, node[%s].", node_info.name.c_str());46+ return FillReduceParams(*params, node_info);
29- node_info.reduce_specific_params = *reduce;47+ }
48+ if (node_info.node_type == kVectorFunc) {
49+ return FillVectorFuncParams(*params, node_info);
50+ }
30 return af::SUCCESS;51 return af::SUCCESS;
31}52}
32} // namespace att53} // namespace att
Rautofuse/att/gen_model_info/parser/reduce_specific_params_builder.h→autofuse/att/gen_model_info/parser/specific_params_builder.h+4-4
@@ -8,13 +8,13 @@
8 * See LICENSE in the root of the software repository for the full text of the License.8 * See LICENSE in the root of the software repository for the full text of the License.
9 */9 */
10 10 
11-#ifndef PARSER_REDUCE_SPECIFIC_PARAMS_BUILDER_H_11+#ifndef PARSER_SPECIFIC_PARAMS_BUILDER_H_
12-#define PARSER_REDUCE_SPECIFIC_PARAMS_BUILDER_H_12+#define PARSER_SPECIFIC_PARAMS_BUILDER_H_
13 13 
14#include "tuning_space.h"14#include "tuning_space.h"
15 15 
16namespace att {16namespace att {
17-af::Status FillReduceSpecificParams(const af::AscNodePtr &ge_node, NodeInfo &node_info);17+af::Status FillSpecificParams(const af::AscNodePtr &ge_node, NodeInfo &node_info);
18} // namespace att18} // namespace att
19 19 
20-#endif // PARSER_REDUCE_SPECIFIC_PARAMS_BUILDER_H_20+#endif // PARSER_SPECIFIC_PARAMS_BUILDER_H_
@@ -181,6 +181,7 @@ struct NodeInfo {
181 af::AscNodePtr node_ptr;181 af::AscNodePtr node_ptr;
182 std::set<std::string> from_data; // 隶属的Data节点名称182 std::set<std::string> from_data; // 隶属的Data节点名称
183 std::vector<NodeInfo> sub_nodes_infos;183 std::vector<NodeInfo> sub_nodes_infos;
184+ ascir_param::VectorFuncNodeParams vector_func_params;
184 ascir_param::ReduceNodeParams reduce_specific_params;185 ascir_param::ReduceNodeParams reduce_specific_params;
185 af::ExecuteCondition exec_condition{af::ExecuteCondition::kNoCache};186 af::ExecuteCondition exec_condition{af::ExecuteCondition::kNoCache};
186 std::string DebugString() const {187 std::string DebugString() const {
@@ -68,7 +68,13 @@ struct ReduceNodeParams {
68 ReduceParamExprs exprs;68 ReduceParamExprs exprs;
69};69};
70 70 
71-using AnySpecificParams = std::variant<std::monostate, ReduceNodeParams>;71+struct VectorFuncNodeParams {
72+ bool is_double_loop{false};
73+ std::vector<ge::Expression> all_strides;
74+ std::vector<ge::Expression> output_dims;
75+};
76+ 
77+using AnySpecificParams = std::variant<std::monostate, ReduceNodeParams, VectorFuncNodeParams>;
72 78 
73struct AscirNodeParams {79struct AscirNodeParams {
74 // 扩展属性载荷版本,用于后续兼容。80 // 扩展属性载荷版本,用于后续兼容。
@@ -23,6 +23,7 @@
23namespace ascir_param {23namespace ascir_param {
24namespace {24namespace {
25constexpr const char *kAscirNodeParams = "AscirNodeParams";25constexpr const char *kAscirNodeParams = "AscirNodeParams";
26+constexpr const char *kVectorFunc = "VectorFunc";
26 27 
27struct AscirParamSourceContext {28struct AscirParamSourceContext {
28 af::AscNodePtr node;29 af::AscNodePtr node;
@@ -84,8 +85,9 @@ af::Status RegisterAscirNodeParams(const af::AscNodePtr &node, const AscirNodePa
84 graph_name.c_str(), static_cast<void *>(node.get()), static_cast<void *>(op_desc.get()),85 graph_name.c_str(), static_cast<void *>(node.get()), static_cast<void *>(op_desc.get()),
85 static_cast<void *>(owner_graph.get()));86 static_cast<void *>(owner_graph.get()));
86 }87 }
88+ const std::string graph_name = owner_graph == nullptr ? std::string("<null>") : owner_graph->GetName();
87 GE_ASSERT_TRUE(op_desc->SetExtAttr(kAscirNodeParams, params), "Graph:%s, Node:%s SetExtAttr failed",89 GE_ASSERT_TRUE(op_desc->SetExtAttr(kAscirNodeParams, params), "Graph:%s, Node:%s SetExtAttr failed",
88- node->GetOwnerComputeGraph()->GetName().c_str(), node->GetNamePtr());90+ graph_name.c_str(), node->GetNamePtr());
89 return af::SUCCESS;91 return af::SUCCESS;
90}92}
91 93 
@@ -97,6 +99,15 @@ af::Status RegisterSkippedAscirNodeParams(const af::AscNodePtr &node) {
97 return RegisterAscirNodeParams(node, params);99 return RegisterAscirNodeParams(node, params);
98}100}
99 101 
102+af::Status RegisterVectorFuncAscirNodeParams(const af::AscNodePtr &node) {
103+ GE_ASSERT_NOTNULL(node);
104+ auto params = std::make_shared<AscirNodeParams>();
105+ params->api_name = node->GetType();
106+ params->status = ParamBuildStatus::kBuilt;
107+ params->specific_params = VectorFuncNodeParams{};
108+ return RegisterAscirNodeParams(node, params);
109+}
110+ 
100std::map<int64_t, af::AxisPtr> BuildAxisMap(const af::AscGraph &graph) {111std::map<int64_t, af::AxisPtr> BuildAxisMap(const af::AscGraph &graph) {
101 std::map<int64_t, af::AxisPtr> axis_map;112 std::map<int64_t, af::AxisPtr> axis_map;
102 for (const auto &axis : graph.GetAllAxis()) {113 for (const auto &axis : graph.GetAllAxis()) {
@@ -414,13 +425,6 @@ af::Status FillReduceReuseSource(const af::AscNodePtr &node, codegen::ReduceReus
414 return af::SUCCESS;425 return af::SUCCESS;
415}426}
416 427 
417-bool IsReduceParamSupported(const std::string &api_name) {
418- static const std::set<std::string> kReduceTypes = {"Max", "Min", "Mean", "Prod", "Sum",
419- "Any", "All", "ReduceMax", "ReduceMean", "ReduceMin",
420- "ReduceProd", "ReduceSum", "ReduceAny", "ReduceAll"};
421- return kReduceTypes.count(api_name) != 0U;
422-}
423- 
424af::Status BuildReduceInput(const AscirParamBuildContext &ctx, codegen::ReduceSpecificParamBuildInput &input) {428af::Status BuildReduceInput(const AscirParamBuildContext &ctx, codegen::ReduceSpecificParamBuildInput &input) {
425 GE_ASSERT_TRUE(!ctx.inputs.empty(), "Reduce input tensor is empty, node[%s].", ctx.api_name.c_str());429 GE_ASSERT_TRUE(!ctx.inputs.empty(), "Reduce input tensor is empty, node[%s].", ctx.api_name.c_str());
426 GE_ASSERT_TRUE(!ctx.outputs.empty(), "Reduce output tensor is empty, node[%s].", ctx.api_name.c_str());430 GE_ASSERT_TRUE(!ctx.outputs.empty(), "Reduce output tensor is empty, node[%s].", ctx.api_name.c_str());
@@ -497,6 +501,9 @@ af::Status BuildAndRegisterAscirNodeParams(const AscirParamBuildContext &ctx) {
497 501 
498af::Status EnrichAscirNodeParams(const AscirParamSourceContext &source) {502af::Status EnrichAscirNodeParams(const AscirParamSourceContext &source) {
499 GE_ASSERT_NOTNULL(source.node);503 GE_ASSERT_NOTNULL(source.node);
504+ if (source.node->GetType() == kVectorFunc) {
505+ return RegisterVectorFuncAscirNodeParams(source.node);
506+ }
500 if (!IsReduceParamSupported(source.node->GetType())) {507 if (!IsReduceParamSupported(source.node->GetType())) {
501 return RegisterSkippedAscirNodeParams(source.node);508 return RegisterSkippedAscirNodeParams(source.node);
502 }509 }
@@ -506,6 +513,13 @@ af::Status EnrichAscirNodeParams(const AscirParamSourceContext &source) {
506}513}
507} // namespace514} // namespace
508 515 
516+bool IsReduceParamSupported(const std::string &api_name) {
517+ static const std::set<std::string> kReduceTypes = {"Max", "Min", "Mean", "Prod", "Sum",
518+ "Any", "All", "ReduceMax", "ReduceMean", "ReduceMin",
519+ "ReduceProd", "ReduceSum", "ReduceAny", "ReduceAll"};
520+ return kReduceTypes.count(api_name) != 0U;
521+}
522+ 
509af::Status EnrichAscirGraphNodeParams(const af::AscGraph &graph) {523af::Status EnrichAscirGraphNodeParams(const af::AscGraph &graph) {
510 for (const auto &node : graph.GetAllNodes()) {524 for (const auto &node : graph.GetAllNodes()) {
511 GE_ASSERT_SUCCESS(EnrichAscirNodeParams({node, &graph}));525 GE_ASSERT_SUCCESS(EnrichAscirNodeParams({node, &graph}));
@@ -14,6 +14,7 @@
14#include "ascir_node_param/ascir_node_param.h"14#include "ascir_node_param/ascir_node_param.h"
15 15 
16namespace ascir_param {16namespace ascir_param {
17+bool IsReduceParamSupported(const std::string &api_name);
17af::Status EnrichAscirGraphNodeParams(const af::AscGraph &graph);18af::Status EnrichAscirGraphNodeParams(const af::AscGraph &graph);
18} // namespace ascir_param19} // namespace ascir_param
19 20 
@@ -12,6 +12,7 @@
12#include <iostream>12#include <iostream>
13#include "gtest/gtest.h"13#include "gtest/gtest.h"
14#include "base/att_const_values.h"14#include "base/att_const_values.h"
15+#include "gen_model_info/api_perf_register/utils/vf_perf_utils.h"
15#include "gen_model_info.h"16#include "gen_model_info.h"
16#include "test_fa_ascir_graph.h"17#include "test_fa_ascir_graph.h"
17#include "parser/ascend_graph_parser.h"18#include "parser/ascend_graph_parser.h"
@@ -19,6 +20,7 @@
19#include "expr_gen/pipe_perf_expr.h"20#include "expr_gen/pipe_perf_expr.h"
20#undef private21#undef private
21#include "ascir_ops.h"22#include "ascir_ops.h"
23+#include "ascir_node_param/ascir_param_builder.h"
22#include "expr_gen/arg_list_manager.h"24#include "expr_gen/arg_list_manager.h"
23#include "ascendc_ir/ascendc_ir_core/ascendc_ir.h"25#include "ascendc_ir/ascendc_ir_core/ascendc_ir.h"
24#include "graph_construct_utils.h"26#include "graph_construct_utils.h"
@@ -176,6 +178,34 @@ NodeInfo CreateSubNodeInfo(const std::string &name, const std::string &type, con
176 return node_info;178 return node_info;
177}179}
178 180 
181+void SetAscirNodeParamsForTest(const af::AscNodePtr &node, const ascir_param::AscirNodeParamsPtr &params) {
182+ ASSERT_NE(node, nullptr);
183+ ASSERT_NE(params, nullptr);
184+ auto op_desc = node->GetOpDesc();
185+ ASSERT_NE(op_desc, nullptr);
186+ ASSERT_TRUE(op_desc->SetExtAttr("AscirNodeParams", params));
187+}
188+ 
189+NodePerfInfo CreateVectorFuncNodePerfInfo() {
190+ NodePerfInfo node_perf_info;
191+ node_perf_info.optype = kAbs;
192+ node_perf_info.input_dtype = kFloat32;
193+ node_perf_info.output_dtype = kFloat32;
194+ node_perf_info.dims = {CreateExpr("vf_dim")};
195+ return node_perf_info;
196+}
197+ 
198+void ExpectValidPerfWithoutDynamicTernary(const ascir_param::VectorFuncNodeParams &vector_func_params) {
199+ std::vector<NodePerfInfo> node_perf_infos = {CreateVectorFuncNodePerfInfo()};
200+ std::map<Expr, TernaryOp, ExprCmp> ternary_ops;
201+ Expr perf;
202+ 
203+ ASSERT_EQ(VfPerfUtils::GetVectorFunctionPerf(node_perf_infos, vector_func_params, ternary_ops, perf), af::SUCCESS);
204+ 
205+ EXPECT_TRUE(perf.IsValid());
206+ EXPECT_TRUE(ternary_ops.empty());
207+}
208+ 
179// 辅助函数:验证 contrib 变量209// 辅助函数:验证 contrib 变量
180void VerifyContribVar(const std::map<Expr, TernaryOp, ExprCmp> &exe_times) {210void VerifyContribVar(const std::map<Expr, TernaryOp, ExprCmp> &exe_times) {
181 bool found_aiv_vec_contrib = false;211 bool found_aiv_vec_contrib = false;
@@ -293,6 +323,136 @@ TEST_F(TestPipePerfExpr, ConvertToPerfInfoUsesParentGraphDims) {
293 EXPECT_EQ(Str(node_perf_infos[0].dims[0]), "z0bt_size");323 EXPECT_EQ(Str(node_perf_infos[0].dims[0]), "z0bt_size");
294}324}
295 325 
326+TEST_F(TestPipePerfExpr, VectorFuncPerfUsesDefaultParamsWhenNoCodegenParams) {
327+ ascir_param::VectorFuncNodeParams vector_func_params;
328+ 
329+ ExpectValidPerfWithoutDynamicTernary(vector_func_params);
330+}
331+ 
332+TEST_F(TestPipePerfExpr, VectorFuncPerfUsesUnequalFormulaWhenDoubleLoopHasNoStride) {
333+ ascir_param::VectorFuncNodeParams vector_func_params;
334+ vector_func_params.is_double_loop = true;
335+ vector_func_params.output_dims = {CreateExpr("outer_dim"), CreateExpr("inner_dim")};
336+ 
337+ ExpectValidPerfWithoutDynamicTernary(vector_func_params);
338+}
339+ 
340+TEST_F(TestPipePerfExpr, VectorFuncPerfUsesEqualFormulaWhenDoubleLoopHasSingleStride) {
341+ ascir_param::VectorFuncNodeParams vector_func_params;
342+ vector_func_params.is_double_loop = true;
343+ vector_func_params.all_strides = {CreateExpr("only_stride")};
344+ vector_func_params.output_dims = {CreateExpr("outer_dim"), CreateExpr("inner_dim")};
345+ 
346+ ExpectValidPerfWithoutDynamicTernary(vector_func_params);
347+}
348+ 
349+TEST_F(TestPipePerfExpr, VectorFuncPerfCreatesDynamicStrideTernaryOp) {
350+ std::vector<NodePerfInfo> node_perf_infos = {CreateVectorFuncNodePerfInfo()};
351+ ascir_param::VectorFuncNodeParams vector_func_params;
352+ vector_func_params.is_double_loop = true;
353+ vector_func_params.all_strides = {CreateExpr("input_stride"), CreateExpr("output_stride")};
354+ vector_func_params.output_dims = {CreateExpr("outer_dim"), CreateExpr("inner_dim")};
355+ std::map<Expr, TernaryOp, ExprCmp> ternary_ops;
356+ Expr perf;
357+ 
358+ ASSERT_EQ(VfPerfUtils::GetVectorFunctionPerf(node_perf_infos, vector_func_params, ternary_ops, perf), af::SUCCESS);
359+ 
360+ EXPECT_EQ(Str(perf), "vf_dynamic_perf");
361+ auto iter = ternary_ops.find(perf);
362+ ASSERT_NE(iter, ternary_ops.end());
363+ EXPECT_EQ(iter->second.GetDescription(), "vf_dynamic_perf");
364+ const std::string ternary_expr = iter->second.GetTernaryOpStr();
365+ EXPECT_NE(ternary_expr.find("input_stride"), std::string::npos);
366+ EXPECT_NE(ternary_expr.find("output_stride"), std::string::npos);
367+}
368+ 
369+TEST_F(TestPipePerfExpr, ParserCopiesVectorFuncNodeParamsFromAscirNodeParams) {
370+ af::AscGraph graph("vf_graph");
371+ ASSERT_EQ(af::ascir::cg::BuildVectorFuncTestGraph(graph), af::SUCCESS);
372+ auto vf_node = graph.FindNode("vector_func");
373+ ASSERT_NE(vf_node, nullptr);
374+ auto params = std::make_shared<ascir_param::AscirNodeParams>();
375+ ascir_param::VectorFuncNodeParams expected_params;
376+ expected_params.is_double_loop = true;
377+ expected_params.all_strides = {CreateExpr("input_stride"), CreateExpr("output_stride")};
378+ expected_params.output_dims = {CreateExpr("outer_dim"), CreateExpr("inner_dim")};
379+ params->api_name = kVectorFunc;
380+ params->status = ascir_param::ParamBuildStatus::kBuilt;
381+ params->specific_params = expected_params;
382+ SetAscirNodeParamsForTest(vf_node, params);
383+ 
384+ TuningSpacePtr ts = std::make_shared<TuningSpace>();
385+ ASSERT_NE(ts, nullptr);
386+ att::AscendGraphParser ascend_graph_parser(ts);
387+ ASSERT_EQ(ascend_graph_parser.GraphParser(graph), af::SUCCESS);
388+ 
389+ const auto vector_func_iter = std::find_if(ts->node_infos.begin(), ts->node_infos.end(),
390+ [](const NodeInfo &node) { return node.node_type == kVectorFunc; });
391+ ASSERT_NE(vector_func_iter, ts->node_infos.end());
392+ EXPECT_TRUE(vector_func_iter->vector_func_params.is_double_loop);
393+ ASSERT_EQ(vector_func_iter->vector_func_params.all_strides.size(), 2U);
394+ EXPECT_EQ(Str(vector_func_iter->vector_func_params.all_strides[0]), "input_stride");
395+ ASSERT_EQ(vector_func_iter->vector_func_params.output_dims.size(), 2U);
396+ EXPECT_EQ(Str(vector_func_iter->vector_func_params.output_dims[1]), "inner_dim");
397+}
398+ 
399+TEST_F(TestPipePerfExpr, ParserKeepsDefaultVectorFuncNodeParamsWhenAscirNodeParamsMissing) {
400+ af::AscGraph graph("vf_graph");
401+ ASSERT_EQ(af::ascir::cg::BuildVectorFuncTestGraph(graph), af::SUCCESS);
402+ 
403+ TuningSpacePtr ts = std::make_shared<TuningSpace>();
404+ ASSERT_NE(ts, nullptr);
405+ att::AscendGraphParser ascend_graph_parser(ts);
406+ ASSERT_EQ(ascend_graph_parser.GraphParser(graph), af::SUCCESS);
407+ 
408+ const auto vector_func_iter = std::find_if(ts->node_infos.begin(), ts->node_infos.end(),
409+ [](const NodeInfo &node) { return node.node_type == kVectorFunc; });
410+ ASSERT_NE(vector_func_iter, ts->node_infos.end());
411+ EXPECT_FALSE(vector_func_iter->vector_func_params.is_double_loop);
412+ EXPECT_TRUE(vector_func_iter->vector_func_params.all_strides.empty());
413+ EXPECT_TRUE(vector_func_iter->vector_func_params.output_dims.empty());
414+}
415+ 
416+TEST_F(TestPipePerfExpr, VectorFuncTailPerfUsesVectorFuncInternalPath) {
417+ af::AscGraph graph("vf_graph");
418+ ASSERT_EQ(af::ascir::cg::BuildVectorFuncTestGraph(graph), af::SUCCESS);
419+ auto vf_node = graph.FindNode("vector_func");
420+ ASSERT_NE(vf_node, nullptr);
421+ auto params = std::make_shared<ascir_param::AscirNodeParams>();
422+ ascir_param::VectorFuncNodeParams vector_func_params;
423+ vector_func_params.is_double_loop = true;
424+ vector_func_params.all_strides = {CreateExpr(1)};
425+ vector_func_params.output_dims = {CreateExpr(1), CreateExpr(64)};
426+ params->api_name = kVectorFunc;
427+ params->status = ascir_param::ParamBuildStatus::kBuilt;
428+ params->specific_params = vector_func_params;
429+ SetAscirNodeParamsForTest(vf_node, params);
430+ 
431+ TuningSpacePtr ts = std::make_shared<TuningSpace>();
432+ ASSERT_NE(ts, nullptr);
433+ att::AscendGraphParser ascend_graph_parser(ts);
434+ ASSERT_EQ(ascend_graph_parser.GraphParser(graph), af::SUCCESS);
435+ const auto vector_func_iter = std::find_if(ts->node_infos.begin(), ts->node_infos.end(),
436+ [](const NodeInfo &node) { return node.node_type == kVectorFunc; });
437+ ASSERT_NE(vector_func_iter, ts->node_infos.end());
438+ 
439+ PipePerfExpr pipe_perf(ts);
440+ std::map<PipeType, Expr> node_perf;
441+ std::map<Expr, TernaryOp, ExprCmp> ternary_ops;
442+ std::vector<PerfBreakdownGroup> perf_breakdowns;
443+ 
444+ ASSERT_EQ(pipe_perf.GetNodePerfInternal(*vector_func_iter, node_perf, ternary_ops, perf_breakdowns, true),
445+ af::SUCCESS);
446+ 
447+ const auto iter = node_perf.find(PipeType::AIV_VEC);
448+ ASSERT_NE(iter, node_perf.end());
449+ const auto ternary_iter = ternary_ops.find(iter->second);
450+ ASSERT_NE(ternary_iter, ternary_ops.end());
451+ const std::string desc = ternary_iter->second.GetDescription();
452+ EXPECT_NE(desc.find("VectorFunc"), std::string::npos) << desc;
453+ EXPECT_NE(desc.find("AIV_VEC_perf"), std::string::npos) << desc;
454+}
455+ 
296TEST_F(TestPipePerfExpr, case_get_perf_for_loop) {456TEST_F(TestPipePerfExpr, case_get_perf_for_loop) {
297 af::AscGraph graph("graph");457 af::AscGraph graph("graph");
298 ASSERT_EQ(af::ascir::cg::BuildEqAscendGraphND(graph), af::SUCCESS);458 ASSERT_EQ(af::ascir::cg::BuildEqAscendGraphND(graph), af::SUCCESS);
@@ -18,7 +18,7 @@
18#include "ascir_ops.h"18#include "ascir_ops.h"
19#include "ascir_node_param/ascir_node_param.h"19#include "ascir_node_param/ascir_node_param.h"
20#include "ascir_node_param/ascir_param_builder.h"20#include "ascir_node_param/ascir_param_builder.h"
21-#include "gen_model_info/parser/reduce_specific_params_builder.h"21+#include "gen_model_info/parser/specific_params_builder.h"
22#include "gen_model_info/parser/tuning_space.h"22#include "gen_model_info/parser/tuning_space.h"
23 23 
24namespace ascir_reduce_test_helpers {24namespace ascir_reduce_test_helpers {
@@ -96,7 +96,7 @@ inline att::NodeInfo BuildReduceNodeInfo(ReduceTestEnv &env, const std::string &
96 BuildParserTensor("y", {&env.axis0, &env.axis1}, {ge::Symbol(8), ge::Symbol(1)}, {ge::Symbol(1), ge::Symbol(0)})};96 BuildParserTensor("y", {&env.axis0, &env.axis1}, {ge::Symbol(8), ge::Symbol(1)}, {ge::Symbol(1), ge::Symbol(0)})};
97 node_info.loop_axes = {&env.axis1};97 node_info.loop_axes = {&env.axis1};
98 (void)ascir_param::EnrichAscirGraphNodeParams(env.graph);98 (void)ascir_param::EnrichAscirGraphNodeParams(env.graph);
99- (void)att::FillReduceSpecificParams(env.node, node_info);99+ (void)att::FillSpecificParams(env.node, node_info);
100 return node_info;100 return node_info;
101}101}
102 102 
@@ -64,6 +64,43 @@ TEST(AscirNodeParamsTest, EnrichGraphRegistersSkippedForUnsupportedApi) {
64 EXPECT_TRUE(std::holds_alternative<std::monostate>(params->specific_params));64 EXPECT_TRUE(std::holds_alternative<std::monostate>(params->specific_params));
65}65}
66 66 
67+TEST(AscirNodeParamsTest, VectorFuncParamsCanBeStoredInAscirNodeParams) {
68+ ascir_param::AscirNodeParams params;
69+ ascir_param::VectorFuncNodeParams vf_params;
70+ vf_params.is_double_loop = true;
71+ vf_params.all_strides = {Expr(4), Expr(8)};
72+ vf_params.output_dims = {Expr(2), Expr(16)};
73+ 
74+ params.specific_params = vf_params;
75+ 
76+ const auto *stored = ascir_param::GetSpecificParams<ascir_param::VectorFuncNodeParams>(params);
77+ ASSERT_NE(stored, nullptr);
78+ EXPECT_TRUE(stored->is_double_loop);
79+ ASSERT_EQ(stored->all_strides.size(), 2U);
80+ EXPECT_EQ(stored->all_strides[0], Expr(4));
81+ ASSERT_EQ(stored->output_dims.size(), 2U);
82+ EXPECT_EQ(stored->output_dims[1], Expr(16));
83+}
84+ 
85+TEST(AscirNodeParamsTest, EnrichGraphRegistersVectorFuncParams) {
86+ af::AscGraph graph("test_graph");
87+ af::ascir_op::VectorFunc vf_op("vf");
88+ graph.AddNode(vf_op);
89+ auto node = graph.FindNode("vf");
90+ ASSERT_NE(node, nullptr);
91+ 
92+ ExpectEnrichSuccess(graph);
93+ auto params = ascir_param::GetAscirNodeParams(node);
94+ ASSERT_NE(params, nullptr);
95+ EXPECT_EQ(params->api_name, "VectorFunc");
96+ EXPECT_EQ(params->status, ascir_param::ParamBuildStatus::kBuilt);
97+ const auto *stored = ascir_param::GetSpecificParams<ascir_param::VectorFuncNodeParams>(*params);
98+ ASSERT_NE(stored, nullptr);
99+ EXPECT_FALSE(stored->is_double_loop);
100+ EXPECT_TRUE(stored->all_strides.empty());
101+ EXPECT_TRUE(stored->output_dims.empty());
102+}
103+ 
67TEST(AscirNodeParamsTest, EnrichReduceParamsForArSingleReduce) {104TEST(AscirNodeParamsTest, EnrichReduceParamsForArSingleReduce) {
68 auto env = MakeArReduceEnv("max");105 auto env = MakeArReduceEnv("max");
69 106 
@@ -22,7 +22,7 @@
22#include "common/platform_context.h"22#include "common/platform_context.h"
23#include "api_perf_register/utils/api_perf_utils.h"23#include "api_perf_register/utils/api_perf_utils.h"
24#include "graph_construct_utils.h"24#include "graph_construct_utils.h"
25-#include "parser/reduce_specific_params_builder.h"25+#include "parser/specific_params_builder.h"
26#include "tests/ut/common/ascir_reduce_test_helpers.h"26#include "tests/ut/common/ascir_reduce_test_helpers.h"
27 27 
28using namespace att;28using namespace att;
@@ -62,7 +62,7 @@ void SetSingleReduceSpecificParams(NodeInfo &node) {
62 params.exprs.merge_times = {true, {{CreateExpr(1), ascir_param::ParamExprRole::kSemantic}}};62 params.exprs.merge_times = {true, {{CreateExpr(1), ascir_param::ParamExprRole::kSemantic}}};
63}63}
64 64 
65-TEST_F(UTestAscirPerfV2, FillReduceSpecificParamsStoresSharedParamsOnAscNode) {65+TEST_F(UTestAscirPerfV2, FillSpecificParamsStoresSharedReduceParamsOnAscNode) {
66 using ascir_reduce_test_helpers::BuildReduceNodeInfo;66 using ascir_reduce_test_helpers::BuildReduceNodeInfo;
67 using ascir_reduce_test_helpers::ReduceTestEnv;67 using ascir_reduce_test_helpers::ReduceTestEnv;
68 ReduceTestEnv env("reduce");68 ReduceTestEnv env("reduce");
@@ -19,7 +19,7 @@
19#include "ascir_node_param/ascir_node_param.h"19#include "ascir_node_param/ascir_node_param.h"
20#include "ascir_node_param/ascir_param_builder.h"20#include "ascir_node_param/ascir_param_builder.h"
21#include "ascir_ops.h"21#include "ascir_ops.h"
22-#include "parser/reduce_specific_params_builder.h"22+#include "parser/specific_params_builder.h"
23#include "common/platform_context.h"23#include "common/platform_context.h"
24#include "codegen_api_param/codegen_api_param.h"24#include "codegen_api_param/codegen_api_param.h"
25#include "v35/att/api_perf_register/ascendc_api_perf/reduce_api_perf_v2.h"25#include "v35/att/api_perf_register/ascendc_api_perf/reduce_api_perf_v2.h"
@@ -167,7 +167,7 @@ void SetReduceSpecificParams(NodeInfo &node, codegen::ReducePattern pattern, cod
167 167 
168// --- Parser integration tests ---168// --- Parser integration tests ---
169 169 
170-TEST_F(UTestReduceMinMaxApiPerfV2, FillReduceSpecificParamsRegistersSharedNodeParams) {170+TEST_F(UTestReduceMinMaxApiPerfV2, FillSpecificParamsRegistersSharedReduceNodeParams) {
171 using ascir_reduce_test_helpers::BuildReduceNodeInfo;171 using ascir_reduce_test_helpers::BuildReduceNodeInfo;
172 using ascir_reduce_test_helpers::ReduceTestEnv;172 using ascir_reduce_test_helpers::ReduceTestEnv;
173 ReduceTestEnv env("max");173 ReduceTestEnv env("max");
@@ -183,7 +183,7 @@ TEST_F(UTestReduceMinMaxApiPerfV2, FillReduceSpecificParamsRegistersSharedNodePa
183 EXPECT_EQ(node_info.reduce_specific_params.canonical_params.merge_size, reduce->canonical_params.merge_size);183 EXPECT_EQ(node_info.reduce_specific_params.canonical_params.merge_size, reduce->canonical_params.merge_size);
184}184}
185 185 
186-TEST_F(UTestReduceMinMaxApiPerfV2, FillReduceSpecificParamsRegistersSkippedForUnsupportedApi) {186+TEST_F(UTestReduceMinMaxApiPerfV2, FillSpecificParamsRegistersSkippedForUnsupportedApi) {
187 af::AscGraph graph("skip_param_graph");187 af::AscGraph graph("skip_param_graph");
188 af::ascir_op::Add add("add");188 af::ascir_op::Add add("add");
189 graph.AddNode(add);189 graph.AddNode(add);
@@ -195,14 +195,14 @@ TEST_F(UTestReduceMinMaxApiPerfV2, FillReduceSpecificParamsRegistersSkippedForUn
195 node_info.node_type = "Add";195 node_info.node_type = "Add";
196 node_info.node_ptr = node;196 node_info.node_ptr = node;
197 EXPECT_EQ(ascir_param::EnrichAscirGraphNodeParams(graph), af::SUCCESS);197 EXPECT_EQ(ascir_param::EnrichAscirGraphNodeParams(graph), af::SUCCESS);
198- EXPECT_EQ(FillReduceSpecificParams(node, node_info), af::SUCCESS);198+ EXPECT_EQ(FillSpecificParams(node, node_info), af::SUCCESS);
199 const auto params = ascir_param::GetAscirNodeParams(node);199 const auto params = ascir_param::GetAscirNodeParams(node);
200 ASSERT_NE(params, nullptr);200 ASSERT_NE(params, nullptr);
201 EXPECT_EQ(params->status, ascir_param::ParamBuildStatus::kSkipped);201 EXPECT_EQ(params->status, ascir_param::ParamBuildStatus::kSkipped);
202 EXPECT_FALSE(node_info.reduce_specific_params.canonical_params.valid);202 EXPECT_FALSE(node_info.reduce_specific_params.canonical_params.valid);
203}203}
204 204 
205-TEST_F(UTestReduceMinMaxApiPerfV2, FillReduceSpecificParamsKeepsInvalidWhenParamsMissing) {205+TEST_F(UTestReduceMinMaxApiPerfV2, FillSpecificParamsKeepsInvalidWhenParamsMissing) {
206 using ascir_reduce_test_helpers::ReduceTestEnv;206 using ascir_reduce_test_helpers::ReduceTestEnv;
207 ReduceTestEnv env("max");207 ReduceTestEnv env("max");
208 NodeInfo node_info;208 NodeInfo node_info;
@@ -210,7 +210,7 @@ TEST_F(UTestReduceMinMaxApiPerfV2, FillReduceSpecificParamsKeepsInvalidWhenParam
210 node_info.node_type = "Max";210 node_info.node_type = "Max";
211 node_info.node_ptr = env.node;211 node_info.node_ptr = env.node;
212 212 
213- EXPECT_EQ(FillReduceSpecificParams(env.node, node_info), af::SUCCESS);213+ EXPECT_EQ(FillSpecificParams(env.node, node_info), af::SUCCESS);
214 EXPECT_EQ(ascir_param::GetAscirNodeParams(env.node), nullptr);214 EXPECT_EQ(ascir_param::GetAscirNodeParams(env.node), nullptr);
215 EXPECT_FALSE(node_info.reduce_specific_params.canonical_params.valid);215 EXPECT_FALSE(node_info.reduce_specific_params.canonical_params.valid);
216}216}
@@ -11,6 +11,8 @@
11#include "gtest/gtest.h"11#include "gtest/gtest.h"
12#include "node_utils_ex.h"12#include "node_utils_ex.h"
13#include "ascir_utils.h"13#include "ascir_utils.h"
14+#include "ascir_node_param/ascir_node_param.h"
15+#include "ascir_node_param/ascir_param_builder.h"
14#include "graph_utils.h"16#include "graph_utils.h"
15#include "ascendc_ir.h"17#include "ascendc_ir.h"
16#include "ascir_ops.h"18#include "ascir_ops.h"
@@ -137,6 +139,129 @@ std::string GenerateScalarDataVfCall(AscGraph &graph, const af::Axis &z0, const
137 EXPECT_EQ(call.Generate(tpipe, vector<af::AxisId>{}, result), 0);139 EXPECT_EQ(call.Generate(tpipe, vector<af::AxisId>{}, result), 0);
138 return result;140 return result;
139}141}
142+ 
143+template <typename TensorLike>
144+void SetFiveDimSchedule(TensorLike &tensor, const std::vector<af::Axis> &axes,
145+ const std::vector<af::Expression> &repeats, const std::vector<af::Expression> &strides) {
146+ *tensor.axis = {axes[0].id, axes[1].id, axes[2].id, axes[3].id, axes[4].id};
147+ *tensor.repeats = repeats;
148+ *tensor.strides = strides;
149+}
150+ 
151+void SetFiveDimVecInAttr(AscTensor &tensor, const std::vector<af::Axis> &axes,
152+ const std::vector<af::Expression> &strides, int64_t tensor_id) {
153+ tensor.attr.vectorized_axis = {axes[0].id, axes[1].id, axes[2].id, axes[3].id, axes[4].id};
154+ tensor.attr.vectorized_strides = strides;
155+ tensor.attr.dtype = af::DT_FLOAT;
156+ tensor.attr.mem.position = af::Position::kPositionVecIn;
157+ tensor.attr.mem.tensor_id = tensor_id;
158+}
159+ 
160+void InitDoubleLoopVfGraph(VectorFunc &vf_op, Store &store_op, Load &sub_load_op, Abs &abs_op, Store &sub_store_op,
161+ Output &sub_output_op, const Load &load_op, const Data &sub_x_op,
162+ const std::vector<af::Axis> &axes, const std::vector<af::Expression> &repeats,
163+ const std::vector<af::Expression> &strides) {
164+ vf_op.InstanceOutputy(1);
165+ vf_op.x = {load_op.y};
166+ vf_op.attr.sched.axis = {axes[0].id, axes[1].id, axes[2].id, axes[3].id, axes[4].id};
167+ SetFiveDimSchedule(vf_op.y[0], axes, repeats, strides);
168+ 
169+ store_op.x = vf_op.y[0];
170+ store_op.ir_attr.SetOffset(af::Symbol(0));
171+ SetFiveDimSchedule(store_op.y, axes, repeats, strides);
172+ 
173+ sub_load_op.x = sub_x_op.y;
174+ sub_load_op.attr.sched.axis = {axes[3].id, axes[4].id};
175+ sub_load_op.attr.sched.loop_axis = axes[4].id;
176+ SetFiveDimSchedule(sub_load_op.y, axes, repeats, strides);
177+ 
178+ abs_op.x = sub_load_op.y;
179+ abs_op.attr.sched.axis = {axes[3].id, axes[4].id};
180+ abs_op.attr.sched.loop_axis = axes[4].id;
181+ SetFiveDimSchedule(abs_op.y, axes, repeats, strides);
182+ 
183+ sub_store_op.x = abs_op.y;
184+ sub_store_op.attr.sched.axis = {axes[3].id, axes[4].id};
185+ sub_store_op.attr.sched.loop_axis = axes[4].id;
186+ SetFiveDimSchedule(sub_store_op.y, axes, repeats, strides);
187+ sub_output_op.x = sub_store_op.y;
188+}
189+ 
190+void InitDoubleLoopTensorAttrs(AscGraph &graph, AscGraph &vf_sub_graph, const std::vector<af::Axis> &axes,
191+ const std::vector<af::Expression> &base_strides,
192+ const std::vector<af::Expression> &output_strides) {
193+ auto load = graph.FindNode("load");
194+ SetFiveDimVecInAttr(load->outputs[0], axes, base_strides, 0);
195+ load->outputs[0].attr.mem.alloc_type = af::AllocType::kAllocTypeQueue;
196+ load->outputs[0].attr.que.id = 1;
197+ load->outputs[0].attr.opt.merge_scope = af::kIdNone;
198+ 
199+ SetFiveDimVecInAttr(vf_sub_graph.FindNode("sub_load")->outputs[0], axes, base_strides, 0);
200+ SetFiveDimVecInAttr(vf_sub_graph.FindNode("abs")->outputs[0], axes, base_strides, 1);
201+ SetFiveDimVecInAttr(vf_sub_graph.FindNode("sub_store")->outputs[0], axes, output_strides, 2);
202+ 
203+ auto vf = graph.FindNode("vf");
204+ SetFiveDimVecInAttr(vf->outputs[0], axes, output_strides, 1);
205+ vf->outputs[0].attr.mem.alloc_type = af::AllocType::kAllocTypeQueue;
206+ vf->outputs[0].attr.que.id = 1;
207+ vf->outputs[0].attr.opt.merge_scope = af::kIdNone;
208+ 
209+ auto store = graph.FindNode("store");
210+ store->outputs[0].attr.vectorized_axis = {axes[0].id, axes[1].id, axes[2].id, axes[3].id, axes[4].id};
211+ store->outputs[0].attr.vectorized_strides = output_strides;
212+ store->outputs[0].attr.dtype = af::DT_FLOAT;
213+ store->outputs[0].attr.mem.position = af::Position::kPositionVecOut;
214+ store->outputs[0].attr.mem.tensor_id = 2;
215+ store->outputs[0].attr.mem.alloc_type = af::AllocType::kAllocTypeQueue;
216+ store->outputs[0].attr.que.id = 2;
217+ store->outputs[0].attr.opt.merge_scope = af::kIdNone;
218+}
219+ 
220+void PrepareDoubleLoopCodegen(AscGraph &graph, const std::vector<af::Axis> &axes,
221+ const std::vector<af::Expression> &repeats, codegen::Tiler &tiler, codegen::TPipe &tpipe,
222+ codegen::VfCall &call, std::stringstream &func_def) {
223+ auto load = graph.FindNode("load");
224+ auto vf = graph.FindNode("vf");
225+ tpipe.AddTensor(load->outputs[0]);
226+ tpipe.AddTensor(vf->outputs[0]);
227+ for (const auto &axis : axes) {
228+ tiler.AddAxis(axis);
229+ }
230+ for (const auto &repeat : repeats) {
231+ tiler.AddSizeVar(af::SizeVar(repeat));
232+ }
233+ 
234+ codegen::ApiTensor x1;
235+ x1.id = load->outputs[0].attr.mem.tensor_id;
236+ ASSERT_EQ(call.Init(vf), 0);
237+ call.inputs.push_back(&x1);
238+ ASSERT_EQ(ascir_param::EnrichAscirGraphNodeParams(graph), af::SUCCESS);
239+ ASSERT_EQ(call.GenerateFuncDefinition(tpipe, tiler, func_def), 0);
240+}
241+ 
242+void RegisterDoubleLoopNodes(AscGraph &graph, AscGraph &vf_sub_graph, VectorFunc &vf_op, Data &sub_x_op,
243+ Output &sub_output_op, Load &load_op, Store &store_op, const std::string &sub_graph_name) {
244+ vf_op.SetAttr("sub_graph_name", sub_graph_name);
245+ sub_x_op.ir_attr.SetIndex(0);
246+ sub_output_op.ir_attr.SetIndex(0);
247+ graph.AddNode(load_op);
248+ graph.AddSubGraph(vf_sub_graph);
249+ graph.AddNode(store_op);
250+}
251+ 
252+void CheckDoubleLoopVectorFuncParams(const af::AscNodePtr &vf) {
253+ auto params = ascir_param::GetAscirNodeParams(vf);
254+ ASSERT_NE(params, nullptr);
255+ const auto *vector_func_params = std::get_if<ascir_param::VectorFuncNodeParams>(&params->specific_params);
256+ ASSERT_NE(vector_func_params, nullptr);
257+ EXPECT_TRUE(vector_func_params->is_double_loop);
258+ ASSERT_EQ(vector_func_params->all_strides.size(), 6U);
259+ EXPECT_STREQ(vector_func_params->all_strides[0].Serialize().get(), "(s2 * s3 * s4)");
260+ EXPECT_STREQ(vector_func_params->all_strides[5].Serialize().get(), "(2 * s4)");
261+ ASSERT_EQ(vector_func_params->output_dims.size(), 4U);
262+ EXPECT_STREQ(vector_func_params->output_dims[0].Serialize().get(), "s1");
263+ EXPECT_STREQ(vector_func_params->output_dims[3].Serialize().get(), "s4");
264+}
140} // namespace265} // namespace
141 266 
142TEST(CodegenKernel, VfCall_TwoDimLoad) {267TEST(CodegenKernel, VfCall_TwoDimLoad) {
@@ -1040,6 +1165,61 @@ TEST(CodegenKernel, VfCall_FiveDimLoad) {
1040 "#endif\n"});1165 "#endif\n"});
1041}1166}
1042 1167 
1168+TEST(CodegenKernel, VfCall_DoubleLoopWritesVectorFuncParams) {
1169+ ge::SetupRuntimeStub();
1170+ af::AscGraph graph("test_graph");
1171+ 
1172+ af::Expression Two = af::Symbol(2);
1173+ af::Expression Three = af::Symbol(3);
1174+ af::Expression Four = af::Symbol(4);
1175+ af::Expression Five = af::Symbol(5);
1176+ 
1177+ auto s0 = graph.CreateSizeVar("s0");
1178+ auto s1 = graph.CreateSizeVar("s1");
1179+ auto s2 = graph.CreateSizeVar("s2");
1180+ auto s3 = graph.CreateSizeVar("s3");
1181+ auto s4 = graph.CreateSizeVar("s4");
1182+ auto z0 = graph.CreateAxis("z0", s0);
1183+ auto z1 = graph.CreateAxis("z1", s1);
1184+ auto z2 = graph.CreateAxis("z2", s2);
1185+ auto z3 = graph.CreateAxis("z3", s3);
1186+ auto z4 = graph.CreateAxis("z4", s4);
1187+ const std::vector<af::Axis> axes = {z0, z1, z2, z3, z4};
1188+ const std::vector<af::Expression> repeats = {s0, s1, s2, s3, s4};
1189+ const std::vector<af::Expression> base_strides = {s1 * s2 * s3 * s4, s2 * s3 * s4, s3 * s4, s4, One};
1190+ const std::vector<af::Expression> output_strides = {s1 * s2 * s3 * s4 * Five, s2 * s3 * s4 * Four, s3 * s4 * Three,
1191+ s4 * Two, One};
1192+ 
1193+ Data x_op("x", graph);
1194+ Load load_op("load");
1195+ std::string sub_graph_name = "vf_sub_graph_double_loop";
1196+ af::AscGraph vf_sub_graph(sub_graph_name.c_str());
1197+ VectorFunc vf_op("vf");
1198+ Data sub_x_op("sub_x", vf_sub_graph);
1199+ Load sub_load_op("sub_load");
1200+ Abs abs_op("abs");
1201+ Store sub_store_op("sub_store");
1202+ Output sub_output_op("sub_output");
1203+ Store store_op("store");
1204+ RegisterDoubleLoopNodes(graph, vf_sub_graph, vf_op, sub_x_op, sub_output_op, load_op, store_op, sub_graph_name);
1205+ 
1206+ load_op.x = x_op.y;
1207+ load_op.attr.sched.axis = {z0.id, z1.id, z2.id, z3.id, z4.id};
1208+ SetFiveDimSchedule(load_op.y, axes, repeats, base_strides);
1209+ InitDoubleLoopVfGraph(vf_op, store_op, sub_load_op, abs_op, sub_store_op, sub_output_op, load_op, sub_x_op, axes,
1210+ repeats, base_strides);
1211+ InitDoubleLoopTensorAttrs(graph, vf_sub_graph, axes, base_strides, output_strides);
1212+ 
1213+ codegen::Tiler tiler;
1214+ codegen::TPipe tpipe("tpipe", tiler);
1215+ codegen::VfCall call;
1216+ std::stringstream func_def;
1217+ PrepareDoubleLoopCodegen(graph, axes, repeats, tiler, tpipe, call, func_def);
1218+ 
1219+ auto vf = graph.FindNode("vf");
1220+ CheckDoubleLoopVectorFuncParams(vf);
1221+}
1222+ 
1043// 测试单维场景不触发优化逻辑1223// 测试单维场景不触发优化逻辑
1044TEST(CodegenKernel, VfCall_OneDim_NoOptimization) {1224TEST(CodegenKernel, VfCall_OneDim_NoOptimization) {
1045 ge::SetupRuntimeStub();1225 ge::SetupRuntimeStub();
@@ -10,7 +10,7 @@
10#include "vec_func_call.h"10#include "vec_func_call.h"
11 11 
12#include <sstream>12#include <sstream>
13-#include "attr_utils.h"13+#include "ascir_node_param/ascir_node_param.h"
14#include "ascir_ops.h"14#include "ascir_ops.h"
15#include "common/ge_common/debug/log.h"15#include "common/ge_common/debug/log.h"
16#include "graph/ascendc_ir/utils/asc_tensor_utils.h"16#include "graph/ascendc_ir/utils/asc_tensor_utils.h"
@@ -31,6 +31,16 @@ using namespace af::ascir_op;
31using namespace ascgen_utils;31using namespace ascgen_utils;
32 32 
33namespace {33namespace {
34+std::vector<ge::Expression> GetVfOutputDims(const VectorizedAxisLoopMergeStatus &merge_info) {
35+ std::vector<ge::Expression> output_dims;
36+ const size_t dim_size = merge_info.merge_repeats.size();
37+ size_t start_idx = dim_size <= kVFMaxLoop ? 0 : dim_size - kVFMaxLoop;
38+ for (; start_idx < dim_size; ++start_idx) {
39+ output_dims.emplace_back(merge_info.merge_repeats[start_idx]);
40+ }
41+ return output_dims;
42+}
43+ 
34void ParamPostProcess(std::stringstream &ss) {44void ParamPostProcess(std::stringstream &ss) {
35 if (ss.str().length() < kCommaSpaceLength) {45 if (ss.str().length() < kCommaSpaceLength) {
36 return;46 return;
@@ -329,36 +339,30 @@ Status VfCall::ParseInputOutputInfo(const TPipe &tpipe) const {
329 return af::SUCCESS;339 return af::SUCCESS;
330}340}
331 341 
332-void GenerateStridesEqualCheck(const std::vector<Tensor> &inputs, const std::vector<Tensor> &outputs,342+void CollectStrideNames(const std::vector<Tensor> &tensors, const std::vector<std::vector<ge::Expression>> &strides,
333- const VectorizedAxisLoopMergeStatus &merge_info, std::stringstream &ss) {343+ std::vector<std::string> &all_stride_names, std::vector<ge::Expression> &all_strides) {
334- std::vector<std::string> all_stride_names;344+ for (size_t j = 0; j < strides.size(); j++) {
335- for (size_t j = 0; j < merge_info.inputs_strides.size(); j++) {345+ size_t stride_size = strides[j].size();
336- size_t stride_size = merge_info.inputs_strides[j].size();
337 size_t start_idx = stride_size <= kVFMaxLoop ? 0 : stride_size - kVFMaxLoop;346 size_t start_idx = stride_size <= kVFMaxLoop ? 0 : stride_size - kVFMaxLoop;
338 for (; start_idx < stride_size; start_idx++) {347 for (; start_idx < stride_size; start_idx++) {
339- if (merge_info.inputs_strides[j][start_idx].Simplify() == af::ops::One ||348+ if (strides[j][start_idx].Simplify() == af::ops::One || strides[j][start_idx].Simplify() == af::ops::Zero) {
340- merge_info.inputs_strides[j][start_idx].Simplify() == af::ops::Zero) {
341 continue;349 continue;
342 }350 }
343 std::stringstream tensor_name;351 std::stringstream tensor_name;
344- tensor_name << inputs[j];352+ tensor_name << tensors[j];
345- all_stride_names.push_back(tensor_name.str() + "_stride_" + std::to_string(start_idx));353+ const std::string stride_name = tensor_name.str() + "_stride_" + std::to_string(start_idx);
354+ all_stride_names.push_back(stride_name);
355+ all_strides.emplace_back(strides[j][start_idx]);
346 }356 }
347 }357 }
358+}
348 359 
349- for (size_t j = 0; j < merge_info.outputs_strides.size(); j++) {360+void GenerateStridesEqualCheck(const std::vector<Tensor> &inputs, const std::vector<Tensor> &outputs,
350- size_t stride_size = merge_info.outputs_strides[j].size();361+ const VectorizedAxisLoopMergeStatus &merge_info,
351- size_t start_idx = stride_size <= kVFMaxLoop ? 0 : stride_size - kVFMaxLoop;362+ std::vector<ge::Expression> &all_strides, std::stringstream &ss) {
352- for (; start_idx < stride_size; start_idx++) {363+ std::vector<std::string> all_stride_names;
353- if (merge_info.outputs_strides[j][start_idx].Simplify() == af::ops::One ||364+ CollectStrideNames(inputs, merge_info.inputs_strides, all_stride_names, all_strides);
354- merge_info.outputs_strides[j][start_idx].Simplify() == af::ops::Zero) {365+ CollectStrideNames(outputs, merge_info.outputs_strides, all_stride_names, all_strides);
355- continue;
356- }
357- std::stringstream tensor_name;
358- tensor_name << outputs[j];
359- all_stride_names.push_back(tensor_name.str() + "_stride_" + std::to_string(start_idx));
360- }
361- }
362 366 
363 if (all_stride_names.empty()) {367 if (all_stride_names.empty()) {
364 ss << " uint32_t strides_align = 0;\n bool strides_equal = false;\n";368 ss << " uint32_t strides_align = 0;\n bool strides_equal = false;\n";
@@ -430,6 +434,21 @@ void GenerateTensorDefs(const TPipe &tpipe, const TensorManager &tensor_mgr, con
430 }434 }
431}435}
432 436 
437+af::Status UpdateVectorFuncNodeParams(const af::AscNodePtr &node, const VectorizedAxisLoopMergeStatus &merge_info,
438+ const std::vector<ge::Expression> &all_strides) {
439+ GE_ASSERT_NOTNULL(node);
440+ auto params = ascir_param::GetAscirNodeParams(node);
441+ GE_ASSERT_NOTNULL(params);
442+ ascir_param::VectorFuncNodeParams vector_func_params;
443+ vector_func_params.is_double_loop = true;
444+ vector_func_params.all_strides = all_strides;
445+ vector_func_params.output_dims = GetVfOutputDims(merge_info);
446+ params->api_name = node->GetType();
447+ params->status = ascir_param::ParamBuildStatus::kBuilt;
448+ params->specific_params = vector_func_params;
449+ return af::SUCCESS;
450+}
451+ 
433Status VfCall::GenerateFuncDefinition(const TPipe &tpipe, const Tiler &tiler, std::stringstream &ss) const {452Status VfCall::GenerateFuncDefinition(const TPipe &tpipe, const Tiler &tiler, std::stringstream &ss) const {
434 // 收集输入输出信息,由于GenInnerLoopSizeAndActualSize函数中会刷新tiler对象中的actual_sizes字段,453 // 收集输入输出信息,由于GenInnerLoopSizeAndActualSize函数中会刷新tiler对象中的actual_sizes字段,
435 // 导致生成函数签名和函数调用时,获取到的size信息不一致,因此生成函数签名和函数调用时均需要调用合轴函数454 // 导致生成函数签名和函数调用时,获取到的size信息不一致,因此生成函数签名和函数调用时均需要调用合轴函数
@@ -485,13 +504,14 @@ Status VfCall::GenerateFuncDefinition(const TPipe &tpipe, const Tiler &tiler, st
485 int32_t only_loop_max_depth = -1;504 int32_t only_loop_max_depth = -1;
486 std::vector<std::string> loop_size_vec;505 std::vector<std::string> loop_size_vec;
487 root_loop_.Generate(tpipe, tensor_mgr_, stride_depth, loop_body, loop_size, only_loop_max_depth, loop_size_vec);506 root_loop_.Generate(tpipe, tensor_mgr_, stride_depth, loop_body, loop_size, only_loop_max_depth, loop_size_vec);
507+ const bool is_double_loop =
508+ stride_depth == MAX_VF_AXIS_MERGE_SIZE - 1 && only_loop_max_depth == MAX_VF_AXIS_MERGE_SIZE - 1;
509+ std::vector<ge::Expression> all_strides;
488 params << std::endl << loop_size << std::endl;510 params << std::endl << loop_size << std::endl;
489- if (stride_depth == MAX_VF_AXIS_MERGE_SIZE - 1 &&511+ if (is_double_loop) { // 假如stride_depth为1即两层循环,那实际上loop里递归了三次,分别是0、1、2,在2里单独处理call
490- only_loop_max_depth ==512+ GenerateStridesEqualCheck(this->ub_inputs_, this->ub_outputs_, merge_info, all_strides, params);
491- MAX_VF_AXIS_MERGE_SIZE -
492- 1) { // 假如stride_depth为1即两层循环,那实际上loop里递归了三次,分别是0、1、2,在2里单独处理call
493- GenerateStridesEqualCheck(this->ub_inputs_, this->ub_outputs_, merge_info, params);
494 OptimizeMergeParamsAndLoopSize(loop_size_vec, params);513 OptimizeMergeParamsAndLoopSize(loop_size_vec, params);
514+ GE_ASSERT_SUCCESS(UpdateVectorFuncNodeParams(node, merge_info, all_strides));
495 }515 }
496 vf_body << std::endl << loop_body << std::endl;516 vf_body << std::endl << loop_body << std::endl;
497 GetVFCallFuncBody(params.str(), vf_body.str(), ss);517 GetVFCallFuncBody(params.str(), vf_body.str(), ss);