已合并
fix: 保留后端不支持的浮点 Cast 路径(#247) #1762
fix: 保留后端不支持的浮点 Cast 路径(#247) #1762
已合并
zqmin创建于 20 天前
4 个文件变更+355-64
@@ -11,6 +11,7 @@
11#include "pre_process/improve_precision.h"11#include "pre_process/improve_precision.h"
12 12 
13#include <atomic>13#include <atomic>
14+#include <algorithm>
14#include <string>15#include <string>
15#include <unordered_map>16#include <unordered_map>
16#include <unordered_set>17#include <unordered_set>
@@ -130,6 +131,13 @@ Status GetOutputTensorDesc(const NodePtr &node, GeTensorDescPtr &output_tensor_d
130 return af::SUCCESS;131 return af::SUCCESS;
131}132}
132 133 
134+Status GetNodeOutputDtype(const NodePtr &node, DataType &dtype) {
135+ GeTensorDescPtr output_tensor_desc;
136+ GE_ASSERT_SUCCESS(GetOutputTensorDesc(node, output_tensor_desc));
137+ dtype = output_tensor_desc->GetDataType();
138+ return af::SUCCESS;
139+}
140+ 
133Status DelNode(AscGraph &asc_graph, const NodePtr &node) {141Status DelNode(AscGraph &asc_graph, const NodePtr &node) {
134 const auto in_data_anchor = node->GetInDataAnchor(0);142 const auto in_data_anchor = node->GetInDataAnchor(0);
135 GE_ASSERT_NOTNULL(in_data_anchor);143 GE_ASSERT_NOTNULL(in_data_anchor);
@@ -256,8 +264,145 @@ const std::unordered_map<std::string, std::string> kTypeToGroup = {
256 {af::ascir_op::Scalar::Type, af::ascir_op::Scalar::Type},264 {af::ascir_op::Scalar::Type, af::ascir_op::Scalar::Type},
257 {af::ascir_op::Store::Type, af::ascir_op::Store::Type}};265 {af::ascir_op::Store::Type, af::ascir_op::Store::Type}};
258 266 
259-bool ShouldDeleteCastNode(DataType peer_output_dtype, DataType output_dtype) {267+struct CastChain {
260- return IsFloatDataType(output_dtype) && IsFloatDataType(peer_output_dtype);268+ std::vector<NodePtr> nodes;
269+ std::vector<DataType> output_dtypes;
270+};
271+ 
272+bool CanCollapseCast(DataType input_dtype, DataType output_dtype) {
273+ return input_dtype == output_dtype ||
274+ (IsFloatDataType(input_dtype) && IsFloatDataType(output_dtype) && CheckCastDtype(input_dtype, output_dtype));
275+}
276+ 
277+struct CastChainPlan {
278+ std::vector<size_t> costs;
279+ std::vector<size_t> previous;
280+ std::vector<bool> requires_cast;
281+};
282+ 
283+Status CollectLinearCastChain(const NodePtr &start, CastChain &chain) {
284+ chain.nodes.clear();
285+ chain.output_dtypes.clear();
286+ NodePtr current = start;
287+ while (current->GetType() == af::ascir_op::Cast::Type) {
288+ std::vector<NodePtr> consumers;
289+ GE_ASSERT_SUCCESS(GetPeerInNodes(current, consumers, 0));
290+ if (consumers.size() != 1U) {
291+ chain.nodes.clear();
292+ chain.output_dtypes.clear();
293+ return af::SUCCESS;
294+ }
295+ const auto asc_node = std::dynamic_pointer_cast<af::AscNode>(current);
296+ GE_ASSERT_NOTNULL(asc_node);
297+ chain.nodes.push_back(current);
298+ chain.output_dtypes.push_back(asc_node->outputs[0].attr.dtype);
299+ current = consumers[0];
300+ }
301+ return af::SUCCESS;
302+}
303+ 
304+void UpdateMinimumCastPath(const std::vector<DataType> &output_dtypes, size_t target, CastChainPlan &plan) {
305+ const auto unreachable = output_dtypes.size() + 1U;
306+ for (size_t source = 0U; source < target; ++source) {
307+ if (plan.costs[source] == unreachable || !CanCollapseCast(output_dtypes[source], output_dtypes[target])) {
308+ continue;
309+ }
310+ const auto transition_cost = output_dtypes[source] == output_dtypes[target] ? 0U : 1U;
311+ if (plan.costs[source] + transition_cost < plan.costs[target]) {
312+ plan.costs[target] = plan.costs[source] + transition_cost;
313+ plan.previous[target] = source;
314+ plan.requires_cast[target] = transition_cost == 1U;
315+ }
316+ }
317+}
318+ 
319+CastChainPlan BuildMinimumCastPlan(DataType source_dtype, const std::vector<DataType> &output_dtypes) {
320+ const auto chain_size = output_dtypes.size();
321+ CastChainPlan plan{std::vector<size_t>(chain_size, chain_size + 1U), std::vector<size_t>(chain_size, chain_size),
322+ std::vector<bool>(chain_size, false)};
323+ for (size_t target = 0U; target < chain_size; ++target) {
324+ if (CanCollapseCast(source_dtype, output_dtypes[target])) {
325+ plan.requires_cast[target] = source_dtype != output_dtypes[target];
326+ plan.costs[target] = plan.requires_cast[target] ? 1U : 0U;
327+ }
328+ UpdateMinimumCastPath(output_dtypes, target, plan);
329+ }
330+ return plan;
331+}
332+ 
333+std::vector<bool> MarkRetainedCastNodes(const CastChainPlan &plan) {
334+ const auto chain_size = plan.previous.size();
335+ std::vector<bool> keep(chain_size, false);
336+ for (size_t index = chain_size - 1U; index < chain_size; index = plan.previous[index]) {
337+ keep[index] = plan.requires_cast[index];
338+ if (plan.previous[index] >= chain_size) {
339+ break;
340+ }
341+ }
342+ return keep;
343+}
344+ 
345+Status DeleteUnretainedCastNodes(AscGraph &asc_graph, const CastChain &chain, const std::vector<bool> &keep,
346+ std::vector<NodePtr> &retained_nodes) {
347+ retained_nodes.clear();
348+ for (size_t i = 0U; i < chain.nodes.size(); ++i) {
349+ if (keep[i]) {
350+ retained_nodes.push_back(chain.nodes[i]);
351+ }
352+ }
353+ for (auto i = chain.nodes.size(); i > 0U; --i) {
354+ if (!keep[i - 1U]) {
355+ GE_ASSERT_SUCCESS(DelNode(asc_graph, chain.nodes[i - 1U]));
356+ }
357+ }
358+ return af::SUCCESS;
359+}
360+ 
361+Status OptimizeLinearCastChain(AscGraph &asc_graph, const NodePtr &source, const CastChain &chain,
362+ std::vector<NodePtr> &retained_nodes) {
363+ retained_nodes.clear();
364+ GE_ASSERT_TRUE(chain.nodes.size() > 1U);
365+ DataType source_dtype;
366+ GE_ASSERT_SUCCESS(GetNodeOutputDtype(source, source_dtype));
367+ if (source_dtype == chain.output_dtypes.back()) {
368+ for (auto i = chain.nodes.size(); i > 0U; --i) {
369+ GE_ASSERT_SUCCESS(DelNode(asc_graph, chain.nodes[i - 1U]));
370+ }
371+ return af::SUCCESS;
372+ }
373+ const auto plan = BuildMinimumCastPlan(source_dtype, chain.output_dtypes);
374+ if (plan.costs.back() == chain.nodes.size() + 1U) {
375+ retained_nodes = chain.nodes;
376+ return af::SUCCESS;
377+ }
378+ return DeleteUnretainedCastNodes(asc_graph, chain, MarkRetainedCastNodes(plan), retained_nodes);
379+}
380+ 
381+Status CanOptimizeLinearCastChain(const NodePtr &source, const CastChain &chain, bool &can_optimize) {
382+ can_optimize = false;
383+ if (chain.nodes.size() <= 1U) {
384+ return af::SUCCESS;
385+ }
386+ DataType source_dtype;
387+ GE_ASSERT_SUCCESS(GetNodeOutputDtype(source, source_dtype));
388+ if (!IsFloatDataType(source_dtype)) {
389+ return af::SUCCESS;
390+ }
391+ can_optimize = std::all_of(chain.output_dtypes.begin(), chain.output_dtypes.end(),
392+ [](const auto dtype) { return IsFloatDataType(dtype); });
393+ return af::SUCCESS;
394+}
395+ 
396+Status DeleteIdentityCast(AscGraph &asc_graph, const NodePtr &source, const NodePtr &cast_node, bool &deleted) {
397+ DataType source_dtype;
398+ GE_ASSERT_SUCCESS(GetNodeOutputDtype(source, source_dtype));
399+ DataType cast_dtype;
400+ GE_ASSERT_SUCCESS(GetNodeOutputDtype(cast_node, cast_dtype));
401+ deleted = source_dtype == cast_dtype;
402+ if (deleted) {
403+ GE_ASSERT_SUCCESS(DelNode(asc_graph, cast_node));
404+ }
405+ return af::SUCCESS;
261}406}
262 407 
263bool ShouldChangeDataType(const NodePtr &node, const std::vector<NodePtr> &peer_in_nodes, DataType peer_output_dtype,408bool ShouldChangeDataType(const NodePtr &node, const std::vector<NodePtr> &peer_in_nodes, DataType peer_output_dtype,
@@ -344,7 +489,7 @@ Status ConfigureCastTensor(const NodePtr &src_node, const NodePtr &cast_node, co
344 GE_ASSERT_NOTNULL(c_o_attr);489 GE_ASSERT_NOTNULL(c_o_attr);
345 // 当上游节点为 Scalar 时,其输出为标量形状,axis/repeats/strides 不包含目标张量的完整形状信息。490 // 当上游节点为 Scalar 时,其输出为标量形状,axis/repeats/strides 不包含目标张量的完整形状信息。
346 // 前因:前端可能传入冗余连续 Cast(如 scalar->cast(FP32->FP32)->cast(FP32->FP16)->store),491 // 前因:前端可能传入冗余连续 Cast(如 scalar->cast(FP32->FP32)->cast(FP32->FP16)->store),
347- // ImprovePrecision 的 ShouldDeleteCastNode 会删除所有浮点 Cast,再在 Store 前重新插入新 Cast。492+ // ImprovePrecision 会删除冗余浮点 Cast,再在 Store 前重新插入新 Cast。
348 // 若从 Scalar 复制输出信息,新 Cast 的 axis/repeats/strides 为空,493 // 若从 Scalar 复制输出信息,新 Cast 的 axis/repeats/strides 为空,
349 // 导致后续 InsertBroadcast 补充的 broadcast 节点输出信息也丢失。494 // 导致后续 InsertBroadcast 补充的 broadcast 节点输出信息也丢失。
350 // 因此当上游为 Scalar 时,从下游节点取 tensor 信息。495 // 因此当上游为 Scalar 时,从下游节点取 tensor 信息。
@@ -363,24 +508,17 @@ Status ConfigureCastTensor(const NodePtr &src_node, const NodePtr &cast_node, co
363 return af::SUCCESS;508 return af::SUCCESS;
364}509}
365 510 
366-// ====================== Per-type processing ======================511+Status ProcessCastNodePrecision(AscGraph &asc_graph, const NodePtr &node) {
367-Status CastNodeProc(AscGraph &asc_graph, const NodePtr &node) {
368 NodePtr peer_out_node;512 NodePtr peer_out_node;
369 GE_ASSERT_SUCCESS(GetPeerOutNode(node, peer_out_node, 0));513 GE_ASSERT_SUCCESS(GetPeerOutNode(node, peer_out_node, 0));
370 std::vector<NodePtr> peer_in_nodes;514 std::vector<NodePtr> peer_in_nodes;
371 GE_ASSERT_SUCCESS(GetPeerInNodes(node, peer_in_nodes, 0));515 GE_ASSERT_SUCCESS(GetPeerInNodes(node, peer_in_nodes, 0));
372 516 
373- GeTensorDescPtr peer_output_tensor_desc;
374- GE_ASSERT_SUCCESS(GetOutputTensorDesc(peer_out_node, peer_output_tensor_desc));
375 GeTensorDescPtr output_tensor_desc;517 GeTensorDescPtr output_tensor_desc;
376 GE_ASSERT_SUCCESS(GetOutputTensorDesc(node, output_tensor_desc));518 GE_ASSERT_SUCCESS(GetOutputTensorDesc(node, output_tensor_desc));
377- const auto peer_output_dtype = peer_output_tensor_desc->GetDataType();519+ DataType peer_output_dtype;
520+ GE_ASSERT_SUCCESS(GetNodeOutputDtype(peer_out_node, peer_output_dtype));
378 const auto output_dtype = output_tensor_desc->GetDataType();521 const auto output_dtype = output_tensor_desc->GetDataType();
379- if (ShouldDeleteCastNode(peer_output_dtype, output_dtype)) {
380- GE_ASSERT_SUCCESS(DelNode(asc_graph, node));
381- return af::SUCCESS;
382- }
383- 
384 if (IsFloatToUltraLowNeedInsertCast(peer_out_node, peer_output_dtype, output_dtype)) {522 if (IsFloatToUltraLowNeedInsertCast(peer_out_node, peer_output_dtype, output_dtype)) {
385 GE_ASSERT_SUCCESS(UpdateTopoId(asc_graph, node, 1));523 GE_ASSERT_SUCCESS(UpdateTopoId(asc_graph, node, 1));
386 NodePtr c_node = nullptr;524 NodePtr c_node = nullptr;
@@ -400,6 +538,41 @@ Status CastNodeProc(AscGraph &asc_graph, const NodePtr &node) {
400 return af::SUCCESS;538 return af::SUCCESS;
401}539}
402 540 
541+Status ProcessCastChains(AscGraph &asc_graph, const std::vector<NodePtr> &nodes) {
542+ std::unordered_set<NodePtr> processed;
543+ std::vector<NodePtr> retained_nodes;
544+ for (const auto &node : nodes) {
545+ if (processed.find(node) != processed.end()) {
546+ continue;
547+ }
548+ NodePtr source;
549+ GE_ASSERT_SUCCESS(GetPeerOutNode(node, source, 0));
550+ CastChain chain;
551+ GE_ASSERT_SUCCESS(CollectLinearCastChain(node, chain));
552+ bool can_optimize = false;
553+ GE_ASSERT_SUCCESS(CanOptimizeLinearCastChain(source, chain, can_optimize));
554+ if (!can_optimize) {
555+ processed.insert(node);
556+ bool deleted = false;
557+ GE_ASSERT_SUCCESS(DeleteIdentityCast(asc_graph, source, node, deleted));
558+ if (!deleted) {
559+ retained_nodes.push_back(node);
560+ }
561+ continue;
562+ }
563+ std::vector<NodePtr> chain_retained_nodes;
564+ GE_ASSERT_SUCCESS(OptimizeLinearCastChain(asc_graph, source, chain, chain_retained_nodes));
565+ retained_nodes.insert(retained_nodes.end(), chain_retained_nodes.begin(), chain_retained_nodes.end());
566+ for (const auto &cast_node : chain.nodes) {
567+ processed.insert(cast_node);
568+ }
569+ }
570+ for (const auto &node : retained_nodes) {
571+ GE_ASSERT_SUCCESS(ProcessCastNodePrecision(asc_graph, node));
572+ }
573+ return af::SUCCESS;
574+}
575+ 
403Status IsNeedInsertCastAfterLoad(const NodePtr &node, bool &is_need_insert_cast) {576Status IsNeedInsertCastAfterLoad(const NodePtr &node, bool &is_need_insert_cast) {
404 const auto node_opdesc = node->GetOpDesc();577 const auto node_opdesc = node->GetOpDesc();
405 GE_ASSERT_NOTNULL(node_opdesc);578 GE_ASSERT_NOTNULL(node_opdesc);
@@ -440,13 +613,13 @@ Status InsertCastToIncreasePrecision(AscGraph &asc_graph, const NodePtr &load_no
440Status IsNeedInsertCastBeforeOther(const NodePtr &other_node, bool &need_insert, std::vector<int32_t> &input_idxs) {613Status IsNeedInsertCastBeforeOther(const NodePtr &other_node, bool &need_insert, std::vector<int32_t> &input_idxs) {
441 std::vector<NodePtr> peer_out_nodes;614 std::vector<NodePtr> peer_out_nodes;
442 GE_ASSERT_SUCCESS(GetPeerOutNodes(other_node, peer_out_nodes));615 GE_ASSERT_SUCCESS(GetPeerOutNodes(other_node, peer_out_nodes));
443- GeTensorDescPtr peer_output_tensor_desc;
444 for (auto idx = 0U; idx < peer_out_nodes.size(); idx++) {616 for (auto idx = 0U; idx < peer_out_nodes.size(); idx++) {
445 const auto &peer_out_node = peer_out_nodes[idx];617 const auto &peer_out_node = peer_out_nodes[idx];
446- GE_ASSERT_SUCCESS(GetOutputTensorDesc(peer_out_node, peer_output_tensor_desc));618+ DataType peer_dtype;
619+ GE_ASSERT_SUCCESS(GetNodeOutputDtype(peer_out_node, peer_dtype));
447 const auto &type = peer_out_node->GetType();620 const auto &type = peer_out_node->GetType();
448 if (type == af::ascir_op::Cast::Type || type == af::ascir_op::Load::Type || type == af::ascir_op::Gather::Type) {621 if (type == af::ascir_op::Cast::Type || type == af::ascir_op::Load::Type || type == af::ascir_op::Gather::Type) {
449- if (IsLowPrecisionDataType(peer_output_tensor_desc->GetDataType())) {622+ if (IsLowPrecisionDataType(peer_dtype)) {
450 need_insert = true;623 need_insert = true;
451 input_idxs.push_back(static_cast<int32_t>(idx));624 input_idxs.push_back(static_cast<int32_t>(idx));
452 }625 }
@@ -475,12 +648,12 @@ Status InsertCastBeforeNode(AscGraph &asc_graph, const NodePtr &other_node, bool
475Status IsNeedInsertCastBeforeStore(const NodePtr &store_node, bool &need_insert, bool &is_increase_precision) {648Status IsNeedInsertCastBeforeStore(const NodePtr &store_node, bool &need_insert, bool &is_increase_precision) {
476 NodePtr peer_out_node;649 NodePtr peer_out_node;
477 GE_ASSERT_SUCCESS(GetPeerOutNode(store_node, peer_out_node, 0));650 GE_ASSERT_SUCCESS(GetPeerOutNode(store_node, peer_out_node, 0));
478- GeTensorDescPtr peer_output_tensor_desc;651+ DataType peer_dtype;
479- GE_ASSERT_SUCCESS(GetOutputTensorDesc(peer_out_node, peer_output_tensor_desc));652+ GE_ASSERT_SUCCESS(GetNodeOutputDtype(peer_out_node, peer_dtype));
480- GeTensorDescPtr store_output_tensor_desc;653+ DataType store_dtype;
481- GE_ASSERT_SUCCESS(GetOutputTensorDesc(store_node, store_output_tensor_desc));654+ GE_ASSERT_SUCCESS(GetNodeOutputDtype(store_node, store_dtype));
482- is_increase_precision = IsHighPrecisionDataType(store_output_tensor_desc->GetDataType());655+ is_increase_precision = IsHighPrecisionDataType(store_dtype);
483- if (peer_output_tensor_desc->GetDataType() == store_output_tensor_desc->GetDataType()) {656+ if (peer_dtype == store_dtype) {
484 need_insert = false;657 need_insert = false;
485 return af::SUCCESS;658 return af::SUCCESS;
486 }659 }
@@ -569,9 +742,7 @@ Status ProcessStoreNodes(AscGraph &asc_graph, const std::vector<NodePtr> &nodes)
569}742}
570 743 
571Status ProcessNodeGroups(AscGraph &asc_graph, TypeToNodesMap &type_to_nodes) {744Status ProcessNodeGroups(AscGraph &asc_graph, TypeToNodesMap &type_to_nodes) {
572- for (const auto &node : type_to_nodes[af::ascir_op::Cast::Type]) {745+ GE_ASSERT_SUCCESS(ProcessCastChains(asc_graph, type_to_nodes[af::ascir_op::Cast::Type]));
573- GE_ASSERT_SUCCESS(CastNodeProc(asc_graph, node));
574- }
575 GE_ASSERT_SUCCESS(ProcessLoadGatherNodes(asc_graph, type_to_nodes[af::ascir_op::Load::Type]));746 GE_ASSERT_SUCCESS(ProcessLoadGatherNodes(asc_graph, type_to_nodes[af::ascir_op::Load::Type]));
576 GE_ASSERT_SUCCESS(ProcessLoadGatherNodes(asc_graph, type_to_nodes[af::ascir_op::Gather::Type]));747 GE_ASSERT_SUCCESS(ProcessLoadGatherNodes(asc_graph, type_to_nodes[af::ascir_op::Gather::Type]));
577 for (const auto &node : type_to_nodes[af::ascir_op::Scalar::Type]) {748 for (const auto &node : type_to_nodes[af::ascir_op::Scalar::Type]) {
@@ -0,0 +1,56 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef AUTOFUSE_TESTS_FRAMEWORK_IMPROVE_PRECISION_TEST_UTILS_H
12+#define AUTOFUSE_TESTS_FRAMEWORK_IMPROVE_PRECISION_TEST_UTILS_H
13+ 
14+#include <string>
15+ 
16+#include "ascgraph_info_complete.h"
17+#include "graph/ascendc_ir/utils/asc_graph_utils.h"
18+#include "optimize/pre_process/improve_precision.h"
19+ 
20+namespace af::testing {
21+ 
22+inline size_t CountNodesByType(AscGraph &graph, const std::string &type) {
23+ size_t count = 0U;
24+ for (const auto &node : AscGraphUtils::GetComputeGraph(graph)->GetAllNodes()) {
25+ if (node->GetType() == type) {
26+ ++count;
27+ }
28+ }
29+ return count;
30+}
31+ 
32+inline bool HasCastOutputDtype(AscGraph &graph, ge::DataType expected_dtype) {
33+ for (const auto &node : AscGraphUtils::GetComputeGraph(graph)->GetAllNodes()) {
34+ if (node->GetType() == ascir_op::Cast::Type &&
35+ node->GetOpDesc()->GetOutputDesc(0).GetDataType() == expected_dtype) {
36+ return true;
37+ }
38+ }
39+ return false;
40+}
41+ 
42+inline bool CheckNodeOutputDtype(AscGraph &graph, const std::string &node_name, ge::DataType expected_dtype) {
43+ for (const auto &node : AscGraphUtils::GetComputeGraph(graph)->GetAllNodes()) {
44+ if (node->GetName() == node_name) {
45+ auto desc = node->GetOpDesc();
46+ if (desc != nullptr && desc->GetOutputDesc(0).GetDataType() == expected_dtype) {
47+ return true;
48+ }
49+ }
50+ }
51+ return false;
52+}
53+ 
54+} // namespace af::testing
55+ 
56+#endif // AUTOFUSE_TESTS_FRAMEWORK_IMPROVE_PRECISION_TEST_UTILS_H
@@ -20,8 +20,8 @@
20#undef private20#undef private
21#include "common/platform_context.h"21#include "common/platform_context.h"
22 22 
23-#include "ascgraph_info_complete.h"
24#include "tests/framework/easy_asc_graph/asc_graph_builder.h"23#include "tests/framework/easy_asc_graph/asc_graph_builder.h"
24+#include "tests/framework/improve_precision_test_utils.h"
25#include "runtime_stub.h"25#include "runtime_stub.h"
26 26 
27using namespace af;27using namespace af;
@@ -29,34 +29,15 @@ using namespace af::ascir_op;
29using af::ops::IsOps;29using af::ops::IsOps;
30using af::ops::One;30using af::ops::One;
31using af::testing::AscGraphBuilder;31using af::testing::AscGraphBuilder;
32+using af::testing::CheckNodeOutputDtype;
33+using af::testing::CountNodesByType;
34+using af::testing::HasCastOutputDtype;
32using af::testing::Sym;35using af::testing::Sym;
33using namespace af::pre_process;36using namespace af::pre_process;
34 37 
35namespace {38namespace {
36// ====================== Helpers ======================39// ====================== Helpers ======================
37 40 
38-size_t CountNodesByType(AscGraph &graph, const std::string &type) {
39- size_t count = 0U;
40- for (const auto &node : AscGraphUtils::GetComputeGraph(graph)->GetAllNodes()) {
41- if (node->GetType() == type) {
42- ++count;
43- }
44- }
45- return count;
46-}
47- 
48-bool CheckNodeOutputDtype(AscGraph &graph, const std::string &node_name, ge::DataType expected_dtype) {
49- for (const auto &node : AscGraphUtils::GetComputeGraph(graph)->GetAllNodes()) {
50- if (node->GetName() == node_name) {
51- auto desc = node->GetOpDesc();
52- if (desc != nullptr && desc->GetOutputDesc(0).GetDataType() == expected_dtype) {
53- return true;
54- }
55- }
56- }
57- return false;
58-}
59- 
60class TestImprovePrecisionST : public ::testing::Test {41class TestImprovePrecisionST : public ::testing::Test {
61 protected:42 protected:
62 void SetUp() override {43 void SetUp() override {
@@ -321,6 +302,64 @@ TEST_F(TestImprovePrecisionST, LoadWithExistingCastPeer_NoDuplicateCast) {
321 EXPECT_TRUE(CheckNodeOutputDtype(graph, "abs0", ge::DT_FLOAT));302 EXPECT_TRUE(CheckNodeOutputDtype(graph, "abs0", ge::DT_FLOAT));
322}303}
323 304 
305+TEST_F(TestImprovePrecisionST, UnsupportedCastBypass_PreservesIntermediateFloatCast) {
306+ auto graph = AscGraphBuilder("st_unsupported_cast_bypass")
307+ .Loops({Sym("s0")})
308+ .Data("data0", 0, ge::DT_BF16)
309+ .Load("load0", "data0")
310+ .Cast("cast_bf16_to_fp32", "load0", ge::DT_FLOAT)
311+ .Cast("cast_fp32_identity", "cast_bf16_to_fp32", ge::DT_FLOAT)
312+ .Cast("cast_fp32_identity2", "cast_fp32_identity", ge::DT_FLOAT)
313+ .Cast("cast_fp32_to_fp16", "cast_fp32_identity2", ge::DT_FLOAT16)
314+ .Store("store0", "cast_fp32_to_fp16")
315+ .Output("output0", "store0", 0, ge::DT_FLOAT16)
316+ .Build();
317+ 
318+ ASSERT_EQ(ImprovePrecisionForAscGraph(graph), af::SUCCESS);
319+ 
320+ EXPECT_EQ(CountNodesByType(graph, Cast::Type), 2U);
321+ EXPECT_TRUE(HasCastOutputDtype(graph, ge::DT_FLOAT));
322+ EXPECT_TRUE(HasCastOutputDtype(graph, ge::DT_FLOAT16));
323+}
324+ 
325+TEST_F(TestImprovePrecisionST, SupportedChainEndpoint_CollapsesAcrossUnsupportedIntermediateCast) {
326+ auto graph = AscGraphBuilder("st_supported_chain_endpoint")
327+ .Loops({Sym("s0")})
328+ .Data("data0", 0, ge::DT_BF16)
329+ .Load("load0", "data0")
330+ .Cast("cast_bf16_to_fp32", "load0", ge::DT_FLOAT)
331+ .Cast("cast_fp32_to_fp16", "cast_bf16_to_fp32", ge::DT_FLOAT16)
332+ .Cast("cast_fp16_to_fp32", "cast_fp32_to_fp16", ge::DT_FLOAT)
333+ .Store("store0", "cast_fp16_to_fp32")
334+ .Output("output0", "store0", 0, ge::DT_FLOAT)
335+ .Build();
336+ 
337+ ASSERT_EQ(ImprovePrecisionForAscGraph(graph), af::SUCCESS);
338+ 
339+ EXPECT_EQ(CountNodesByType(graph, Cast::Type), 1U);
340+ EXPECT_TRUE(HasCastOutputDtype(graph, ge::DT_FLOAT));
341+}
342+ 
343+TEST_F(TestImprovePrecisionST, UnsupportedChainEndpoint_RemovesIdentitySubchain) {
344+ auto graph = AscGraphBuilder("st_identity_subchain")
345+ .Loops({Sym("s0")})
346+ .Data("data0", 0, ge::DT_BF16)
347+ .Load("load0", "data0")
348+ .Cast("cast_bf16_to_fp32", "load0", ge::DT_FLOAT)
349+ .Cast("cast_fp32_identity", "cast_bf16_to_fp32", ge::DT_FLOAT)
350+ .Cast("cast_fp32_identity2", "cast_fp32_identity", ge::DT_FLOAT)
351+ .Cast("cast_fp32_to_fp16", "cast_fp32_identity2", ge::DT_FLOAT16)
352+ .Store("store0", "cast_fp32_to_fp16")
353+ .Output("output0", "store0", 0, ge::DT_FLOAT16)
354+ .Build();
355+ 
356+ ASSERT_EQ(ImprovePrecisionForAscGraph(graph), af::SUCCESS);
357+ 
358+ EXPECT_EQ(CountNodesByType(graph, Cast::Type), 2U);
359+ EXPECT_FALSE(CheckNodeOutputDtype(graph, "cast_fp32_identity", ge::DT_FLOAT));
360+ EXPECT_FALSE(CheckNodeOutputDtype(graph, "cast_fp32_identity2", ge::DT_FLOAT));
361+}
362+ 
324TEST_F(TestImprovePrecisionST, PreProcessEntryPoint_Succeeds) {363TEST_F(TestImprovePrecisionST, PreProcessEntryPoint_Succeeds) {
325 auto graph = AscGraphBuilder("st_preprocess_entry")364 auto graph = AscGraphBuilder("st_preprocess_entry")
326 .Loops({Sym("s0")})365 .Loops({Sym("s0")})
@@ -3,7 +3,7 @@
3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of3 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 * CANN Open Software License Agreement Version 2.0 (the "License").4 * CANN Open Software License Agreement Version 2.0 (the "License").
5 * Please refer to the License for details. You may not use this file except in compliance with the License.5 * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * THIS FILE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR ANY KIND, EITHER EXPRESS OR IMPLIED,6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.7 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 * See LICENSE in the root of the software repository for the full text of the License.8 * See LICENSE in the root of the software repository for the full text of the License.
9 */9 */
@@ -11,12 +11,11 @@
11#include "gtest/gtest.h"11#include "gtest/gtest.h"
12 12 
13#include "asc_graph_builder.h"13#include "asc_graph_builder.h"
14-#include "graph/ascendc_ir/utils/asc_graph_utils.h"
15#define private public14#define private public
16#include "optimize/pre_process/improve_precision.h"15#include "optimize/pre_process/improve_precision.h"
17#include "optimize/pre_process/pre_process_config.h"16#include "optimize/pre_process/pre_process_config.h"
18#undef private17#undef private
19-#include "ascgraph_info_complete.h"18+#include "tests/framework/improve_precision_test_utils.h"
20#include "platform_context.h"19#include "platform_context.h"
21#include "runtime_stub.h"20#include "runtime_stub.h"
22 21 
@@ -27,16 +26,6 @@ using namespace af::ascir_op;
27 26 
28namespace {27namespace {
29 28 
30-size_t CountNodesByType(AscGraph &graph, const std::string &type) {
31- size_t count = 0U;
32- for (const auto &node : AscGraphUtils::GetComputeGraph(graph)->GetAllNodes()) {
33- if (node->GetType() == type) {
34- ++count;
35- }
36- }
37- return count;
38-}
39- 
40bool HasNodeWithName(AscGraph &graph, const std::string &name) {29bool HasNodeWithName(AscGraph &graph, const std::string &name) {
41 for (const auto &node : AscGraphUtils::GetComputeGraph(graph)->GetAllNodes()) {30 for (const auto &node : AscGraphUtils::GetComputeGraph(graph)->GetAllNodes()) {
42 if (node->GetName() == name) {31 if (node->GetName() == name) {
@@ -81,10 +70,10 @@ TEST_F(TestImprovePrecisionUT, Fp16ToFp16CastBeforeStore_CastDeleted) {
81 EXPECT_FALSE(HasNodeWithName(graph, "cast0"));70 EXPECT_FALSE(HasNodeWithName(graph, "cast0"));
82}71}
83 72 
84-TEST_F(TestImprovePrecisionUT, Fp32ToFp16CastBeforeStore_CastDeletedAndAbsPromoted) {73+TEST_F(TestImprovePrecisionUT, Fp32ToFp16CastBeforeStore_CastPreserved) {
85 auto graph = AscGraphBuilder("ut_fp32_to_fp16_before_store")74 auto graph = AscGraphBuilder("ut_fp32_to_fp16_before_store")
86 .Loops({Sym("s0")})75 .Loops({Sym("s0")})
87- .Data("data0", 0, ge::DT_FLOAT16)76+ .Data("data0", 0, ge::DT_FLOAT)
88 .Load("load0", "data0")77 .Load("load0", "data0")
89 .Abs("abs0", "load0")78 .Abs("abs0", "load0")
90 .Cast("cast0", "abs0", ge::DT_FLOAT16)79 .Cast("cast0", "abs0", ge::DT_FLOAT16)
@@ -94,5 +83,41 @@ TEST_F(TestImprovePrecisionUT, Fp32ToFp16CastBeforeStore_CastDeletedAndAbsPromot
94 83 
95 ASSERT_EQ(ImprovePrecisionForAscGraph(graph), af::SUCCESS);84 ASSERT_EQ(ImprovePrecisionForAscGraph(graph), af::SUCCESS);
96 85 
97- EXPECT_FALSE(HasNodeWithName(graph, "cast0"));86+ EXPECT_TRUE(HasNodeWithName(graph, "cast0"));
87+ EXPECT_TRUE(HasCastOutputDtype(graph, ge::DT_FLOAT16));
88+}
89+ 
90+TEST_F(TestImprovePrecisionUT, UnsupportedCastChain_PreservesRequiredCast) {
91+ auto graph = AscGraphBuilder("ut_unsupported_cast_chain")
92+ .Loops({Sym("s0")})
93+ .Data("data0", 0, ge::DT_BF16)
94+ .Load("load0", "data0")
95+ .Cast("cast_bf16_to_fp32", "load0", ge::DT_FLOAT)
96+ .Cast("cast_fp32_identity", "cast_bf16_to_fp32", ge::DT_FLOAT)
97+ .Cast("cast_fp32_to_fp16", "cast_fp32_identity", ge::DT_FLOAT16)
98+ .Store("store0", "cast_fp32_to_fp16")
99+ .Output("output0", "store0", 0, ge::DT_FLOAT16)
100+ .Build();
101+ 
102+ ASSERT_EQ(ImprovePrecisionForAscGraph(graph), af::SUCCESS);
103+ 
104+ EXPECT_EQ(CountNodesByType(graph, Cast::Type), 2U);
105+ EXPECT_TRUE(HasCastOutputDtype(graph, ge::DT_FLOAT));
106+ EXPECT_TRUE(HasCastOutputDtype(graph, ge::DT_FLOAT16));
107+}
108+ 
109+TEST_F(TestImprovePrecisionUT, NonFloatSourceCastChain_FallbackDeletesIdentity) {
110+ auto graph = AscGraphBuilder("ut_non_float_source")
111+ .Loops({Sym("s0")})
112+ .Data("data0", 0, ge::DT_INT32)
113+ .Load("load0", "data0")
114+ .Cast("cast_int32_to_fp32", "load0", ge::DT_FLOAT)
115+ .Cast("cast_fp32_identity", "cast_int32_to_fp32", ge::DT_FLOAT)
116+ .Store("store0", "cast_fp32_identity")
117+ .Output("output0", "store0", 0, ge::DT_FLOAT)
118+ .Build();
119+ 
120+ ASSERT_EQ(ImprovePrecisionForAscGraph(graph), af::SUCCESS);
121+ 
122+ EXPECT_FALSE(HasNodeWithName(graph, "cast_fp32_identity"));
98}123}