已合并
feat(autofuse): support effective-view NDDMA models #1792
feat(autofuse): support effective-view NDDMA models #1792
已合并
zhang_shengjie创建于 17 天前
12 个文件变更+339-174
@@ -19,8 +19,9 @@
19#include "perf_param.h"19#include "perf_param.h"
20 20 
21namespace att {21namespace att {
22-// legacy 连续轴合并前原始 DataCopyNddma 描述所有向量按 vectorized_axis 排列,dim 和 stride 的单位22+// Codegen DataCopyNddma effective view。向量按 vectorized_axis 排列,并应与
23-// 分别为元素个数和元素;input_strides 对应 GM stride,output_strides 对应 UB vectorized stride23+// CalculateDmaParams 合轴、忽略双零 stride 轴及 tail actual size 后的参数一致;dim 和 stride 的单位
24+// 分别为元素个数和元素,input_strides 对应 GM stride,output_strides 对应 UB vectorized stride。
24struct NddmaDescriptorInfo {25struct NddmaDescriptorInfo {
25 std::vector<Expr> output_dims;26 std::vector<Expr> output_dims;
26 std::vector<Expr> input_strides;27 std::vector<Expr> input_strides;
@@ -120,7 +120,6 @@ bool CalculateDmaParams(const TPipe &tpipe, const Tensor &gm_tensor, const Tenso
120 }120 }
121 AxisInfo axis_info;121 AxisInfo axis_info;
122 MergeInfo merge_info;122 MergeInfo merge_info;
123- 
124 size_t vec_axis_pos = ub_tensor.vectorized_axis.size() - 1;123 size_t vec_axis_pos = ub_tensor.vectorized_axis.size() - 1;
125 bool has_non_zero_axis = false;124 bool has_non_zero_axis = false;
126 for (vec_axis_pos = ub_tensor.vectorized_axis.size(); vec_axis_pos-- > 0UL;) {125 for (vec_axis_pos = ub_tensor.vectorized_axis.size(); vec_axis_pos-- > 0UL;) {
@@ -128,11 +127,8 @@ bool CalculateDmaParams(const TPipe &tpipe, const Tensor &gm_tensor, const Tenso
128 GE_ASSERT_TRUE((pos != gm_tensor.axis.end()), "Codegen vectorized axis[%zu] not found", vec_axis_pos);127 GE_ASSERT_TRUE((pos != gm_tensor.axis.end()), "Codegen vectorized axis[%zu] not found", vec_axis_pos);
129 const auto axis_pos = std::distance(gm_tensor.axis.begin(), pos);128 const auto axis_pos = std::distance(gm_tensor.axis.begin(), pos);
130 // 如果当前轴gm和ub上对应的stride均为0,如果前序轴的stride不为1,则保留当前轴129 // 如果当前轴gm和ub上对应的stride均为0,如果前序轴的stride不为1,则保留当前轴
131- const bool ignore_zero_axis = has_non_zero_axis || vec_axis_pos == 0UL ||130+ const bool ignore_zero_axis =
132- af::SymbolicUtils::StaticCheckEq(ub_tensor.vectorized_strides[vec_axis_pos - 1],131+ ascgen_utils::ShouldIgnoreDataCopyZeroAxis(has_non_zero_axis, vec_axis_pos, ub_tensor.vectorized_strides);
133- af::ops::One) == af::TriBool::kTrue ||
134- af::SymbolicUtils::StaticCheckEq(ub_tensor.vectorized_strides[vec_axis_pos - 1],
135- af::ops::Zero) == af::TriBool::kTrue;
136 if (af::SymbolicUtils::StaticCheckEq(gm_tensor.axis_strides[axis_pos], af::ops::Zero) == af::TriBool::kTrue &&132 if (af::SymbolicUtils::StaticCheckEq(gm_tensor.axis_strides[axis_pos], af::ops::Zero) == af::TriBool::kTrue &&
137 af::SymbolicUtils::StaticCheckEq(ub_tensor.vectorized_strides[vec_axis_pos], af::ops::Zero) ==133 af::SymbolicUtils::StaticCheckEq(ub_tensor.vectorized_strides[vec_axis_pos], af::ops::Zero) ==
138 af::TriBool::kTrue &&134 af::TriBool::kTrue &&
@@ -142,9 +138,9 @@ bool CalculateDmaParams(const TPipe &tpipe, const Tensor &gm_tensor, const Tenso
142 has_non_zero_axis = true;138 has_non_zero_axis = true;
143 ascir::SizeExpr cur_axis_stride = axis_info.prev_axis_stride * axis_info.prev_repeat;139 ascir::SizeExpr cur_axis_stride = axis_info.prev_axis_stride * axis_info.prev_repeat;
144 ascir::SizeExpr cur_vectorized_axis_stride = axis_info.prev_vectorized_axis_stride * axis_info.prev_repeat;140 ascir::SizeExpr cur_vectorized_axis_stride = axis_info.prev_vectorized_axis_stride * axis_info.prev_repeat;
145- if (af::SymbolicUtils::StaticCheckEq(cur_axis_stride, gm_tensor.axis_strides[axis_pos]) != af::TriBool::kTrue ||141+ if (!ascgen_utils::IsDataCopyAxisContinuous(cur_axis_stride, cur_vectorized_axis_stride,
146- af::SymbolicUtils::StaticCheckEq(cur_vectorized_axis_stride, ub_tensor.vectorized_strides[vec_axis_pos]) !=142+ gm_tensor.axis_strides[axis_pos],
147- af::TriBool::kTrue ||143+ ub_tensor.vectorized_strides[vec_axis_pos]) ||
148 merge_info.merge_repeats.empty() ||144 merge_info.merge_repeats.empty() ||
149 (vec_axis_pos < (ub_tensor.vectorized_axis.size() - 1) &&145 (vec_axis_pos < (ub_tensor.vectorized_axis.size() - 1) &&
150 tpipe.tiler.GetAxis(ub_tensor.vectorized_axis[vec_axis_pos + 1]).type ==146 tpipe.tiler.GetAxis(ub_tensor.vectorized_axis[vec_axis_pos + 1]).type ==
@@ -161,6 +157,14 @@ bool CalculateDmaParams(const TPipe &tpipe, const Tensor &gm_tensor, const Tenso
161 std::reverse(merge_info.merge_repeats.begin(), merge_info.merge_repeats.end());157 std::reverse(merge_info.merge_repeats.begin(), merge_info.merge_repeats.end());
162 std::reverse(merge_info.merge_gm_strides.begin(), merge_info.merge_gm_strides.end());158 std::reverse(merge_info.merge_gm_strides.begin(), merge_info.merge_gm_strides.end());
163 std::reverse(merge_info.merge_ub_strides.begin(), merge_info.merge_ub_strides.end());159 std::reverse(merge_info.merge_ub_strides.begin(), merge_info.merge_ub_strides.end());
160+ GELOGD(
161+ "[Codegen NDDMA] effective view: raw_rank=%zu, effective_rank=%zu, repeats=[%s], gm_strides=[%s], "
162+ "ub_strides=[%s], vectorized_axis=[%s]",
163+ ub_tensor.vectorized_axis.size(), merge_info.merge_repeats.size(),
164+ ascgen_utils::VectorToStr(merge_info.merge_repeats).c_str(),
165+ ascgen_utils::VectorToStr(merge_info.merge_gm_strides).c_str(),
166+ ascgen_utils::VectorToStr(merge_info.merge_ub_strides).c_str(),
167+ ascgen_utils::VectorToStr(ub_tensor.vectorized_axis).c_str());
164 SetDataCopyParams(merge_info, param, multi_axis_copy);168 SetDataCopyParams(merge_info, param, multi_axis_copy);
165 return true;169 return true;
166}170}
@@ -65,12 +65,6 @@ struct DmaParamsExpr {
65 }65 }
66};66};
67 67 
68-struct AxisInfo {
69- ascir::SizeExpr prev_repeat = af::ops::One;
70- ascir::SizeExpr prev_axis_stride = af::ops::One;
71- ascir::SizeExpr prev_vectorized_axis_stride = af::ops::One;
72-};
73- 
74struct ApiLoopParams {68struct ApiLoopParams {
75 std::vector<std::string> outer_repeats;69 std::vector<std::string> outer_repeats;
76 std::vector<std::vector<ascir::SizeExpr>> inputs_strides;70 std::vector<std::vector<ascir::SizeExpr>> inputs_strides;
@@ -86,6 +80,12 @@ struct MergeInfo {
86 std::vector<ascir::SizeExpr> merge_ub_strides;80 std::vector<ascir::SizeExpr> merge_ub_strides;
87};81};
88 82 
83+struct AxisInfo {
84+ ascir::SizeExpr prev_repeat = af::ops::One;
85+ ascir::SizeExpr prev_axis_stride = af::ops::One;
86+ ascir::SizeExpr prev_vectorized_axis_stride = af::ops::One;
87+};
88+ 
89struct VectorizedAixsLoopStatus {89struct VectorizedAixsLoopStatus {
90 ascir::SizeExpr prev_repeat = af::ops::One;90 ascir::SizeExpr prev_repeat = af::ops::One;
91 std::vector<ascir::SizeExpr> prev_input_axis_stride;91 std::vector<ascir::SizeExpr> prev_input_axis_stride;
@@ -233,6 +233,19 @@ bool IsStaticSchedResult(const ascir::FusedScheduledResult &fused_schedule_resul
233 return true;233 return true;
234}234}
235 235 
236+bool ShouldIgnoreDataCopyZeroAxis(bool has_non_zero_axis, size_t axis_pos,
237+ const std::vector<af::Expression> &ub_strides) {
238+ return has_non_zero_axis || axis_pos == 0U ||
239+ af::SymbolicUtils::StaticCheckEq(ub_strides[axis_pos - 1U], af::ops::One) == af::TriBool::kTrue ||
240+ af::SymbolicUtils::StaticCheckEq(ub_strides[axis_pos - 1U], af::ops::Zero) == af::TriBool::kTrue;
241+}
242+ 
243+bool IsDataCopyAxisContinuous(const af::Expression &cur_gm_stride, const af::Expression &cur_ub_stride,
244+ const af::Expression &gm_stride, const af::Expression &ub_stride) {
245+ return af::SymbolicUtils::StaticCheckEq(cur_gm_stride, gm_stride) == af::TriBool::kTrue &&
246+ af::SymbolicUtils::StaticCheckEq(cur_ub_stride, ub_stride) == af::TriBool::kTrue;
247+}
248+ 
236const std::vector<af::Expression> &GetFrontendShapeVars(const ascir::FusedScheduledResult &fused_schedule_result) {249const std::vector<af::Expression> &GetFrontendShapeVars(const ascir::FusedScheduledResult &fused_schedule_result) {
237 if (fused_schedule_result.frontend_shape_vars_collected || !fused_schedule_result.frontend_shape_vars.empty()) {250 if (fused_schedule_result.frontend_shape_vars_collected || !fused_schedule_result.frontend_shape_vars.empty()) {
238 return fused_schedule_result.frontend_shape_vars;251 return fused_schedule_result.frontend_shape_vars;
@@ -167,6 +167,13 @@ bool IsScalarNextNodeSupportBlkTensor(const af::AscNodePtr &node);
167bool IsUbScalarLoad(const af::AscNodePtr &node);167bool IsUbScalarLoad(const af::AscNodePtr &node);
168bool IsStaticSchedResult(const ascir::FusedScheduledResult &fused_schedule_result);168bool IsStaticSchedResult(const ascir::FusedScheduledResult &fused_schedule_result);
169 169 
170+// Shared DataCopy axis rules. The caller owns the original loop state so the
171+// codegen state machine remains unchanged while ATT can reuse the same rules.
172+bool ShouldIgnoreDataCopyZeroAxis(bool has_non_zero_axis, size_t axis_pos,
173+ const std::vector<af::Expression> &ub_strides);
174+bool IsDataCopyAxisContinuous(const af::Expression &cur_gm_stride, const af::Expression &cur_ub_stride,
175+ const af::Expression &gm_stride, const af::Expression &ub_stride);
176+ 
170// Return the frontend ABI symbol list. Results produced before the new field177// Return the frontend ABI symbol list. Results produced before the new field
171// was introduced may only have origin_vars; keep that as a compatibility178// was introduced may only have origin_vars; keep that as a compatibility
172// fallback for legacy callers and unit fixtures.179// fallback for legacy callers and unit fixtures.
@@ -1269,7 +1269,6 @@ af::Status AnalyzeRewrittenGraph(af::AscGraph &graph, const af::AscNodePtr &indi
1269 if (!is_candidate_legal) {1269 if (!is_candidate_legal) {
1270 return af::SUCCESS;1270 return af::SUCCESS;
1271 }1271 }
1272- 
1273 if (template_id != ascir::TemplateId::kIndirectLoadSK) {1272 if (template_id != ascir::TemplateId::kIndirectLoadSK) {
1274 // 收集阶段:一次遍历收集全部状态(改写定稿后无需重收)1273 // 收集阶段:一次遍历收集全部状态(改写定稿后无需重收)
1275 GE_ASSERT_SUCCESS(CollectRewrittenBoundaries(indirect_load, analysis));1274 GE_ASSERT_SUCCESS(CollectRewrittenBoundaries(indirect_load, analysis));
@@ -410,7 +410,6 @@ TEST_F(STestAscirPerfV2, TestNddma1DModelHandlesStaticAndDynamicShapes) {
410 EXPECT_NE(cycles.find("TernaryOp"), std::string::npos);410 EXPECT_NE(cycles.find("TernaryOp"), std::string::npos);
411 EXPECT_NE(cycles.find("input_stride"), std::string::npos);411 EXPECT_NE(cycles.find("input_stride"), std::string::npos);
412 EXPECT_NE(cycles.find("output_stride"), std::string::npos);412 EXPECT_NE(cycles.find("output_stride"), std::string::npos);
413- EXPECT_NE(cycles.find("Max"), std::string::npos);
414 EXPECT_NE(cycles.find("Min"), std::string::npos);413 EXPECT_NE(cycles.find("Min"), std::string::npos);
415 EXPECT_NE(cycles.find("n"), std::string::npos);414 EXPECT_NE(cycles.find("n"), std::string::npos);
416}415}
@@ -639,7 +639,7 @@ TEST_F(UTestAscirPerfV2, TestNddma1DModelSupportsFourDtypeSizes) {
639 }639 }
640}640}
641 641 
642-TEST_F(UTestAscirPerfV2, TestNddmaRaw2DTo5DKeepLegacyOutputAfterMerge) {642+TEST_F(UTestAscirPerfV2, TestNddmaEffectiveRank2To5UsesNewModel) {
643 auto nddma = ApiPerfFactory::Instance().Create("NddmaV2");643 auto nddma = ApiPerfFactory::Instance().Create("NddmaV2");
644 ASSERT_NE(nddma, nullptr);644 ASSERT_NE(nddma, nullptr);
645 NodeInfo node;645 NodeInfo node;
@@ -663,11 +663,22 @@ TEST_F(UTestAscirPerfV2, TestNddmaRaw2DTo5DKeepLegacyOutputAfterMerge) {
663 663 
664 ASSERT_EQ(nddma->GetPerfFunc()(shapes, shapes, node, perf_res), af::SUCCESS);664 ASSERT_EQ(nddma->GetPerfFunc()(shapes, shapes, node, perf_res), af::SUCCESS);
665 const auto cycles = Str(perf_res.pipe_res[PipeType::AIV_MTE2]);665 const auto cycles = Str(perf_res.pipe_res[PipeType::AIV_MTE2]);
666- EXPECT_EQ(cycles.find("nddma_1d_multicore"), std::string::npos);666+ EXPECT_NE(cycles.find("nddma_1d_multicore"), std::string::npos);
667- EXPECT_EQ(cycles, "((2048 / (((6.3899998664856 / (block_dim)) + 7.6100001335144))) + 418.978912353516)");
668 }667 }
669}668}
670 669 
670+TEST_F(UTestAscirPerfV2, TestNddmaFallsBackOnDtypeSizeMismatch) {
671+ const auto shape = Make1DNddmaShape("float16", 4, CreateExpr(256), CreateExpr(1), CreateExpr(1));
672+ const std::vector<TensorShapeInfo> shapes = {shape};
673+ NodeInfo node;
674+ node.node_ptr = GraphConstructUtils::ConstructSingleOp("Nddma", 1, 1);
675+ PerfOutputInfo perf_res;
676+ auto nddma = ApiPerfFactory::Instance().Create("NddmaV2");
677+ ASSERT_NE(nddma, nullptr);
678+ ASSERT_EQ(nddma->GetPerfFunc()(shapes, shapes, node, perf_res), af::SUCCESS);
679+ EXPECT_EQ(Str(perf_res.pipe_res[PipeType::AIV_MTE2]).find("nddma_1d_multicore"), std::string::npos);
680+}
681+ 
671TEST_F(UTestAscirPerfV2, TestNddma1DModelFallsBackForCvUbFusion) {682TEST_F(UTestAscirPerfV2, TestNddma1DModelFallsBackForCvUbFusion) {
672 const auto shape = Make1DNddmaShape("int8", 1, CreateExpr(256), CreateExpr(1), CreateExpr(1));683 const auto shape = Make1DNddmaShape("int8", 1, CreateExpr(256), CreateExpr(1), CreateExpr(1));
673 const std::vector<TensorShapeInfo> shapes = {shape};684 const std::vector<TensorShapeInfo> shapes = {shape};
@@ -13,6 +13,7 @@
13#include <numeric>13#include <numeric>
14#include "gtest/gtest.h"14#include "gtest/gtest.h"
15#include "tests/depends/slog/src/slog_stub.h"15#include "tests/depends/slog/src/slog_stub.h"
16+#include "common_utils.h"
16#include "v35/att/api_perf_register/nddma_model.h"17#include "v35/att/api_perf_register/nddma_model.h"
17 18 
18namespace att {19namespace att {
@@ -40,7 +41,7 @@ TEST(NddmaModelV2, EvaluatesStaticB8LowCoreFormula) {
40 41 
41 ASSERT_EQ(EvaluateNddmaModel(descriptor, "uint8", CreateExpr(2), result), af::SUCCESS);42 ASSERT_EQ(EvaluateNddmaModel(descriptor, "uint8", CreateExpr(2), result), af::SUCCESS);
42 ASSERT_TRUE(result.selected);43 ASSERT_TRUE(result.selected);
43- EXPECT_EQ(result.model_name, "NDDMA_1D_MULTICORE_V1");44+ EXPECT_EQ(result.model_name, "NDDMA_1D_MULTICORE_V2");
44 EXPECT_EQ(result.fallback_reason, NddmaFallbackReason::kNone);45 EXPECT_EQ(result.fallback_reason, NddmaFallbackReason::kNone);
45 EXPECT_TRUE(result.ternary_ops.empty());46 EXPECT_TRUE(result.ternary_ops.empty());
46 EXPECT_NEAR(GetConstCycles(result.cycles), 219.37435914613698, 1e-6);47 EXPECT_NEAR(GetConstCycles(result.cycles), 219.37435914613698, 1e-6);
@@ -52,8 +53,7 @@ TEST(NddmaModelV2, UsesMergedLowCoreCoefficientsForStridedOutput) {
52 53 
53 ASSERT_EQ(EvaluateNddmaModel(descriptor, "uint8", CreateExpr(2), result), af::SUCCESS);54 ASSERT_EQ(EvaluateNddmaModel(descriptor, "uint8", CreateExpr(2), result), af::SUCCESS);
54 ASSERT_TRUE(result.selected);55 ASSERT_TRUE(result.selected);
55- result.cycles.Simplify();56+ EXPECT_TRUE(result.cycles.IsValid());
56- EXPECT_NE(Str(result.cycles).find("6.998497418"), std::string::npos);
57}57}
58 58 
59TEST(NddmaModelV2, AllowsZeroInputStride) {59TEST(NddmaModelV2, AllowsZeroInputStride) {
@@ -71,7 +71,7 @@ TEST(NddmaModelV2, EvaluatesStaticB16HighCoreFormula) {
71 71 
72 ASSERT_EQ(EvaluateNddmaModel(descriptor, "float16", CreateExpr(8), result), af::SUCCESS);72 ASSERT_EQ(EvaluateNddmaModel(descriptor, "float16", CreateExpr(8), result), af::SUCCESS);
73 ASSERT_TRUE(result.selected);73 ASSERT_TRUE(result.selected);
74- EXPECT_NEAR(GetConstCycles(result.cycles), 3670.0883956043676, 1e-6);74+ EXPECT_NEAR(GetConstCycles(result.cycles), 3667.5976503571769, 1e-6);
75}75}
76 76 
77TEST(NddmaModelV2, EvaluatesStaticB32LowCoreFormula) {77TEST(NddmaModelV2, EvaluatesStaticB32LowCoreFormula) {
@@ -80,7 +80,7 @@ TEST(NddmaModelV2, EvaluatesStaticB32LowCoreFormula) {
80 80 
81 ASSERT_EQ(EvaluateNddmaModel(descriptor, "float32", CreateExpr(2), result), af::SUCCESS);81 ASSERT_EQ(EvaluateNddmaModel(descriptor, "float32", CreateExpr(2), result), af::SUCCESS);
82 ASSERT_TRUE(result.selected);82 ASSERT_TRUE(result.selected);
83- EXPECT_NEAR(GetConstCycles(result.cycles), 418.8332396657097, 1e-6);83+ EXPECT_NEAR(GetConstCycles(result.cycles), 415.93874765450965, 1e-6);
84}84}
85 85 
86TEST(NddmaModelV2, EvaluatesStaticB64HighCoreWithSaturatedInputStride) {86TEST(NddmaModelV2, EvaluatesStaticB64HighCoreWithSaturatedInputStride) {
@@ -89,7 +89,7 @@ TEST(NddmaModelV2, EvaluatesStaticB64HighCoreWithSaturatedInputStride) {
89 89 
90 ASSERT_EQ(EvaluateNddmaModel(descriptor, "int64", CreateExpr(8), result), af::SUCCESS);90 ASSERT_EQ(EvaluateNddmaModel(descriptor, "int64", CreateExpr(8), result), af::SUCCESS);
91 ASSERT_TRUE(result.selected);91 ASSERT_TRUE(result.selected);
92- EXPECT_NEAR(GetConstCycles(result.cycles), 1191.9782139196707, 1e-6);92+ EXPECT_NEAR(GetConstCycles(result.cycles), 1490.6546508742531, 1e-6);
93}93}
94 94 
95TEST(NddmaModelV2, ReplaysAllCoefficientGroupsForFourDtypeSizes) {95TEST(NddmaModelV2, ReplaysAllCoefficientGroupsForFourDtypeSizes) {
@@ -98,10 +98,10 @@ TEST(NddmaModelV2, ReplaysAllCoefficientGroupsForFourDtypeSizes) {
98 double low_core;98 double low_core;
99 double high_core;99 double high_core;
100 };100 };
101- const DtypeCase cases[] = {{"int8", 1846.461366862137, 3651.7081836980888},101+ const DtypeCase cases[] = {{"int8", 1846.461366862137, 3651.7082163688292},
102- {"float16", 1853.918764807075, 3670.0883956043676},102+ {"float16", 1852.9241082038752, 3667.5976503571769},
103- {"float32", 418.8332396657097, 787.4986605915115},103+ {"float32", 415.93874765450965, 801.7379345551185},
104- {"int64", 457.66637430298624, 889.5759615048607}};104+ {"int64", 436.1371738143462, 834.9812022377600}};
105 const auto descriptor = MakeDescriptor(CreateExpr(256), CreateExpr(4), CreateExpr(2));105 const auto descriptor = MakeDescriptor(CreateExpr(256), CreateExpr(4), CreateExpr(2));
106 for (const auto &test_case : cases) {106 for (const auto &test_case : cases) {
107 SCOPED_TRACE(test_case.dtype);107 SCOPED_TRACE(test_case.dtype);
@@ -136,7 +136,7 @@ TEST(NddmaModelV2, RejectsMismatchedDescriptorSchema) {
136 EXPECT_STREQ(NddmaFallbackReasonToString(result.fallback_reason), "schema_mismatch");136 EXPECT_STREQ(NddmaFallbackReasonToString(result.fallback_reason), "schema_mismatch");
137}137}
138 138 
139-TEST(NddmaModelV2, FallsBackForUnregisteredRanks2To5) {139+TEST(NddmaModelV2, EvaluatesRegisteredRanks2To5) {
140 for (size_t rank = 2U; rank <= 5U; ++rank) {140 for (size_t rank = 2U; rank <= 5U; ++rank) {
141 SCOPED_TRACE(rank);141 SCOPED_TRACE(rank);
142 NddmaDescriptorInfo descriptor;142 NddmaDescriptorInfo descriptor;
@@ -149,13 +149,29 @@ TEST(NddmaModelV2, FallsBackForUnregisteredRanks2To5) {
149 NddmaModelResult result;149 NddmaModelResult result;
150 150 
151 ASSERT_EQ(EvaluateNddmaModel(descriptor, "int64", CreateExpr(1), result), af::SUCCESS);151 ASSERT_EQ(EvaluateNddmaModel(descriptor, "int64", CreateExpr(1), result), af::SUCCESS);
152- EXPECT_FALSE(result.selected);152+ EXPECT_TRUE(result.selected);
153- EXPECT_EQ(result.raw_rank, rank);153+ EXPECT_EQ(result.raw_rank, 0U);
154 EXPECT_EQ(result.effective_rank, rank);154 EXPECT_EQ(result.effective_rank, rank);
155- EXPECT_EQ(result.fallback_reason, NddmaFallbackReason::kNoRegisteredModel);155+ EXPECT_EQ(result.fallback_reason, NddmaFallbackReason::kNone);
156+ EXPECT_EQ(result.model_name, "NDDMA_ND_MULTICORE_V1");
156 }157 }
157}158}
158 159 
160+TEST(NddmaModelV2, UsesInnerToOuterFormulaOrderForTwoDimensions) {
161+ NddmaDescriptorInfo descriptor;
162+ // Descriptor is effective outer -> inner; the formula must consume [inner, outer].
163+ descriptor.output_dims = {CreateExpr(3), CreateExpr(8)};
164+ descriptor.input_strides = {CreateExpr(8), CreateExpr(1)};
165+ descriptor.output_strides = {CreateExpr(8), CreateExpr(1)};
166+ descriptor.vectorized_axis = {1, 0};
167+ NddmaModelResult result;
168+ ASSERT_EQ(EvaluateNddmaModel(descriptor, "float32", CreateExpr(2), result), af::SUCCESS);
169+ ASSERT_TRUE(result.selected);
170+ EXPECT_EQ(result.effective_rank, 2U);
171+ EXPECT_EQ(result.model_name, "NDDMA_ND_MULTICORE_V1");
172+ EXPECT_TRUE(result.cycles.IsValid());
173+}
174+ 
159TEST(NddmaModelV2, RejectsNonPositiveStaticOutputStride) {175TEST(NddmaModelV2, RejectsNonPositiveStaticOutputStride) {
160 const auto descriptor = MakeDescriptor(CreateExpr(16), CreateExpr(1), CreateExpr(0));176 const auto descriptor = MakeDescriptor(CreateExpr(16), CreateExpr(1), CreateExpr(0));
161 NddmaModelResult result;177 NddmaModelResult result;
@@ -166,7 +182,7 @@ TEST(NddmaModelV2, RejectsNonPositiveStaticOutputStride) {
166}182}
167 183 
168TEST(NddmaModelV2, RejectsRankOutsideSupportedRange) {184TEST(NddmaModelV2, RejectsRankOutsideSupportedRange) {
169- for (const size_t rank : {0U, 6U}) {185+ for (const size_t rank : {0U, 6U, 7U}) {
170 SCOPED_TRACE(rank);186 SCOPED_TRACE(rank);
171 NddmaDescriptorInfo descriptor;187 NddmaDescriptorInfo descriptor;
172 descriptor.output_dims.assign(rank, CreateExpr(8));188 descriptor.output_dims.assign(rank, CreateExpr(8));
@@ -182,6 +198,16 @@ TEST(NddmaModelV2, RejectsRankOutsideSupportedRange) {
182 }198 }
183}199}
184 200 
201+TEST(NddmaModelV2, SharedDataCopyAxisRulesMatchCodegenConditions) {
202+ const std::vector<Expr> ub_strides = {CreateExpr(15), CreateExpr(5), CreateExpr(1)};
203+ EXPECT_TRUE(ascgen_utils::ShouldIgnoreDataCopyZeroAxis(false, 0U, ub_strides));
204+ EXPECT_TRUE(ascgen_utils::ShouldIgnoreDataCopyZeroAxis(true, 2U, ub_strides));
205+ EXPECT_FALSE(ascgen_utils::ShouldIgnoreDataCopyZeroAxis(false, 2U, {CreateExpr(15), CreateExpr(5), CreateExpr(3)}));
206+ 
207+ EXPECT_TRUE(ascgen_utils::IsDataCopyAxisContinuous(CreateExpr(5), CreateExpr(1), CreateExpr(5), CreateExpr(1)));
208+ EXPECT_FALSE(ascgen_utils::IsDataCopyAxisContinuous(CreateExpr(4), CreateExpr(1), CreateExpr(5), CreateExpr(1)));
209+}
210+ 
185TEST(NddmaModelV2, RejectsUnsupportedDtype) {211TEST(NddmaModelV2, RejectsUnsupportedDtype) {
186 const auto descriptor = MakeDescriptor(CreateExpr(16), CreateExpr(1), CreateExpr(1));212 const auto descriptor = MakeDescriptor(CreateExpr(16), CreateExpr(1), CreateExpr(1));
187 NddmaModelResult result;213 NddmaModelResult result;
@@ -257,10 +283,10 @@ TEST(NddmaModelV2, BuildsDescriptorFromRawTensorShape) {
257 NddmaDescriptorInfo descriptor;283 NddmaDescriptorInfo descriptor;
258 284 
259 EXPECT_EQ(BuildNddmaDescriptor(shape_info, {3, 1}, descriptor), NddmaFallbackReason::kNone);285 EXPECT_EQ(BuildNddmaDescriptor(shape_info, {3, 1}, descriptor), NddmaFallbackReason::kNone);
260- EXPECT_EQ(descriptor.output_dims, shape_info.repeats);286+ EXPECT_EQ(descriptor.output_dims, (std::vector<Expr>{CreateExpr(256)}));
261- EXPECT_EQ(descriptor.input_strides, shape_info.gm_strides);287+ EXPECT_EQ(descriptor.input_strides, (std::vector<Expr>{CreateExpr(2)}));
262- EXPECT_EQ(descriptor.output_strides, shape_info.strides);288+ EXPECT_EQ(descriptor.output_strides, (std::vector<Expr>{CreateExpr(1)}));
263- EXPECT_EQ(descriptor.vectorized_axis, (std::vector<int64_t>{3, 1}));289+ EXPECT_EQ(descriptor.vectorized_axis, (std::vector<int64_t>{1}));
264}290}
265 291 
266TEST(NddmaModelV2, RejectsMissingVectorizedAxisForCodegenParity) {292TEST(NddmaModelV2, RejectsMissingVectorizedAxisForCodegenParity) {
@@ -40,7 +40,25 @@ af::Status TryNewNddmaModel(const TensorShapeInfo &shape_info, const NodeInfo &n
40 LogNddmaFallback(node_detail.name, shape_info.data_type, nullptr, result);40 LogNddmaFallback(node_detail.name, shape_info.data_type, nullptr, result);
41 return af::SUCCESS;41 return af::SUCCESS;
42 }42 }
43- const auto build_reason = BuildNddmaDescriptor(shape_info, GetNddmaVectorizedAxis(node.node_ptr), descriptor);43+ uint64_t expected_dtype_size = 0U;
44+ const auto dtype_iter = kDataTypeSizeMap.find(shape_info.data_type);
45+ if (dtype_iter == kDataTypeSizeMap.end() || !dtype_iter->second.GetConstValue(expected_dtype_size) ||
46+ expected_dtype_size == 0U || expected_dtype_size != shape_info.data_type_size) {
47+ result.raw_rank = shape_info.repeats.size();
48+ result.fallback_reason = NddmaFallbackReason::kDtypeUnsupported;
49+ LogNddmaFallback(node_detail.name, shape_info.data_type, nullptr, result);
50+ return af::SUCCESS;
51+ }
52+ std::vector<bool> tile_inner;
53+ if (!node.outputs.empty() && node.outputs[0] != nullptr &&
54+ node.outputs[0]->dim_info.size() == shape_info.repeats.size()) {
55+ tile_inner.reserve(node.outputs[0]->dim_info.size());
56+ for (const auto *axis : node.outputs[0]->dim_info) {
57+ tile_inner.push_back(axis != nullptr && axis->axis_type == AxisPosition::INNER && !axis->is_bind_multi_core);
58+ }
59+ }
60+ const auto build_reason =
61+ BuildNddmaDescriptor(shape_info, GetNddmaVectorizedAxis(node.node_ptr), descriptor, tile_inner);
44 if (build_reason != NddmaFallbackReason::kNone) {62 if (build_reason != NddmaFallbackReason::kNone) {
45 result.raw_rank = shape_info.repeats.size();63 result.raw_rank = shape_info.repeats.size();
46 result.fallback_reason = build_reason;64 result.fallback_reason = build_reason;
@@ -49,6 +67,7 @@ af::Status TryNewNddmaModel(const TensorShapeInfo &shape_info, const NodeInfo &n
49 }67 }
50 node_detail.nddma_descriptor = descriptor;68 node_detail.nddma_descriptor = descriptor;
51 GE_ASSERT_SUCCESS(EvaluateNddmaModel(descriptor, shape_info.data_type, CreateExpr("block_dim"), result));69 GE_ASSERT_SUCCESS(EvaluateNddmaModel(descriptor, shape_info.data_type, CreateExpr("block_dim"), result));
70+ result.raw_rank = shape_info.repeats.size();
52 if (!result.selected) {71 if (!result.selected) {
53 LogNddmaFallback(node_detail.name, shape_info.data_type, &descriptor, result);72 LogNddmaFallback(node_detail.name, shape_info.data_type, &descriptor, result);
54 return af::SUCCESS;73 return af::SUCCESS;
@@ -23,51 +23,27 @@ constexpr size_t kMaxNddmaRank = 5U;
23constexpr uint64_t kLowCoreUpperBound = 2U;23constexpr uint64_t kLowCoreUpperBound = 2U;
24constexpr uint64_t kInputStrideUpperBound = 128U;24constexpr uint64_t kInputStrideUpperBound = 128U;
25 25 
26-// 合并模型直接表示最终多项式,避免运行时重新组合 NG、NGM 和 rho。
27-struct NddmaLowCoreParams {
28- double constant;
29- double bytes;
30- double stride;
31- double bytes_stride;
32-};
33- 
34-struct NddmaHighCoreParams {
35- double constant;
36- double stride;
37- double stride_square;
38- double bytes;
39- double bytes_stride;
40- double bytes_stride_square;
41-};
42- 
43struct Nddma1DParams {26struct Nddma1DParams {
44- NddmaLowCoreParams low_os_one;27+ double t1;
45- NddmaLowCoreParams low_os_ge_two;28+ double h1;
46- NddmaHighCoreParams high_os_one;29+ double t2;
47- NddmaHighCoreParams high_os_ge_two;30+ double h2;
31+ double a1, a2, b1, b2, b3, b4, c1, c2, c3, c4;
48};32};
49 33 
50const Nddma1DParams *GetNddma1DParams(uint64_t dtype_size) {34const Nddma1DParams *GetNddma1DParams(uint64_t dtype_size) {
51- static constexpr Nddma1DParams kB8 = {35+ static constexpr Nddma1DParams kB8 = {11.7626, 194.421, 6.05735, 373.274, 1.1117457,
52- {194.421, 0.08501521772, 1.1117457, 0.0081160848},36+ 0.0081160848, -140.60967, -0.85102455, 6.9134822, -0.0081240796,
53- {53.81133, 6.998497418, 0.26072115, -0.0000079948},37+ 1.5052979, 0.007823918, 0.47972982, -0.0078196972};
54- {373.274, 1.673508468, 0.008698207194, 0.1650886939, 0.01221712541, 0.00006349958196},38+ static constexpr Nddma1DParams kB16 = {25.7579, 204.604, 13.259, 399.909, 0.93077499,
55- {94.1599102, 0.5169452208, 0.000001100449223, 13.88854236, 0.00001331045708, -0.00000000003374437189}};39+ 0.0041300563, -144.93927, -0.65222426, 3.4593577, -0.0041392279,
56- static constexpr Nddma1DParams kB16 = {40+ 1.1389125, 0.015061621, 0.84385983, -0.015043028};
57- {204.604, 0.0388230407, 0.96094416, 0.0091341897},41+ static constexpr Nddma1DParams kB32 = {57.2624, 235.137, 29.4096, 453.859, 0.29118972,
58- {61.53027, 3.498120341, 0.34357215, -0.0000114298},42+ 0.0021022688, -75.847618, -0.098291434, 0.23017978, -0.0021037148,
59- {399.909, 2.009597249, -0.007116959052, 0.07542046912, 0.01910209069, -0.00006764977277},43+ 1.5982043, 0.0048954057, 0.21802244, 0.0016884734};
60- {116.1609008, 0.6810487439, 0.0000008016156689, 6.936002307, -0.00001459675784, -0.00000000002666778076}};44+ static constexpr Nddma1DParams kB64 = {57.2346, 243.205, 29.3906, 468.971, 0.32660604,
61- static constexpr Nddma1DParams kB32 = {45+ 0.00087486841, -62.346718, -0.089369196, 0.10364992, -0.00088051835,
62- {235.137, 0.01746346643, 0.3675047, 0.0058486918},46+ 0.75774914, 0.021509891, 1.1274142, -0.021494907};
63- {158.723147, 0.2427621164, 1.4596785, 0.0013874442},
64- {453.859, 1.420515439, -0.008545279188, 0.03400250258, 0.02260694081, -0.0001359947351},
65- {323.0812754, 0.7652737348, 0.03310192195, 0.4195876242, 0.007483747325, 0.00003146382551}};
66- static constexpr Nddma1DParams kB64 = {
67- {243.205, 0.01747194879, 0.90051511, 0.0030237371},
68- {189.027128, 0.1244607288, 1.98330501, 0.0007092839},
69- {468.971, 3.366949543, -0.02124182228, 0.03402448402, 0.0113054963, -0.00007132549515},
70- {361.5012267, 4.200466405, -0.009748131943, 0.2462524565, 0.0008811088948, -0.000003486197538}};
71 switch (dtype_size) {47 switch (dtype_size) {
72 case 1U:48 case 1U:
73 return &kB8;49 return &kB8;
@@ -143,43 +119,97 @@ bool HasStaticByteCountOverflow(const Expr &dim, uint64_t dtype_size) {
143 return static_cast<uint64_t>(dim_value) > std::numeric_limits<uint64_t>::max() / dtype_size;119 return static_cast<uint64_t>(dim_value) > std::numeric_limits<uint64_t>::max() / dtype_size;
144}120}
145 121 
146-Expr BuildLowCore(const NddmaLowCoreParams &params, const Expr &bytes, const Expr &stride) {122+NddmaFallbackReason ValidateNddmaDescriptorInput(const TensorShapeInfo &shape_info,
147- return CreateExpr(params.constant) + CreateExpr(params.bytes) * bytes + CreateExpr(params.stride) * stride +123+ const std::vector<int64_t> &vectorized_axis,
148- CreateExpr(params.bytes_stride) * bytes * stride;124+ const std::vector<bool> &tile_inner) {
149-}125+ if (shape_info.repeats.empty()) {
150- 126+ return NddmaFallbackReason::kNoDescriptor;
151-Expr BuildHighCore(const NddmaHighCoreParams &params, const Expr &bytes, const Expr &stride) {
152- const Expr stride_square = stride * stride;
153- return CreateExpr(params.constant) + CreateExpr(params.stride) * stride +
154- CreateExpr(params.stride_square) * stride_square +
155- bytes * (CreateExpr(params.bytes) + CreateExpr(params.bytes_stride) * stride +
156- CreateExpr(params.bytes_stride_square) * stride_square);
157-}
158- 
159-Expr SelectOutputStrideModel(const Expr &output_stride, const Expr &os_one, const Expr &os_ge_two) {
160- if (output_stride.IsConstExpr()) {
161- if (af::SymbolicUtils::StaticCheckEq(output_stride, af::sym::kSymbolOne) == af::TriBool::kTrue) {
162- return os_one;
163- }
164- if (af::SymbolicUtils::StaticCheckGt(output_stride, af::sym::kSymbolOne) == af::TriBool::kTrue) {
165- return os_ge_two;
166- }
167 }127 }
168- const Expr output_stride_gate =128+ if (shape_info.repeats.size() != shape_info.gm_strides.size() ||
169- af::sym::Max(af::sym::kSymbolZero, af::sym::Min(af::sym::kSymbolOne, output_stride - af::sym::kSymbolOne));129+ shape_info.repeats.size() != shape_info.strides.size()) {
170- return os_one + output_stride_gate * (os_ge_two - os_one);130+ return NddmaFallbackReason::kSchemaMismatch;
131+ }
132+ if (vectorized_axis.empty() || vectorized_axis.size() != shape_info.repeats.size()) {
133+ return NddmaFallbackReason::kCodegenMismatch;
134+ }
135+ if (!tile_inner.empty() && tile_inner.size() != shape_info.repeats.size()) {
136+ return NddmaFallbackReason::kSchemaMismatch;
137+ }
138+ return NddmaFallbackReason::kNone;
171}139}
172 140 
173-void BuildNddma1DBranches(const NddmaNormalizedDesc &descriptor, uint64_t dtype_size, const Nddma1DParams &params,141+void BuildNddmaEffectiveView(const TensorShapeInfo &shape_info, const std::vector<bool> &tile_inner,
174- Expr &low_core, Expr &high_core) {142+ std::vector<Expr> &effective_repeats, std::vector<Expr> &effective_gm_strides,
175- const Expr bytes = descriptor.output_dims[0] * CreateExpr(dtype_size);143+ std::vector<Expr> &effective_ub_strides, std::vector<int64_t> &effective_axes) {
176- const Expr input_stride = af::sym::Min(CreateExpr(kInputStrideUpperBound), descriptor.input_strides[0]);144+ const auto &origin_repeats =
177- const Expr low_os_one = BuildLowCore(params.low_os_one, bytes, input_stride);145+ shape_info.origin_repeats.size() == shape_info.repeats.size() ? shape_info.origin_repeats : shape_info.repeats;
178- const Expr low_os_ge_two = BuildLowCore(params.low_os_ge_two, bytes, input_stride);146+ const size_t rank = origin_repeats.size();
179- const Expr high_os_one = BuildHighCore(params.high_os_one, bytes, input_stride);147+ Expr prev_repeat = CreateExpr(1);
180- const Expr high_os_ge_two = BuildHighCore(params.high_os_ge_two, bytes, input_stride);148+ Expr prev_actual_repeat = CreateExpr(1);
181- low_core = SelectOutputStrideModel(descriptor.output_strides[0], low_os_one, low_os_ge_two);149+ Expr prev_gm_stride = CreateExpr(1);
182- high_core = SelectOutputStrideModel(descriptor.output_strides[0], high_os_one, high_os_ge_two);150+ Expr prev_ub_stride = CreateExpr(1);
151+ bool has_non_zero_axis = false;
152+ for (size_t pos = rank; pos-- > 0U;) {
153+ const bool ignore_zero_axis =
154+ ascgen_utils::ShouldIgnoreDataCopyZeroAxis(has_non_zero_axis, pos, shape_info.strides);
155+ const bool zero_axis =
156+ af::SymbolicUtils::StaticCheckEq(shape_info.gm_strides[pos], CreateExpr(0)) == af::TriBool::kTrue &&
157+ af::SymbolicUtils::StaticCheckEq(shape_info.strides[pos], CreateExpr(0)) == af::TriBool::kTrue &&
158+ ignore_zero_axis;
159+ if (zero_axis) {
160+ continue;
161+ }
162+ has_non_zero_axis = true;
163+ const Expr cur_gm_stride = prev_gm_stride * prev_repeat;
164+ const Expr cur_ub_stride = prev_ub_stride * prev_repeat;
165+ const bool tile_boundary = !tile_inner.empty() && pos + 1U < rank && tile_inner[pos + 1U];
166+ if (!ascgen_utils::IsDataCopyAxisContinuous(cur_gm_stride, cur_ub_stride, shape_info.gm_strides[pos],
167+ shape_info.strides[pos]) ||
168+ effective_repeats.empty() || tile_boundary) {
169+ effective_repeats.emplace_back(shape_info.repeats[pos]);
170+ effective_gm_strides.emplace_back(shape_info.gm_strides[pos]);
171+ effective_ub_strides.emplace_back(shape_info.strides[pos]);
172+ effective_axes.emplace_back(static_cast<int64_t>(pos));
173+ prev_gm_stride = shape_info.gm_strides[pos];
174+ prev_ub_stride = shape_info.strides[pos];
175+ prev_repeat = origin_repeats[pos];
176+ prev_actual_repeat = shape_info.repeats[pos];
177+ continue;
178+ }
179+ const Expr product = origin_repeats[pos] * prev_repeat;
180+ effective_repeats.back() = shape_info.repeats[pos] * prev_actual_repeat;
181+ prev_repeat = product;
182+ prev_actual_repeat = shape_info.repeats[pos] * prev_actual_repeat;
183+ }
184+ std::reverse(effective_repeats.begin(), effective_repeats.end());
185+ std::reverse(effective_gm_strides.begin(), effective_gm_strides.end());
186+ std::reverse(effective_ub_strides.begin(), effective_ub_strides.end());
187+ std::reverse(effective_axes.begin(), effective_axes.end());
188+}
189+ 
190+NddmaFallbackReason MapNddmaEffectiveAxes(std::vector<int64_t> &effective_axes,
191+ const std::vector<int64_t> &vectorized_axis) {
192+ for (auto &axis : effective_axes) {
193+ if (axis < 0 || static_cast<size_t>(axis) >= vectorized_axis.size()) {
194+ return NddmaFallbackReason::kCodegenMismatch;
195+ }
196+ axis = vectorized_axis[static_cast<size_t>(axis)];
197+ }
198+ return NddmaFallbackReason::kNone;
199+}
200+ 
201+Expr AbsExpr(const Expr &value) {
202+ return af::sym::Max(value, af::sym::kSymbolZero - value);
203+}
204+ 
205+Expr BuildNddma1DResidual(const Nddma1DParams &p, const Expr &bytes, const Expr &input_stride,
206+ const Expr &output_stride, uint64_t dtype_size, bool high_core) {
207+ const Expr s = af::sym::Min(CreateExpr(kInputStrideUpperBound), input_stride * CreateExpr(dtype_size));
208+ const Expr g = af::sym::Min(af::sym::kSymbolOne, output_stride - af::sym::kSymbolOne);
209+ const Expr ng = (CreateExpr(p.a1) + CreateExpr(p.a2) * bytes) * s;
210+ const Expr ngu = (CreateExpr(p.b1) + CreateExpr(p.b2) * s + (CreateExpr(p.b3) + CreateExpr(p.b4) * s) * bytes) * g;
211+ const Expr rho = CreateExpr(p.c1) + CreateExpr(p.c2) * s + g * (CreateExpr(p.c3) + CreateExpr(p.c4) * s);
212+ return (ng + ngu) * (high_core ? rho : CreateExpr(1));
183}213}
184 214 
185af::Status SelectCoreBranch(const Expr &block_dim, const Expr &low_core, const Expr &high_core,215af::Status SelectCoreBranch(const Expr &block_dim, const Expr &low_core, const Expr &high_core,
@@ -203,6 +233,42 @@ af::Status SelectCoreBranch(const Expr &block_dim, const Expr &low_core, const E
203 result.ternary_ops[result.cycles] = std::move(ternary);233 result.ternary_ops[result.cycles] = std::move(ternary);
204 return af::SUCCESS;234 return af::SUCCESS;
205}235}
236+ 
237+NddmaFallbackReason BuildNddmaCoreCycles(const NddmaNormalizedDesc &normalized, const Nddma1DParams &params,
238+ uint64_t dtype_size, Expr &low_core, Expr &high_core) {
239+ Expr total_elements = CreateExpr(1);
240+ for (const auto &dim : normalized.output_dims) {
241+ total_elements = total_elements * dim;
242+ }
243+ if (HasStaticByteCountOverflow(total_elements, dtype_size)) {
244+ return NddmaFallbackReason::kSchemaMismatch;
245+ }
246+ const Expr total_bytes = total_elements * CreateExpr(dtype_size);
247+ low_core = total_bytes / CreateExpr(params.t1) + CreateExpr(params.h1);
248+ high_core = total_bytes / CreateExpr(params.t2) + CreateExpr(params.h2);
249+ Expr input_prefix = CreateExpr(0);
250+ Expr output_prefix = CreateExpr(0);
251+ for (size_t i = 0; i < normalized.effective_rank; ++i) {
252+ if (i > 0U) {
253+ input_prefix = input_prefix + normalized.output_dims[i - 1U] * normalized.input_strides[i - 1U];
254+ output_prefix = output_prefix + normalized.output_dims[i - 1U] * normalized.output_strides[i - 1U];
255+ }
256+ Expr input_stride = normalized.input_strides[i];
257+ Expr output_stride = normalized.output_strides[i];
258+ if (i > 0U) {
259+ input_stride = AbsExpr(input_stride - input_prefix) + CreateExpr(1);
260+ output_stride = AbsExpr(output_stride - output_prefix) + CreateExpr(1);
261+ }
262+ Expr suffix_elements = CreateExpr(1);
263+ for (size_t suffix = i; suffix < normalized.effective_rank; ++suffix) {
264+ suffix_elements = suffix_elements * normalized.output_dims[suffix];
265+ }
266+ const Expr bytes = suffix_elements * CreateExpr(dtype_size);
267+ low_core = low_core + BuildNddma1DResidual(params, bytes, input_stride, output_stride, dtype_size, false);
268+ high_core = high_core + BuildNddma1DResidual(params, bytes, input_stride, output_stride, dtype_size, true);
269+ }
270+ return NddmaFallbackReason::kNone;
271+}
206} // namespace272} // namespace
207 273 
208const char *NddmaFallbackReasonToString(NddmaFallbackReason reason) {274const char *NddmaFallbackReasonToString(NddmaFallbackReason reason) {
@@ -214,30 +280,39 @@ const char *NddmaFallbackReasonToString(NddmaFallbackReason reason) {
214}280}
215 281 
216NddmaFallbackReason BuildNddmaDescriptor(const TensorShapeInfo &shape_info, const std::vector<int64_t> &vectorized_axis,282NddmaFallbackReason BuildNddmaDescriptor(const TensorShapeInfo &shape_info, const std::vector<int64_t> &vectorized_axis,
217- NddmaDescriptorInfo &descriptor) {283+ NddmaDescriptorInfo &descriptor, const std::vector<bool> &tile_inner) {
218 descriptor = NddmaDescriptorInfo{};284 descriptor = NddmaDescriptorInfo{};
219- if (shape_info.repeats.empty()) {285+ const auto input_reason = ValidateNddmaDescriptorInput(shape_info, vectorized_axis, tile_inner);
220- return NddmaFallbackReason::kNoDescriptor;286+ if (input_reason != NddmaFallbackReason::kNone) {
287+ return input_reason;
221 }288 }
222- if (shape_info.repeats.size() != shape_info.gm_strides.size() ||289+ std::vector<Expr> effective_repeats;
223- shape_info.repeats.size() != shape_info.strides.size()) {290+ std::vector<Expr> effective_gm_strides;
224- return NddmaFallbackReason::kSchemaMismatch;291+ std::vector<Expr> effective_ub_strides;
292+ std::vector<int64_t> effective_axes;
293+ BuildNddmaEffectiveView(shape_info, tile_inner, effective_repeats, effective_gm_strides, effective_ub_strides,
294+ effective_axes);
295+ const auto axis_reason = MapNddmaEffectiveAxes(effective_axes, vectorized_axis);
296+ if (axis_reason != NddmaFallbackReason::kNone) {
297+ return axis_reason;
225 }298 }
226- descriptor.output_dims = shape_info.repeats;299+ descriptor.output_dims = std::move(effective_repeats);
227- descriptor.input_strides = shape_info.gm_strides;300+ descriptor.input_strides = std::move(effective_gm_strides);
228- descriptor.output_strides = shape_info.strides;301+ descriptor.output_strides = std::move(effective_ub_strides);
229- if (vectorized_axis.empty()) {302+ descriptor.vectorized_axis = std::move(effective_axes);
230- return NddmaFallbackReason::kCodegenMismatch;303+ GELOGD(
231- }304+ "[ATT NDDMA] effective view: raw_rank=%zu, effective_rank=%zu, repeats=[%s], gm_strides=[%s], ub_strides=[%s], "
232- descriptor.vectorized_axis = vectorized_axis;305+ "axes=[%s]",
306+ shape_info.repeats.size(), descriptor.output_dims.size(), GetVecString(descriptor.output_dims).c_str(),
307+ GetVecString(descriptor.input_strides).c_str(), GetVecString(descriptor.output_strides).c_str(),
308+ ascgen_utils::VectorToStr(descriptor.vectorized_axis).c_str());
233 return NddmaFallbackReason::kNone;309 return NddmaFallbackReason::kNone;
234}310}
235 311 
236NddmaFallbackReason NormalizeNddmaDescriptor(const NddmaDescriptorInfo &descriptor, NddmaNormalizedDesc &normalized) {312NddmaFallbackReason NormalizeNddmaDescriptor(const NddmaDescriptorInfo &descriptor, NddmaNormalizedDesc &normalized) {
237 normalized = NddmaNormalizedDesc{};313 normalized = NddmaNormalizedDesc{};
238- normalized.raw_rank = descriptor.output_dims.size();314+ normalized.effective_rank = descriptor.output_dims.size();
239- normalized.effective_rank = normalized.raw_rank;315+ if (normalized.effective_rank < kMinNddmaRank || normalized.effective_rank > kMaxNddmaRank) {
240- if (normalized.raw_rank < kMinNddmaRank || normalized.raw_rank > kMaxNddmaRank) {
241 return NddmaFallbackReason::kRankUnsupported;316 return NddmaFallbackReason::kRankUnsupported;
242 }317 }
243 if (!HasValidVectorLengths(descriptor)) {318 if (!HasValidVectorLengths(descriptor)) {
@@ -246,10 +321,10 @@ NddmaFallbackReason NormalizeNddmaDescriptor(const NddmaDescriptorInfo &descript
246 if (!HasValidAxisOrder(descriptor)) {321 if (!HasValidAxisOrder(descriptor)) {
247 return NddmaFallbackReason::kCodegenMismatch;322 return NddmaFallbackReason::kCodegenMismatch;
248 }323 }
249- normalized.output_dims = descriptor.output_dims;324+ normalized.output_dims.assign(descriptor.output_dims.rbegin(), descriptor.output_dims.rend());
250- normalized.input_strides = descriptor.input_strides;325+ normalized.input_strides.assign(descriptor.input_strides.rbegin(), descriptor.input_strides.rend());
251- normalized.output_strides = descriptor.output_strides;326+ normalized.output_strides.assign(descriptor.output_strides.rbegin(), descriptor.output_strides.rend());
252- normalized.vectorized_axis = descriptor.vectorized_axis;327+ normalized.vectorized_axis.assign(descriptor.vectorized_axis.rbegin(), descriptor.vectorized_axis.rend());
253 return HasInvalidStaticValue(normalized) ? NddmaFallbackReason::kStrideInvalid : NddmaFallbackReason::kNone;328 return HasInvalidStaticValue(normalized) ? NddmaFallbackReason::kStrideInvalid : NddmaFallbackReason::kNone;
254}329}
255 330 
@@ -258,12 +333,11 @@ af::Status EvaluateNddmaModel(const NddmaDescriptorInfo &descriptor, const std::
258 result = NddmaModelResult{};333 result = NddmaModelResult{};
259 NddmaNormalizedDesc normalized;334 NddmaNormalizedDesc normalized;
260 result.fallback_reason = NormalizeNddmaDescriptor(descriptor, normalized);335 result.fallback_reason = NormalizeNddmaDescriptor(descriptor, normalized);
261- result.raw_rank = normalized.raw_rank;
262 result.effective_rank = normalized.effective_rank;336 result.effective_rank = normalized.effective_rank;
263 if (result.fallback_reason != NddmaFallbackReason::kNone) {337 if (result.fallback_reason != NddmaFallbackReason::kNone) {
264 return af::SUCCESS;338 return af::SUCCESS;
265 }339 }
266- if (normalized.raw_rank != 1U || normalized.effective_rank != 1U) {340+ if (normalized.effective_rank < kMinNddmaRank || normalized.effective_rank > kMaxNddmaRank) {
267 result.fallback_reason = NddmaFallbackReason::kNoRegisteredModel;341 result.fallback_reason = NddmaFallbackReason::kNoRegisteredModel;
268 return af::SUCCESS;342 return af::SUCCESS;
269 }343 }
@@ -278,15 +352,23 @@ af::Status EvaluateNddmaModel(const NddmaDescriptorInfo &descriptor, const std::
278 params == nullptr ? NddmaFallbackReason::kDtypeUnsupported : NddmaFallbackReason::kSchemaMismatch;352 params == nullptr ? NddmaFallbackReason::kDtypeUnsupported : NddmaFallbackReason::kSchemaMismatch;
279 return af::SUCCESS;353 return af::SUCCESS;
280 }354 }
281- if (HasStaticByteCountOverflow(normalized.output_dims[0], dtype_size)) {355+ Expr low_core;
356+ Expr high_core;
357+ result.fallback_reason = BuildNddmaCoreCycles(normalized, *params, dtype_size, low_core, high_core);
358+ if (result.fallback_reason != NddmaFallbackReason::kNone) {
359+ return af::SUCCESS;
360+ }
361+ GE_ASSERT_SUCCESS(SelectCoreBranch(block_dim, low_core, high_core, result));
362+ // Fitted residual terms can become non-positive outside their measured
363+ // domain (for example, tiny B with a large effective stride). Do not emit
364+ // an invalid performance expression; use the existing legacy path instead.
365+ if (IsStaticNonPositive(result.cycles)) {
366+ result.selected = false;
282 result.fallback_reason = NddmaFallbackReason::kSchemaMismatch;367 result.fallback_reason = NddmaFallbackReason::kSchemaMismatch;
283 return af::SUCCESS;368 return af::SUCCESS;
284 }369 }
285- Expr low_core;
286- Expr high_core;
287- BuildNddma1DBranches(normalized, dtype_size, *params, low_core, high_core);
288- GE_ASSERT_SUCCESS(SelectCoreBranch(block_dim, low_core, high_core, result));
289 result.selected = true;370 result.selected = true;
371+ result.model_name = normalized.effective_rank == 1U ? "NDDMA_1D_MULTICORE_V2" : "NDDMA_ND_MULTICORE_V1";
290 result.fallback_reason = NddmaFallbackReason::kNone;372 result.fallback_reason = NddmaFallbackReason::kNone;
291 return af::SUCCESS;373 return af::SUCCESS;
292}374}
@@ -18,8 +18,7 @@ namespace att {
18 * @brief NDDMA 精确性能模型的组成和计算原理。18 * @brief NDDMA 精确性能模型的组成和计算原理。
19 *19 *
20 * 当前范围:20 * 当前范围:
21- * - 注册 NDDMA_1D_MULTICORE_V1,覆盖默认 Codegen 路径下 raw rank=1effective rank=1 的搬运21+ * - 注册 NDDMA_1D_MULTICORE_V2(effective rank=1)和 NDDMA_ND_MULTICORE_V1(effective rank=2~5)
22- * - raw rank=25 暂无正式模型,保留完整 descriptor 后回退 legacy NDDMA 模型;不会因连续轴合并而伪装成 1D。
23 * - kUBFuse Codegen 使用 {curAivM, curAlignN} 和固定 2D stride,与 raw descriptor 不等价,因此通过22 * - kUBFuse Codegen 使用 {curAivM, curAlignN} 和固定 2D stride,与 raw descriptor 不等价,因此通过
24 * NodeInfo::is_cv_ub_fusion 门禁回退 legacy 模型。23 * NodeInfo::is_cv_ub_fusion 门禁回退 legacy 模型。
25 *24 *
@@ -30,44 +29,50 @@ namespace att {
30 * - AscTensorAttr::vectorized_axis -> descriptor 轴序;29 * - AscTensorAttr::vectorized_axis -> descriptor 轴序;
31 * - dtype -> dtype_size,并选择 B8/B16/B32/B64 参数;30 * - dtype -> dtype_size,并选择 B8/B16/B32/B64 参数;
32 * - block_dim -> 参与搬运的核数。31 * - block_dim -> 参与搬运的核数。
33- * 三组向量均为 legacy 连续轴并前原始值,顺序与 vectorized_axis 一致,stride 单位均为元素。32+ * 三组向量表示 codegen轴后 effective view,顺序与 vectorized_axis 一致,stride 单位均为元素。
34 * 默认 Codegen 使用同源的 DataCopyParams,经 SetNddmaParams 左侧补 1 后生成 5 元素 API 数组;补 1 不改变33 * 默认 Codegen 使用同源的 DataCopyParams,经 SetNddmaParams 左侧补 1 后生成 5 元素 API 数组;补 1 不改变
35 * effective rank 和搬运语义。34 * effective rank 和搬运语义。
36 *35 *
37- * 1D 特征36+ * 统一公式(effective rank D=1~5)
38- * B = output_dims[0] * dtype_size // 每核每次搬运字节数37+ * 设归一化后的维度和 stride 按内轴到外轴排列为
39- * is = input_strides[0], s = min(is, 128) // GM 非连续程度is 允许 038+ * d_j、is_j、os_j(j=0...D-1)dtype_size每个元素的字节数。
40- * os = output_strides[0]39+ * B = dtype_size * Π(d_j) // 单次搬运字节数
41- * g = max(0, min(1, os - 1)) // os=1 连续,os>=2 时修正饱和40+ * P_is_0 = 0,P_os_0 = 0
41+ * P_is_j = Σ(d_m * is_m),P_os_j = Σ(d_m * os_m),m=0...j-1
42+ * hat_is_j = 1 (j=0)
43+ * |is_j - P_is_j| + 1 (j>0)
44+ * hat_os_j = 1 (j=0)
45+ * |os_j - P_os_j| + 1 (j>0)
46+ * B_j = dtype_size * Π(d_m),m=j...D-1 // 第 j 层剩余数据量
47+ * s_j = min(hat_is_j * dtype_size, 128)
48+ * g_j = min(1, hat_os_j - 1)
49+ * NG_j = (a1 + a2 * B_j) * s_j
50+ * NGU_j = (b1 + b2 * s_j + (b3 + b4 * s_j) * B_j) * g_j
51+ * rho_j = c1 + c2 * s_j + g_j * (c3 + c4 * s_j)
52+ * N_j = NG_j + NGU_j (block_dim<=2)
53+ * (NG_j + NGU_j) * rho_j (block_dim>2)
54+ * N_base = B/T1 + H1 (block_dim<=2)
55+ * B/T2 + H2 (block_dim>2)
56+ * cycles = N_base + Σ(N_j),j=0...D-1
42 *57 *
43- * 参数组成(每种 dtype_size 各有一组):58+ * D=1 时上述求和只有一个层级,即为 1D 模型;D=2~5 时逐层累加同一
44- * - low_os_one / low_os_ge_two:k<=2 且 os=1/os>=2 时的 L0LB、Ls、LBs;59+ * 参数,不复制或展开另一套多维系数。参数表保存 T1/H1/T2/H2a1...c4。
45- * - high_os_one / high_os_ge_two:k>2 且 os=1/os>=2 时的 C0、C1、C2、E0、E1、E2。
46- * 合并后的参数是原始 t/h/a/b/c 参数在合法 os 分支上的代数展开结果,参数表直接对应最终 cycles。
47- *
48- * 1D 公式:
49- * low(os=1) = L0 + LB*B + Ls*s + LBs*B*s
50- * high(os=1) = C0 + C1*s + C2*s² + B*(E0 + E1*s + E2*s²)
51- * low(os>=2) = L0 + LB*B + Ls*s + LBs*B*s
52- * high(os>=2) = C0 + C1*s + C2*s² + B*(E0 + E1*s + E2*s²)
53- * cycles = low(block_dim <= 2) or high(block_dim > 2)
54- * 合法 stride 为整数,因此动态 os 使用 g=max(0,min(1,os-1)) 在两个 os 多项式之间选择;g=0 对应 os=1
55- * g=1 对应 os>=2。对于非法分数 stride,不承诺与原始连续 g 公式等价。
56 *60 *
57 * 静态和动态 shape:61 * 静态和动态 shape:
58 * - 两者使用同一组参数和同一公式;静态表达式直接折叠,动态 block_dim 生成一个 TernaryOp;62 * - 两者使用同一组参数和同一公式;静态表达式直接折叠,动态 block_dim 生成一个 TernaryOp;
59- * - 动态 is/os 保留符号 Min/Maxg 的非负保护避免动态 os 产生负修正;63+ * - 动态 is/os 保留符号 Min,输出 stride 修正项按统一公式保留符号表达式
60 * - 静态非正 dim、负 input_stride、非正 output_stride、非法 dtype/schema 均记录原因并回退 legacy。64 * - 静态非正 dim、负 input_stride、非正 output_stride、非法 dtype/schema 均记录原因并回退 legacy。
61 *65 *
62 * 处理阶段:66 * 处理阶段:
63- * 1. BuildNddmaDescriptor:从 TensorShapeInfo 构造原始 descriptor,不从 legacy 标量 stride 反推;67+ * 1. BuildNddmaDescriptor:从 TensorShapeInfo 构造并合并 effective descriptor,不从 legacy 标量 stride 反推;
64- * 2. NormalizeNddmaDescriptor:校验 rank、向量长度、轴序静态值及默认 Codegen 一致性68+ * 2. NormalizeNddmaDescriptor:校验 rank、向量长度、轴序静态值;
65 * 3. EvaluateNddmaModel:选择 dtype 参数、构造静态/动态 cycles,并返回 ternary_ops;69 * 3. EvaluateNddmaModel:选择 dtype 参数、构造静态/动态 cycles,并返回 ternary_ops;
66- * 4. LogNddmaFallback:未选择新模型时记录一次稳定 reason,调用方继续执行原有 GetDmaPerf。70+ * 4. EvaluateNddmaModel 只返回 fallback reason;TryNewNddmaModel 在 CV-Fusion、dtype/schema、descriptor 构造和
71+ * 模型归一化/求值失败路径统一调用 LogNddmaFallback,记录 node、raw/effective rank、candidate model 和 reason,
72+ * 然后继续执行原有 GetDmaPerf。
67 * 模型只输出单次调用的 AIV_MTE2 cycles;全局 pipe head 仍由 PipePerfExpr 统一添加。73 * 模型只输出单次调用的 AIV_MTE2 cycles;全局 pipe head 仍由 PipePerfExpr 统一添加。
68 *74 *
69- * 扩展约束:原始 NddmaDescriptorInfo 与物理 NddmaNormalizedDesc 分离后续 2D~5D 可在归一化阶段构造75+ * raw rank 仅用于诊断日志;模型选择和公式计算只使用 effective rank
70- * effective view,并独立选择 raw 或 normalized 统计特征,不改变现有 descriptor 字段及 legacy 数据结构。
71 */76 */
72enum class NddmaFallbackReason : int32_t {77enum class NddmaFallbackReason : int32_t {
73 kNone = 0,78 kNone = 0,
@@ -87,7 +92,6 @@ struct NddmaNormalizedDesc {
87 std::vector<Expr> input_strides;92 std::vector<Expr> input_strides;
88 std::vector<Expr> output_strides;93 std::vector<Expr> output_strides;
89 std::vector<int64_t> vectorized_axis;94 std::vector<int64_t> vectorized_axis;
90- size_t raw_rank{0U};
91 size_t effective_rank{0U};95 size_t effective_rank{0U};
92};96};
93 97 
@@ -95,7 +99,7 @@ struct NddmaNormalizedDesc {
95struct NddmaModelResult {99struct NddmaModelResult {
96 bool selected{false};100 bool selected{false};
97 Expr cycles;101 Expr cycles;
98- std::string model_name{"NDDMA_1D_MULTICORE_V1"};102+ std::string model_name{"NDDMA_1D_MULTICORE_V2"};
99 NddmaFallbackReason fallback_reason{NddmaFallbackReason::kNone};103 NddmaFallbackReason fallback_reason{NddmaFallbackReason::kNone};
100 size_t raw_rank{0U};104 size_t raw_rank{0U};
101 size_t effective_rank{0U};105 size_t effective_rank{0U};
@@ -105,7 +109,7 @@ struct NddmaModelResult {
105const char *NddmaFallbackReasonToString(NddmaFallbackReason reason);109const char *NddmaFallbackReasonToString(NddmaFallbackReason reason);
106 110 
107NddmaFallbackReason BuildNddmaDescriptor(const TensorShapeInfo &shape_info, const std::vector<int64_t> &vectorized_axis,111NddmaFallbackReason BuildNddmaDescriptor(const TensorShapeInfo &shape_info, const std::vector<int64_t> &vectorized_axis,
108- NddmaDescriptorInfo &descriptor);112+ NddmaDescriptorInfo &descriptor, const std::vector<bool> &tile_inner = {});
109 113 
110NddmaFallbackReason NormalizeNddmaDescriptor(const NddmaDescriptorInfo &descriptor, NddmaNormalizedDesc &normalized);114NddmaFallbackReason NormalizeNddmaDescriptor(const NddmaDescriptorInfo &descriptor, NddmaNormalizedDesc &normalized);
111 115