已合并
feat: add IndirectLoad fusion support #1463
xiebangrui2025创建于 7月24日
feat: add IndirectLoad fusion support #1463
已合并
共 52 个文件变更+3552-100
| @@ -205,6 +205,7 @@ inline const std::string kXor = "Xor"; | |||
| 205 | inline const std::string kVshrs = "Vshrs"; | 205 | inline const std::string kVshrs = "Vshrs"; |
| 206 | // 下面均为ASCIR定义(已有性能评估) | 206 | // 下面均为ASCIR定义(已有性能评估) |
| 207 | inline const std::string kGather = "Gather"; | 207 | inline const std::string kGather = "Gather"; |
| 208 | +inline const std::string kIndirectLoad = "IndirectLoad"; | ||
| 208 | inline const std::string kAbs = "Abs"; | 209 | inline const std::string kAbs = "Abs"; |
| 209 | inline const std::string kAdd = "Add"; | 210 | inline const std::string kAdd = "Add"; |
| 210 | inline const std::string kBroadcast = "Broadcast"; | 211 | inline const std::string kBroadcast = "Broadcast"; |
| @@ -24,6 +24,8 @@ | |||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | + | ||
| 28 | + | ||
| 27 | 29 | ||
| 28 | 30 | ||
| 29 | 31 | ||
| @@ -853,11 +855,11 @@ void AscendGraphParser::ParserOptionalInfos(const af::AscGraph &graph) const { | |||
| 853 | } | 855 | } |
| 854 | 856 | ||
| 855 | af::Status AscendGraphParser::CalculateReservedUbSize(const af::AscGraph &graph) { | 857 | af::Status AscendGraphParser::CalculateReservedUbSize(const af::AscGraph &graph) { |
| 856 | - constexpr int32_t kSimtDcacheSize = 32 * 1024; | ||
| 857 | tuning_space_->reserve_ub["ascendc"] = ascgen_utils::CalcReservedTmpBufSizeForAscGraph(graph); | 858 | tuning_space_->reserve_ub["ascendc"] = ascgen_utils::CalcReservedTmpBufSizeForAscGraph(graph); |
| 858 | for (const auto &node : graph.GetAllNodes()) { | 859 | for (const auto &node : graph.GetAllNodes()) { |
| 859 | - if (node->GetType() == kGather) { | 860 | + const int64_t simt_dcache_size = ::ascir::GetDcacheSize(*node); |
| 860 | - tuning_space_->reserve_ub["simt_dcache"] = kSimtDcacheSize; | 861 | + if (simt_dcache_size > 0) { |
| 862 | + tuning_space_->reserve_ub["simt_dcache"] = static_cast<uint32_t>(simt_dcache_size); | ||
| 861 | break; | 863 | break; |
| 862 | } | 864 | } |
| 863 | } | 865 | } |
| @@ -875,6 +877,13 @@ af::Status AscendGraphParser::ConvertToTuningSpace(const af::AscGraph &graph) { | |||
| 875 | if (node_iter == graph_sched_info_.end()) { | 877 | if (node_iter == graph_sched_info_.end()) { |
| 876 | continue; | 878 | continue; |
| 877 | } | 879 | } |
| 880 | + const auto indirect_load_behavior = ascgen_utils::indirect_load::GetTemplateBehavior(node); | ||
| 881 | + if (indirect_load_behavior.uses_direct_gm_pipeline || indirect_load_behavior.skips_api_emit) { | ||
| 882 | + GELOGD("[IndirectLoad] Skip tuning-space node info for node[%s], direct_gm[%d], skips_emit[%d].", | ||
| 883 | + node->GetNamePtr(), static_cast<int32_t>(indirect_load_behavior.uses_direct_gm_pipeline), | ||
| 884 | + static_cast<int32_t>(indirect_load_behavior.skips_api_emit)); | ||
| 885 | + continue; | ||
| 886 | + } | ||
| 878 | const auto &sched_attrs = node_iter->second; | 887 | const auto &sched_attrs = node_iter->second; |
| 879 | GE_ASSERT_SUCCESS(ConvertNodeInfos(node, sched_attrs, graph, use_cache_flag), "Parse node info failed."); | 888 | GE_ASSERT_SUCCESS(ConvertNodeInfos(node, sched_attrs, graph, use_cache_flag), "Parse node info failed."); |
| 880 | } | 889 | } |
| @@ -2384,6 +2384,11 @@ af::Status TilingCodeGenImpl::GenSelectBetterTilingBasedOnObjAndUbRatio() { | |||
| 2384 | tiling_func_.AddLine(" tilingCaseImplPtr->GetTilingData(tmp_tiling, tiling_data);"); | 2384 | tiling_func_.AddLine(" tilingCaseImplPtr->GetTilingData(tmp_tiling, tiling_data);"); |
| 2385 | tiling_func_.AddLine(" }"); | 2385 | tiling_func_.AddLine(" }"); |
| 2386 | tiling_func_.AddLine(" }"); | 2386 | tiling_func_.AddLine(" }"); |
| 2387 | + } else { | ||
| 2388 | + tiling_func_.AddLine(GenCallUpdateBetterTiling(is_uniq_group_)); | ||
| 2389 | + tiling_func_.AddLine(" sub_case_flag = is_sub_case;"); | ||
| 2390 | + tiling_func_.AddLine(" obj = cur_obj;"); | ||
| 2391 | + tiling_func_.AddLine(" return true;"); | ||
| 2387 | } | 2392 | } |
| 2388 | return af::SUCCESS; | 2393 | return af::SUCCESS; |
| 2389 | } | 2394 | } |
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | + | ||
| 16 | 17 | ||
| 17 | namespace codegen { | 18 | namespace codegen { |
| 18 | 19 | ||
| @@ -214,6 +215,9 @@ Status IsRepeatStrideValid(const ascir::ImplGraph &graph) { | |||
| 214 | 215 | ||
| 215 | Status IsGraphNodeValid(const ascir::ImplGraph &graph) { | 216 | Status IsGraphNodeValid(const ascir::ImplGraph &graph) { |
| 216 | for (const auto &node : graph.GetAllNodes()) { | 217 | for (const auto &node : graph.GetAllNodes()) { |
| 218 | + if (ascgen_utils::indirect_load::GetTemplateBehavior(node).skips_api_emit) { | ||
| 219 | + continue; | ||
| 220 | + } | ||
| 217 | auto impl = ascgen_utils::GetAscIrCodegenImpl(node->GetType()); | 221 | auto impl = ascgen_utils::GetAscIrCodegenImpl(node->GetType()); |
| 218 | GE_ASSERT_NOTNULL(impl, "GetAscIrCodegenImpl of node %s[%s] is null", node->GetTypePtr(), node->GetNamePtr()); | 222 | GE_ASSERT_NOTNULL(impl, "GetAscIrCodegenImpl of node %s[%s] is null", node->GetTypePtr(), node->GetNamePtr()); |
| 219 | GE_ASSERT_TRUE(impl->IsNodeValid(*node), "Node %s[%s] is invalid", node->GetTypePtr(), node->GetNamePtr()); | 223 | GE_ASSERT_TRUE(impl->IsNodeValid(*node), "Node %s[%s] is invalid", node->GetTypePtr(), node->GetNamePtr()); |
| @@ -25,7 +25,10 @@ | |||
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | 27 | ||
| 28 | + | ||
| 28 | 29 | ||
| 30 | + | ||
| 31 | + | ||
| 29 | 32 | ||
| 30 | 33 | ||
| 31 | 34 | ||
| @@ -40,6 +43,43 @@ constexpr uint32_t kFuncIdBegin = 20000000U; | |||
| 40 | constexpr const char kInputTensorDescName[] = "input_tensor_desc"; | 43 | constexpr const char kInputTensorDescName[] = "input_tensor_desc"; |
| 41 | constexpr const char kOutputTensorDescName[] = "output_tensor_desc"; | 44 | constexpr const char kOutputTensorDescName[] = "output_tensor_desc"; |
| 42 | 45 | ||
| 46 | +struct QueCollection { | ||
| 47 | + std::unordered_map<ascir::QueId, af::Position> que_id_to_src_position; | ||
| 48 | + std::set<ascir::QueId> need_bind_que_id; | ||
| 49 | + std::map<ascir::QueId, bool> direct_gm_ques; | ||
| 50 | +}; | ||
| 51 | + | ||
| 52 | +Status AllocateQues(TPipe &tpipe, const QueCollection &collection) { | ||
| 53 | + for (const auto &iter : collection.que_id_to_src_position) { | ||
| 54 | + if (tpipe.ques.count(iter.first) > 0UL) { | ||
| 55 | + continue; | ||
| 56 | + } | ||
| 57 | + std::string position; | ||
| 58 | + GE_CHK_STATUS_RET(PositionValue(iter.second, position), "Codegen get position value failed"); | ||
| 59 | + const bool need_que_bind = collection.need_bind_que_id.count(iter.first) > 0UL; | ||
| 60 | + if (need_que_bind) { | ||
| 61 | + std::string dst_position; | ||
| 62 | + GE_CHK_STATUS_RET(PositionValue(af::Position::kPositionVecOut, dst_position), | ||
| 63 | + "Codegen get position value failed"); | ||
| 64 | + const auto new_que = tpipe.ques.emplace(iter.first, TQue{iter.first, iter.second, position, dst_position}); | ||
| 65 | + GE_CHK_BOOL_RET_STATUS(new_que.second, af::FAILED, "Codegen emplace que [%ld] failed", iter.first); | ||
| 66 | + } else { | ||
| 67 | + const auto new_que = tpipe.ques.emplace(iter.first, TQue{iter.first, iter.second, position}); | ||
| 68 | + GE_CHK_BOOL_RET_STATUS(new_que.second, af::FAILED, "Codegen emplace que [%ld] failed", iter.first); | ||
| 69 | + } | ||
| 70 | + const auto skip_iter = collection.direct_gm_ques.find(iter.first); | ||
| 71 | + if (skip_iter != collection.direct_gm_ques.end()) { | ||
| 72 | + tpipe.ques.at(iter.first).skip_init_for_simt_direct_gm = skip_iter->second; | ||
| 73 | + } | ||
| 74 | + } | ||
| 75 | + for (auto &[id, que] : tpipe.ques) { | ||
| 76 | + if (id != tpipe.cube_output_que_id) { | ||
| 77 | + que.is_cv_ub_fusion = (tpipe.cv_fusion_type == ascir::CubeTemplateType::kUBFuse); | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + return af::SUCCESS; | ||
| 81 | +} | ||
| 82 | + | ||
| 43 | std::string GetTensorName(const ascir::TensorAttr &tensor) { | 83 | std::string GetTensorName(const ascir::TensorAttr &tensor) { |
| 44 | const auto node = tensor.anchor.GetOwnerNodeBarePtr(); | 84 | const auto node = tensor.anchor.GetOwnerNodeBarePtr(); |
| 45 | if (node != nullptr && node->GetType() == ScalarData::Type) { | 85 | if (node != nullptr && node->GetType() == ScalarData::Type) { |
| @@ -1288,7 +1328,8 @@ Status Kernel::ParseUbScalarOptimizationInfo(const ascir::NodeView &node, Tensor | |||
| 1288 | Status Kernel::JudgeIsLoadLinkStoreAndVec(const ascir::NodeView &node, Tensor &t, ascir::TensorId id) const { | 1328 | Status Kernel::JudgeIsLoadLinkStoreAndVec(const ascir::NodeView &node, Tensor &t, ascir::TensorId id) const { |
| 1289 | // todo: 解决load多引用场景,被store, vec 同时引用的缺少mte3到mte2的同步的问题, | 1329 | // todo: 解决load多引用场景,被store, vec 同时引用的缺少mte3到mte2的同步的问题, |
| 1290 | // 临时方案,从这里解析下是否该场景 | 1330 | // 临时方案,从这里解析下是否该场景 |
| 1291 | - if ((node->attr.api.compute_type == ascir::ComputeType::kComputeLoad) && (!IsOps<Gather>(node))) { | 1331 | + if ((node->attr.api.compute_type == ascir::ComputeType::kComputeLoad) && |
| 1332 | + (!optimize::ScheduleUtils::IsGatherLikeLoad(std::dynamic_pointer_cast<af::AscNode>(node)))) { | ||
| 1292 | bool link_to_store = false; | 1333 | bool link_to_store = false; |
| 1293 | bool link_to_vec = false; | 1334 | bool link_to_vec = false; |
| 1294 | for (auto &out : node->outputs()) { | 1335 | for (auto &out : node->outputs()) { |
| @@ -1416,6 +1457,10 @@ Status TPipe::TensorAlloc(const Tensor &tensor, std::string &result) const { | |||
| 1416 | } | 1457 | } |
| 1417 | 1458 | ||
| 1418 | Status TPipe::InitTQueBuffers(const TQue &que, std::string &result) const { | 1459 | Status TPipe::InitTQueBuffers(const TQue &que, std::string &result) const { |
| 1460 | + if (que.skip_init_for_simt_direct_gm) { | ||
| 1461 | + result.clear(); | ||
| 1462 | + return af::SUCCESS; | ||
| 1463 | + } | ||
| 1419 | stringstream ss; | 1464 | stringstream ss; |
| 1420 | std::string blk_align; | 1465 | std::string blk_align; |
| 1421 | GE_CHK_STATUS_RET(KernelUtils::BlkAlign(ge::DT_UINT8, blk_align), "Codegen blk align failed"); | 1466 | GE_CHK_STATUS_RET(KernelUtils::BlkAlign(ge::DT_UINT8, blk_align), "Codegen blk align failed"); |
| @@ -1465,6 +1510,10 @@ std::string TPipe::TensorSizeCalc() const { | |||
| 1465 | for (const auto &pair : this->tensors) { | 1510 | for (const auto &pair : this->tensors) { |
| 1466 | const auto &t = pair.second; | 1511 | const auto &t = pair.second; |
| 1467 | if (t.alloc_type == af::AllocType::kAllocTypeQueue) { | 1512 | if (t.alloc_type == af::AllocType::kAllocTypeQueue) { |
| 1513 | + const TQue *que = GetQue(t.que_id); | ||
| 1514 | + if (que != nullptr && que->skip_init_for_simt_direct_gm) { | ||
| 1515 | + continue; | ||
| 1516 | + } | ||
| 1468 | ss << t.size.DefineConst(this->tiler.TensorVectorizedSize(t)) << std::endl; | 1517 | ss << t.size.DefineConst(this->tiler.TensorVectorizedSize(t)) << std::endl; |
| 1469 | ss << t.que_buf_num.DefineConst(to_string(t.que_buf_num_value)) << std::endl; | 1518 | ss << t.que_buf_num.DefineConst(to_string(t.que_buf_num_value)) << std::endl; |
| 1470 | } else if (t.alloc_type == af::AllocType::kAllocTypeBuffer) { | 1519 | } else if (t.alloc_type == af::AllocType::kAllocTypeBuffer) { |
| @@ -1547,7 +1596,7 @@ Status TPipe::LocalTQueAlloc(std::string &result) const { | |||
| 1547 | stringstream ss; | 1596 | stringstream ss; |
| 1548 | 1597 | ||
| 1549 | for (auto &[id, que] : this->ques) { | 1598 | for (auto &[id, que] : this->ques) { |
| 1550 | - if (id == this->cube_output_que_id) { | 1599 | + if (id == this->cube_output_que_id || que.skip_init_for_simt_direct_gm) { |
| 1551 | continue; | 1600 | continue; |
| 1552 | } | 1601 | } |
| 1553 | stringstream tensor_size_max; | 1602 | stringstream tensor_size_max; |
| @@ -1560,10 +1609,7 @@ Status TPipe::LocalTQueAlloc(std::string &result) const { | |||
| 1560 | 1609 | ||
| 1561 | for (auto mid : que.merge_scopes) { | 1610 | for (auto mid : que.merge_scopes) { |
| 1562 | auto merge_scope = this->merge_scopes.find(mid); | 1611 | auto merge_scope = this->merge_scopes.find(mid); |
| 1563 | - if (merge_scope == this->merge_scopes.end()) { | 1612 | + GE_ASSERT_TRUE(merge_scope != this->merge_scopes.end(), "Codegen merge scope not found:%ld", mid); |
| 1564 | - GELOGE(af::FAILED, "Codegen merge scope not found:%ld", mid); | ||
| 1565 | - return af::FAILED; | ||
| 1566 | - } | ||
| 1567 | 1613 | ||
| 1568 | if (is_first) { | 1614 | if (is_first) { |
| 1569 | is_first = false; | 1615 | is_first = false; |
| @@ -1579,10 +1625,7 @@ Status TPipe::LocalTQueAlloc(std::string &result) const { | |||
| 1579 | uint32_t tensor_buf_num_max_val = 0; | 1625 | uint32_t tensor_buf_num_max_val = 0; |
| 1580 | for (auto tid : que.not_merge_tensors) { | 1626 | for (auto tid : que.not_merge_tensors) { |
| 1581 | auto tensor = this->tensors.find(tid); | 1627 | auto tensor = this->tensors.find(tid); |
| 1582 | - if (tensor == this->tensors.end()) { | 1628 | + GE_ASSERT_TRUE(tensor != this->tensors.end(), "Codegen tensor not found:%ld", tid); |
| 1583 | - GELOGE(af::FAILED, "Codegen tensor not found:%ld", tid); | ||
| 1584 | - return af::FAILED; | ||
| 1585 | - } | ||
| 1586 | 1629 | ||
| 1587 | if (is_first) { | 1630 | if (is_first) { |
| 1588 | is_first = false; | 1631 | is_first = false; |
| @@ -1815,8 +1858,7 @@ std::string TPipe::SyncMte2ToMte3(const Tensor in_tensor) const { | |||
| 1815 | } | 1858 | } |
| 1816 | 1859 | ||
| 1817 | Status TPipe::CollectQues(const ascir::ImplGraph &graph) { | 1860 | Status TPipe::CollectQues(const ascir::ImplGraph &graph) { |
| 1818 | - std::unordered_map<ascir::QueId, af::Position> que_id_to_src_position; | 1861 | + QueCollection collection; |
| 1819 | - std::set<ascir::QueId> need_bind_que_id; | ||
| 1820 | for (auto node : graph.GetAllNodes()) { | 1862 | for (auto node : graph.GetAllNodes()) { |
| 1821 | if (node->attr.api.type == ge::ApiType::kAPITypeBuffer) { | 1863 | if (node->attr.api.type == ge::ApiType::kAPITypeBuffer) { |
| 1822 | continue; | 1864 | continue; |
| @@ -1826,8 +1868,13 @@ Status TPipe::CollectQues(const ascir::ImplGraph &graph) { | |||
| 1826 | continue; | 1868 | continue; |
| 1827 | } | 1869 | } |
| 1828 | const int64_t tensor_que_id = out_tensor->attr.que.id; | 1870 | const int64_t tensor_que_id = out_tensor->attr.que.id; |
| 1871 | + const bool uses_direct_gm = ascgen_utils::indirect_load::GetTemplateBehavior(node).uses_direct_gm_pipeline; | ||
| 1872 | + auto [direct_gm_iter, inserted] = collection.direct_gm_ques.emplace(tensor_que_id, uses_direct_gm); | ||
| 1873 | + if (!inserted) { | ||
| 1874 | + direct_gm_iter->second = direct_gm_iter->second && uses_direct_gm; | ||
| 1875 | + } | ||
| 1829 | if (out_tensor->attr.mem.position == af::Position::kPositionVecIn) { | 1876 | if (out_tensor->attr.mem.position == af::Position::kPositionVecIn) { |
| 1830 | - que_id_to_src_position.emplace(tensor_que_id, out_tensor->attr.mem.position); | 1877 | + collection.que_id_to_src_position.emplace(tensor_que_id, out_tensor->attr.mem.position); |
| 1831 | 1878 | ||
| 1832 | std::set<std::string> peer_node_types; | 1879 | std::set<std::string> peer_node_types; |
| 1833 | for (const auto &peer_in_anchor : out_tensor->anchor.GetPeerInDataAnchorsPtr()) { | 1880 | for (const auto &peer_in_anchor : out_tensor->anchor.GetPeerInDataAnchorsPtr()) { |
| @@ -1836,40 +1883,14 @@ Status TPipe::CollectQues(const ascir::ImplGraph &graph) { | |||
| 1836 | } | 1883 | } |
| 1837 | } | 1884 | } |
| 1838 | if ((peer_node_types.size() == 1U) && *peer_node_types.begin() == Store::Type) { | 1885 | if ((peer_node_types.size() == 1U) && *peer_node_types.begin() == Store::Type) { |
| 1839 | - need_bind_que_id.emplace(tensor_que_id); | 1886 | + collection.need_bind_que_id.emplace(tensor_que_id); |
| 1840 | } | 1887 | } |
| 1841 | } else if (out_tensor->attr.mem.position == af::Position::kPositionVecOut) { | 1888 | } else if (out_tensor->attr.mem.position == af::Position::kPositionVecOut) { |
| 1842 | - que_id_to_src_position.emplace(tensor_que_id, out_tensor->attr.mem.position); | 1889 | + collection.que_id_to_src_position.emplace(tensor_que_id, out_tensor->attr.mem.position); |
| 1843 | } | 1890 | } |
| 1844 | } | 1891 | } |
| 1845 | } | 1892 | } |
| 1846 | - | 1893 | + return AllocateQues(*this, collection); |
| 1847 | - // Allocate | ||
| 1848 | - for (const auto &iter : que_id_to_src_position) { | ||
| 1849 | - if (this->ques.count(iter.first) > 0UL) { | ||
| 1850 | - continue; | ||
| 1851 | - } | ||
| 1852 | - std::string position; | ||
| 1853 | - GE_CHK_STATUS_RET(PositionValue(iter.second, position), "Codegen get position value failed"); | ||
| 1854 | - // 如果qid被非load->store的load复用,则不能使用TQueBind | ||
| 1855 | - bool need_que_bind = need_bind_que_id.count(iter.first) > 0UL; | ||
| 1856 | - if (need_que_bind) { | ||
| 1857 | - std::string dst_position; | ||
| 1858 | - GE_CHK_STATUS_RET(PositionValue(af::Position::kPositionVecOut, dst_position), | ||
| 1859 | - "Codegen get position value failed"); | ||
| 1860 | - auto new_que = this->ques.emplace(iter.first, TQue{iter.first, iter.second, position, dst_position}); | ||
| 1861 | - GE_CHK_BOOL_RET_STATUS(new_que.second, af::FAILED, "Codegen emplace que [%ld] failed", iter.first); | ||
| 1862 | - } else { | ||
| 1863 | - auto new_que = this->ques.emplace(iter.first, TQue{iter.first, iter.second, position}); | ||
| 1864 | - GE_CHK_BOOL_RET_STATUS(new_que.second, af::FAILED, "Codegen emplace que [%ld] failed", iter.first); | ||
| 1865 | - } | ||
| 1866 | - } | ||
| 1867 | - for (auto &[id, que] : this->ques) { | ||
| 1868 | - if (id != this->cube_output_que_id) { | ||
| 1869 | - que.is_cv_ub_fusion = (this->cv_fusion_type == ascir::CubeTemplateType::kUBFuse); | ||
| 1870 | - } | ||
| 1871 | - } | ||
| 1872 | - return af::SUCCESS; | ||
| 1873 | } | 1894 | } |
| 1874 | 1895 | ||
| 1875 | void TPipe::SetUsingAttCalcQBTSizeConfig(bool using_att_calc_qbt_size) { | 1896 | void TPipe::SetUsingAttCalcQBTSizeConfig(bool using_att_calc_qbt_size) { |
| @@ -2155,7 +2176,8 @@ Status Kernel::ParseGraph(const ascir::ImplGraph &graph, const ascir::FusedSched | |||
| 2155 | } | 2176 | } |
| 2156 | continue; | 2177 | continue; |
| 2157 | } | 2178 | } |
| 2158 | - has_gather = (has_gather || IsOps<Gather>(node)); | 2179 | + has_gather = |
| 2180 | + (has_gather || optimize::ScheduleUtils::IsGatherLikeLoad(std::dynamic_pointer_cast<af::AscNode>(node))); | ||
| 2159 | } | 2181 | } |
| 2160 | for (const auto &pair : kernel_outputs) { | 2182 | for (const auto &pair : kernel_outputs) { |
| 2161 | kernel.outputs.emplace_back(GM_ADDR(GenValidName(pair.second.first))); | 2183 | kernel.outputs.emplace_back(GM_ADDR(GenValidName(pair.second.first))); |
| @@ -2192,6 +2214,11 @@ Status Kernel::ParseGraph(const ascir::ImplGraph &graph, const ascir::FusedSched | |||
| 2192 | if (IsOps<Output>(node) || IsOps<Data>(node) || IsOps<ScalarData>(node)) { | 2214 | if (IsOps<Output>(node) || IsOps<Data>(node) || IsOps<ScalarData>(node)) { |
| 2193 | continue; | 2215 | continue; |
| 2194 | } | 2216 | } |
| 2217 | + const auto indirect_load_behavior = ascgen_utils::indirect_load::GetTemplateBehavior(node); | ||
| 2218 | + if (indirect_load_behavior.skips_api_emit && indirect_load_behavior.uses_direct_gm_pipeline && | ||
| 2219 | + !IsOps<Store>(node)) { | ||
| 2220 | + continue; | ||
| 2221 | + } | ||
| 2195 | 2222 | ||
| 2196 | auto desc = node->GetOpDesc(); | 2223 | auto desc = node->GetOpDesc(); |
| 2197 | for (auto output : node->outputs()) { | 2224 | for (auto output : node->outputs()) { |
| @@ -203,6 +203,7 @@ class TQue : public Variable { | |||
| 203 | 203 | ||
| 204 | Variable buf; | 204 | Variable buf; |
| 205 | bool is_cv_ub_fusion{false}; | 205 | bool is_cv_ub_fusion{false}; |
| 206 | + bool skip_init_for_simt_direct_gm{false}; | ||
| 206 | 207 | ||
| 207 | TQue(ascir::QueId que_id, ascir::Position pos, std::string &position_name); | 208 | TQue(ascir::QueId que_id, ascir::Position pos, std::string &position_name); |
| 208 | TQue(ascir::QueId que_id, ascir::Position src_position, const std::string &src_position_name, | 209 | TQue(ascir::QueId que_id, ascir::Position src_position, const std::string &src_position_name, |
| @@ -18,6 +18,8 @@ | |||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | + | ||
| 22 | + | ||
| 21 | 23 | ||
| 22 | using namespace std; | 24 | using namespace std; |
| 23 | using namespace af::ops; | 25 | using namespace af::ops; |
| @@ -32,6 +34,59 @@ const std::string kEnCacheOriginBroadcastAxis = "enable_cache_origin_brc_axis"; | |||
| 32 | const std::string kEnCacheFusedBroadcastAxis = "enable_cache_fused_brc_axis"; | 34 | const std::string kEnCacheFusedBroadcastAxis = "enable_cache_fused_brc_axis"; |
| 33 | const std::string kEnCacheA = "dis_enable_cache_a"; | 35 | const std::string kEnCacheA = "dis_enable_cache_a"; |
| 34 | const std::string kEnCacheR = "dis_enable_cache_r"; | 36 | const std::string kEnCacheR = "dis_enable_cache_r"; |
| 37 | + | ||
| 38 | +ApiCall *FindIndirectLoadOpCall(Loop *loop, bool require_direct_gm) { | ||
| 39 | + if (loop == nullptr) { | ||
| 40 | + return nullptr; | ||
| 41 | + } | ||
| 42 | + for (const auto &body : loop->bodys) { | ||
| 43 | + if (body.type == LoopType::CALL) { | ||
| 44 | + if (body.call == nullptr || !af::ops::IsOps<af::ascir_op::IndirectLoad>(body.call->node)) { | ||
| 45 | + continue; | ||
| 46 | + } | ||
| 47 | + const auto behavior = | ||
| 48 | + ascgen_utils::indirect_load::GetTemplateBehavior(std::dynamic_pointer_cast<af::AscNode>(body.call->node)); | ||
| 49 | + if (!require_direct_gm || (behavior.uses_direct_gm_pipeline && behavior.skips_ub_lifecycle)) { | ||
| 50 | + return body.call; | ||
| 51 | + } | ||
| 52 | + } else if (ApiCall *call = FindIndirectLoadOpCall(body.loop, require_direct_gm); call != nullptr) { | ||
| 53 | + return call; | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + return nullptr; | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +Status AddSkippedApiEmitProcessCall(const ascir::NodeView &node, Loop *current_loop, | ||
| 60 | + const std::vector<ascir::AxisId> ¤t_axis, | ||
| 61 | + std::map<ascir::TensorId, ApiCall *> &tensor_calls) { | ||
| 62 | + auto call = CreateApiCallObject(node); | ||
| 63 | + GE_ASSERT_NOTNULL(call, "Create api call object failed, ascir type:%s", node->GetTypePtr()); | ||
| 64 | + current_loop->AddCall(call); | ||
| 65 | + GE_CHK_STATUS_RET(call->Init(node), "ApiCall Init failed, ascir type:%s", node->GetTypePtr()); | ||
| 66 | + call->skip_api_emit = true; | ||
| 67 | + call->exec_condition = node->attr.sched.exec_condition; | ||
| 68 | + call->axis = current_loop->axis_id; | ||
| 69 | + call->depth = current_axis.size(); | ||
| 70 | + if (IsOps<Store>(node)) { | ||
| 71 | + for (auto out : node->outputs()) { | ||
| 72 | + tensor_calls.insert({out->attr.mem.tensor_id, call}); | ||
| 73 | + } | ||
| 74 | + return af::SUCCESS; | ||
| 75 | + } | ||
| 76 | + GE_ASSERT_TRUE(node->inputs.Size() == 1UL, | ||
| 77 | + "Skipped api emit process node only supports single-input tensor mapping, node[%s].", | ||
| 78 | + node->GetNamePtr()); | ||
| 79 | + auto input = node->inputs()[0]; | ||
| 80 | + GE_ASSERT_NOTNULL(input, "Skipped api emit process node[%s] input is null", node->GetNamePtr()); | ||
| 81 | + auto in_call = tensor_calls.find(input->attr.mem.tensor_id); | ||
| 82 | + GE_CHK_BOOL_RET_STATUS(in_call != tensor_calls.end(), af::FAILED, | ||
| 83 | + "Codegen node[%s] no API call found for process input tensor id[%ld]", node->GetNamePtr(), | ||
| 84 | + input->attr.mem.tensor_id); | ||
| 85 | + for (auto out : node->outputs()) { | ||
| 86 | + tensor_calls.insert({out->attr.mem.tensor_id, in_call->second}); | ||
| 87 | + } | ||
| 88 | + return af::SUCCESS; | ||
| 89 | +} | ||
| 35 | } // namespace | 90 | } // namespace |
| 36 | 91 | ||
| 37 | Loop::Loop(const ascir::AxisId axis) : axis_id(axis), parent(nullptr) {} | 92 | Loop::Loop(const ascir::AxisId axis) : axis_id(axis), parent(nullptr) {} |
| @@ -250,6 +305,11 @@ Status Loop::ConstructFromNodes(ascir::NodeViewVisitorConst nodes, const Tiler & | |||
| 250 | TraverseGraphForReduceNodes(nodes, current_loop->is_graph_has_reduce_node, current_loop->is_ar); | 305 | TraverseGraphForReduceNodes(nodes, current_loop->is_graph_has_reduce_node, current_loop->is_ar); |
| 251 | auto lifecycle_edge = GetLifecycleEdge(nodes, tpipe); | 306 | auto lifecycle_edge = GetLifecycleEdge(nodes, tpipe); |
| 252 | for (auto node : nodes) { | 307 | for (auto node : nodes) { |
| 308 | + if (IsSkippedApiEmitProcessNode(node)) { | ||
| 309 | + GE_CHK_STATUS_RET(AddSkippedApiEmitProcessCall(node, current_loop, current_axis, tensor_calls)); | ||
| 310 | + continue; | ||
| 311 | + } | ||
| 312 | + | ||
| 253 | // Loop enter or create | 313 | // Loop enter or create |
| 254 | GELOGI("node:%s, ComputeUnit:%u\r\n", node->GetNamePtr(), static_cast<uint32_t>(node->attr.api.unit)); | 314 | GELOGI("node:%s, ComputeUnit:%u\r\n", node->GetNamePtr(), static_cast<uint32_t>(node->attr.api.unit)); |
| 255 | if (node->attr.api.unit != af::ComputeUnit::kUnitNone) { | 315 | if (node->attr.api.unit != af::ComputeUnit::kUnitNone) { |
| @@ -454,21 +514,30 @@ Status Loop::GenerateBody(const Tiler &tiler, const TPipe &tpipe, std::vector<as | |||
| 454 | } | 514 | } |
| 455 | if (body.type == LoopType::LOOP) { | 515 | if (body.type == LoopType::LOOP) { |
| 456 | for (auto call : target_calls) { | 516 | for (auto call : target_calls) { |
| 457 | - GE_CHK_STATUS_RET(call->AllocOutputs(tpipe, ss), "Codegen alloc outputs failed"); | 517 | + if (!ascgen_utils::indirect_load::GetTemplateBehavior(std::dynamic_pointer_cast<af::AscNode>(call->node)) |
| 518 | + .skips_ub_lifecycle) { | ||
| 519 | + GE_CHK_STATUS_RET(call->AllocOutputs(tpipe, ss), "Codegen alloc outputs failed"); | ||
| 520 | + } | ||
| 458 | used_calls.insert(call); | 521 | used_calls.insert(call); |
| 459 | } | 522 | } |
| 460 | body.loop->compute_stage = this->compute_stage; | 523 | body.loop->compute_stage = this->compute_stage; |
| 461 | GE_CHK_STATUS_RET(body.loop->GenerateLoop(tiler, tpipe, current_axis, ss), "Generate loop for body failed"); | 524 | GE_CHK_STATUS_RET(body.loop->GenerateLoop(tiler, tpipe, current_axis, ss), "Generate loop for body failed"); |
| 462 | for (auto call : target_calls) { | 525 | for (auto call : target_calls) { |
| 463 | - GE_CHK_BOOL_RET_STATUS(call->SyncOutputs(tpipe, ss), af::FAILED, "Func SyncOutputs return false"); | 526 | + if (!ascgen_utils::indirect_load::GetTemplateBehavior(std::dynamic_pointer_cast<af::AscNode>(call->node)) |
| 527 | + .skips_ub_lifecycle) { | ||
| 528 | + GE_CHK_BOOL_RET_STATUS(call->SyncOutputs(tpipe, ss), af::FAILED, "Func SyncOutputs return false"); | ||
| 529 | + } | ||
| 464 | } | 530 | } |
| 465 | used_calls.clear(); | 531 | used_calls.clear(); |
| 466 | } else { | 532 | } else { |
| 467 | - if (body.call->unit == af::ComputeUnit::kUnitNone) { | 533 | + if (body.call->unit == af::ComputeUnit::kUnitNone || body.call->skip_api_emit) { |
| 468 | continue; | 534 | continue; |
| 469 | } | 535 | } |
| 536 | + const bool skips_ub_lifecycle = | ||
| 537 | + ascgen_utils::indirect_load::GetTemplateBehavior(std::dynamic_pointer_cast<af::AscNode>(body.call->node)) | ||
| 538 | + .skips_ub_lifecycle; | ||
| 470 | GE_CHK_BOOL_RET_STATUS(body.call->WaitInputs(tpipe, ss), af::FAILED, "Func WaitInputs return false"); | 539 | GE_CHK_BOOL_RET_STATUS(body.call->WaitInputs(tpipe, ss), af::FAILED, "Func WaitInputs return false"); |
| 471 | - if (!IsFindInUsedCalls(body.call)) { | 540 | + if (!IsFindInUsedCalls(body.call) && !skips_ub_lifecycle) { |
| 472 | GE_CHK_STATUS_RET(body.call->AllocOutputs(tpipe, ss), "Codegen alloc outputs failed"); | 541 | GE_CHK_STATUS_RET(body.call->AllocOutputs(tpipe, ss), "Codegen alloc outputs failed"); |
| 473 | } | 542 | } |
| 474 | std::string call; | 543 | std::string call; |
| @@ -501,12 +570,14 @@ Status Loop::GenerateBody(const Tiler &tiler, const TPipe &tpipe, std::vector<as | |||
| 501 | } | 570 | } |
| 502 | } | 571 | } |
| 503 | 572 | ||
| 504 | - if (!IsFindInUsedCalls(body.call)) { | 573 | + if (!skips_ub_lifecycle) { |
| 505 | - GE_CHK_BOOL_RET_STATUS(body.call->SyncOutputs(tpipe, ss), af::FAILED, "Func SyncOutputs return false"); | 574 | + if (!IsFindInUsedCalls(body.call)) { |
| 575 | + GE_CHK_BOOL_RET_STATUS(body.call->SyncOutputs(tpipe, ss), af::FAILED, "Func SyncOutputs return false"); | ||
| 576 | + } | ||
| 577 | + GE_CHK_BOOL_RET_STATUS(body.call->FreeInputs(tpipe, ss), af::FAILED, "Func FreeInputs return false"); | ||
| 578 | + GE_CHK_BOOL_RET_STATUS(body.call->FreeUnusedOutputs(tpipe, ss), af::FAILED, | ||
| 579 | + "Func FreeUnusedOutputs return false"); | ||
| 506 | } | 580 | } |
| 507 | - GE_CHK_BOOL_RET_STATUS(body.call->FreeInputs(tpipe, ss), af::FAILED, "Func FreeInputs return false"); | ||
| 508 | - GE_CHK_BOOL_RET_STATUS(body.call->FreeUnusedOutputs(tpipe, ss), af::FAILED, | ||
| 509 | - "Func FreeUnusedOutputs return false"); | ||
| 510 | ss << std::endl; | 581 | ss << std::endl; |
| 511 | } | 582 | } |
| 512 | } | 583 | } |
| @@ -627,13 +698,27 @@ Status Loop::GenerateLoop(const Tiler &tiler, const TPipe &tpipe, std::vector<as | |||
| 627 | if (axis.type == Axis::Type::kAxisTypeBlockInner) { | 698 | if (axis.type == Axis::Type::kAxisTypeBlockInner) { |
| 628 | auto peer = tiler.GetAxis(axis.split_pair_other_id); | 699 | auto peer = tiler.GetAxis(axis.split_pair_other_id); |
| 629 | ss << "int32_t block_dim_offset = " << peer.Str() << " * " << tiler.Size(axis.size) << ";" << std::endl; | 700 | ss << "int32_t block_dim_offset = " << peer.Str() << " * " << tiler.Size(axis.size) << ";" << std::endl; |
| 701 | + ApiCall *call = FindIndirectLoadOpCall(this, true); | ||
| 702 | + if (call != nullptr) { | ||
| 703 | + std::string call_code; | ||
| 704 | + GE_CHK_STATUS_RET(call->Generate(tpipe, current_axis, call_code), "Codegen generate hoisted call failed"); | ||
| 705 | + ss << call_code << std::endl; | ||
| 706 | + call->skip_api_emit = true; | ||
| 707 | + current_axis.pop_back(); | ||
| 708 | + return af::SUCCESS; | ||
| 709 | + } | ||
| 710 | + } | ||
| 711 | + if (axis.type == Axis::Type::kAxisTypeTileInner && FindIndirectLoadOpCall(this, false) != nullptr) { | ||
| 712 | + ss << tiler.GenInnerLoopSizeAndActualSize(axis.id, axis.split_pair_other_id); | ||
| 630 | } | 713 | } |
| 631 | if (tpipe.cv_fusion_type == ascir::CubeTemplateType::kUBFuse && axis.type == Axis::Type::kAxisTypeTileOuter) { | 714 | if (tpipe.cv_fusion_type == ascir::CubeTemplateType::kUBFuse && axis.type == Axis::Type::kAxisTypeTileOuter) { |
| 632 | ss << axis.loop_size.AsArg() << " = 1;" << std::endl; | 715 | ss << axis.loop_size.AsArg() << " = 1;" << std::endl; |
| 633 | } | 716 | } |
| 634 | ss << "for (" << axis.AsArg() << " = 0; " << axis << " < " << axis.loop_size.Str() << "; " << axis << "++) " | 717 | ss << "for (" << axis.AsArg() << " = 0; " << axis << " < " << axis.loop_size.Str() << "; " << axis << "++) " |
| 635 | << "{" << std::endl; | 718 | << "{" << std::endl; |
| 636 | - if (tpipe.cv_fusion_type != ascir::CubeTemplateType::kUBFuse) { | 719 | + const bool skip_calc_from_axis_for_indirect_load_simt = |
| 720 | + axis.type == Axis::Type::kAxisTypeTileInner && FindIndirectLoadOpCall(this, true) != nullptr; | ||
| 721 | + if (tpipe.cv_fusion_type != ascir::CubeTemplateType::kUBFuse && !skip_calc_from_axis_for_indirect_load_simt) { | ||
| 637 | ss << tiler.CalcFromAxis(axis.id); | 722 | ss << tiler.CalcFromAxis(axis.id); |
| 638 | } | 723 | } |
| 639 | GenerateEnCacheCondition(tiler, tpipe, axis, ss); | 724 | GenerateEnCacheCondition(tiler, tpipe, axis, ss); |
| @@ -1187,7 +1272,8 @@ bool ApiCall::WaitInputVector(const TPipe &tpipe, const ApiTensor *in, const Ten | |||
| 1187 | bool ApiCall::WaitInputMte(const TPipe &tpipe, const ApiTensor *in, const Tensor &t, std::stringstream &ss) const { | 1272 | bool ApiCall::WaitInputMte(const TPipe &tpipe, const ApiTensor *in, const Tensor &t, std::stringstream &ss) const { |
| 1188 | // 1. load->store 2. load->store store 3. load->vec store store | 1273 | // 1. load->store 2. load->store store 3. load->vec store store |
| 1189 | if (this->type == Store::Type && | 1274 | if (this->type == Store::Type && |
| 1190 | - ((in->write->compute_type == ascir::ComputeType::kComputeLoad) && (in->write->type != Gather::Type)) && | 1275 | + ((in->write->compute_type == ascir::ComputeType::kComputeLoad) && (in->write->type != Gather::Type) && |
| 1276 | + (in->write->type != IndirectLoad::Type)) && | ||
| 1191 | IsUnitFirstRead(*this, *in)) { | 1277 | IsUnitFirstRead(*this, *in)) { |
| 1192 | ss << tpipe.SyncMte2ToMte3(t) << std::endl; | 1278 | ss << tpipe.SyncMte2ToMte3(t) << std::endl; |
| 1193 | } | 1279 | } |
| @@ -136,6 +136,7 @@ class ApiCall { | |||
| 136 | std::vector<const ApiTensor *> inputs; | 136 | std::vector<const ApiTensor *> inputs; |
| 137 | bool enable_cache{false}; | 137 | bool enable_cache{false}; |
| 138 | bool is_input_tbuf_contiguous = false; | 138 | bool is_input_tbuf_contiguous = false; |
| 139 | + bool skip_api_emit{false}; | ||
| 139 | std::string enable_cache_with_condition; | 140 | std::string enable_cache_with_condition; |
| 140 | // 用于标记Call节点执行状态 | 141 | // 用于标记Call节点执行状态 |
| 141 | // broadcast cache场景:在Call节点外生成控制条件 | 142 | // broadcast cache场景:在Call节点外生成控制条件 |
| @@ -16,11 +16,14 @@ | |||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | + | ||
| 19 | 20 | ||
| 20 | using namespace af::ascir_op; | 21 | using namespace af::ascir_op; |
| 21 | 22 | ||
| 22 | namespace optimize { | 23 | namespace optimize { |
| 23 | namespace { | 24 | namespace { |
| 25 | +constexpr int64_t kGatherSimtDcacheSize = 32 * 1024; | ||
| 26 | + | ||
| 24 | static Status GetNodeIrAttrOffset(const af::NodePtr &node, af::Expression &offset) { | 27 | static Status GetNodeIrAttrOffset(const af::NodePtr &node, af::Expression &offset) { |
| 25 | auto asc_node = std::dynamic_pointer_cast<af::AscNode>(node); | 28 | auto asc_node = std::dynamic_pointer_cast<af::AscNode>(node); |
| 26 | GE_ASSERT_NOTNULL(asc_node); | 29 | GE_ASSERT_NOTNULL(asc_node); |
| @@ -76,6 +79,7 @@ void CompleteSplitApiInfo(af::AscNodePtr &node) { | |||
| 76 | void CompleteGatherApiInfo(af::AscNodePtr &node) { | 79 | void CompleteGatherApiInfo(af::AscNodePtr &node) { |
| 77 | node->attr.api.type = af::ApiType::kAPITypeCompute; | 80 | node->attr.api.type = af::ApiType::kAPITypeCompute; |
| 78 | node->attr.api.unit = af::ComputeUnit::kUnitMTE2; | 81 | node->attr.api.unit = af::ComputeUnit::kUnitMTE2; |
| 82 | + (void)::ascir::SetDcacheSize(node, kGatherSimtDcacheSize); | ||
| 79 | } | 83 | } |
| 80 | 84 | ||
| 81 | void CompleteCubeApiInfo(af::AscNodePtr &node) { | 85 | void CompleteCubeApiInfo(af::AscNodePtr &node) { |
| @@ -154,6 +158,7 @@ static const std::map<std::string, af::ComputeType> kOpTypeToComputeType = { | |||
| 154 | {Sigmoid::Type, af::ComputeType::kComputeElewise}, | 158 | {Sigmoid::Type, af::ComputeType::kComputeElewise}, |
| 155 | {Concat::Type, af::ComputeType::kComputeConcat}, | 159 | {Concat::Type, af::ComputeType::kComputeConcat}, |
| 156 | {Gather::Type, af::ComputeType::kComputeGather}, | 160 | {Gather::Type, af::ComputeType::kComputeGather}, |
| 161 | + {IndirectLoad::Type, af::ComputeType::kComputeLoad}, | ||
| 157 | 162 | ||
| 158 | {Where::Type, af::ComputeType::kComputeElewise}, | 163 | {Where::Type, af::ComputeType::kComputeElewise}, |
| 159 | {Select::Type, af::ComputeType::kComputeElewise}, | 164 | {Select::Type, af::ComputeType::kComputeElewise}, |
| @@ -0,0 +1,243 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +namespace ascgen_utils::indirect_load { | ||
| 21 | +namespace { | ||
| 22 | +constexpr char kTemplateOuterAxisAttr[] = "af.internal.indirect_load.outer_axis"; | ||
| 23 | +constexpr char kTemplateInnerAxisAttr[] = "af.internal.indirect_load.inner_axis"; | ||
| 24 | +constexpr char kTemplateInputInnerAxisAttr[] = "af.internal.indirect_load.input_inner_axis"; | ||
| 25 | +constexpr char kTemplateLogicalViewAttr[] = "af.internal.indirect_load.logical_view"; | ||
| 26 | + | ||
| 27 | +bool IsValidLogicalTensorView(const LogicalTensorView &view) { | ||
| 28 | + return !view.axis_ids.empty() && view.axis_ids.size() == view.strides.size(); | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +TemplateRole GetAnnotatedTemplateRole(const af::AscNodePtr &node) { | ||
| 32 | + if (node == nullptr) { | ||
| 33 | + return TemplateRole::kNone; | ||
| 34 | + } | ||
| 35 | + return static_cast<TemplateRole>(::ascir::GetTemplateRoleOrDefault(*node, static_cast<int64_t>(TemplateRole::kNone))); | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +TemplateBehavior GetBehavior(TemplateRole role) { | ||
| 39 | + TemplateBehavior behavior; | ||
| 40 | + switch (role) { | ||
| 41 | + case TemplateRole::kSimdInputPre: | ||
| 42 | + behavior.skips_main_schedule_tiling = true; | ||
| 43 | + behavior.skips_api_emit = true; | ||
| 44 | + behavior.preserves_vectorized_axis = true; | ||
| 45 | + break; | ||
| 46 | + case TemplateRole::kSimdOp: | ||
| 47 | + break; | ||
| 48 | + case TemplateRole::kSimtInputBoundary: | ||
| 49 | + behavior.skips_main_schedule_tiling = true; | ||
| 50 | + behavior.skips_api_emit = true; | ||
| 51 | + behavior.uses_direct_gm_pipeline = true; | ||
| 52 | + behavior.skips_ub_expr = true; | ||
| 53 | + behavior.preserves_vectorized_axis = true; | ||
| 54 | + break; | ||
| 55 | + case TemplateRole::kSimtDirectGmBoundary: | ||
| 56 | + case TemplateRole::kSimtInlineTransform: | ||
| 57 | + behavior.skips_api_emit = true; | ||
| 58 | + behavior.uses_direct_gm_pipeline = true; | ||
| 59 | + behavior.skips_ub_expr = true; | ||
| 60 | + behavior.preserves_vectorized_axis = true; | ||
| 61 | + break; | ||
| 62 | + case TemplateRole::kSimtOp: | ||
| 63 | + behavior.uses_direct_gm_pipeline = true; | ||
| 64 | + behavior.skips_ub_lifecycle = true; | ||
| 65 | + behavior.skips_ub_expr = true; | ||
| 66 | + behavior.preserves_vectorized_axis = true; | ||
| 67 | + break; | ||
| 68 | + case TemplateRole::kNone: | ||
| 69 | + break; | ||
| 70 | + } | ||
| 71 | + return behavior; | ||
| 72 | +} | ||
| 73 | +} // namespace | ||
| 74 | + | ||
| 75 | +TemplateRole GetTemplateRole(const af::AscNodePtr &node) { | ||
| 76 | + return GetAnnotatedTemplateRole(node); | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +TemplateBehavior GetTemplateBehavior(const af::AscNodePtr &node) { | ||
| 80 | + return GetBehavior(GetTemplateRole(node)); | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +af::Status InheritTemplateRoleIfIL(af::AscGraph &graph, const std::string &vf_node_name, const af::AscNodePtr &src) { | ||
| 84 | + GE_ASSERT_NOTNULL(src); | ||
| 85 | + const auto &behavior = GetTemplateBehavior(src); | ||
| 86 | + if (!behavior.skips_api_emit && !behavior.uses_direct_gm_pipeline) { | ||
| 87 | + return af::SUCCESS; | ||
| 88 | + } | ||
| 89 | + auto vf_node = graph.FindNode(vf_node_name.c_str()); | ||
| 90 | + GE_ASSERT_NOTNULL(vf_node, "IndirectLoad: cannot find new VectorFunc node %s.", vf_node_name.c_str()); | ||
| 91 | + GE_ASSERT_SUCCESS(::ascir::SetTemplateRole(vf_node, ::ascir::GetTemplateRoleOrDefault(*src))); | ||
| 92 | + return af::SUCCESS; | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +af::Status SetTemplateRole(const af::AscNodePtr &node, TemplateRole role) { | ||
| 96 | + return ::ascir::SetTemplateRole(node, static_cast<int64_t>(role)); | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +af::Status SetTemplateAxes(const af::AscNodePtr &node, const TemplateAxes &axes) { | ||
| 100 | + GE_ASSERT_NOTNULL(node); | ||
| 101 | + auto op_desc = node->GetOpDesc(); | ||
| 102 | + GE_ASSERT_NOTNULL(op_desc); | ||
| 103 | + GE_ASSERT_TRUE(op_desc->SetExtAttr(kTemplateOuterAxisAttr, static_cast<int64_t>(axes.outer_axis)), | ||
| 104 | + "Set IndirectLoad outer axis failed, node = %s", node->GetNamePtr()); | ||
| 105 | + GE_ASSERT_TRUE(op_desc->SetExtAttr(kTemplateInnerAxisAttr, static_cast<int64_t>(axes.inner_axis)), | ||
| 106 | + "Set IndirectLoad inner axis failed, node = %s", node->GetNamePtr()); | ||
| 107 | + GE_ASSERT_TRUE(op_desc->SetExtAttr(kTemplateInputInnerAxisAttr, static_cast<int64_t>(axes.input_inner_axis)), | ||
| 108 | + "Set IndirectLoad input inner axis failed, node = %s", node->GetNamePtr()); | ||
| 109 | + return af::SUCCESS; | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +af::Status GetTemplateAxes(const af::AscNodePtr &node, TemplateAxes &axes) { | ||
| 113 | + GE_ASSERT_NOTNULL(node); | ||
| 114 | + auto op_desc = node->GetOpDesc(); | ||
| 115 | + GE_ASSERT_NOTNULL(op_desc); | ||
| 116 | + axes.outer_axis = op_desc->TryGetExtAttr(kTemplateOuterAxisAttr, static_cast<int64_t>(af::kIdNone)); | ||
| 117 | + axes.inner_axis = op_desc->TryGetExtAttr(kTemplateInnerAxisAttr, static_cast<int64_t>(af::kIdNone)); | ||
| 118 | + axes.input_inner_axis = op_desc->TryGetExtAttr(kTemplateInputInnerAxisAttr, static_cast<int64_t>(af::kIdNone)); | ||
| 119 | + GE_ASSERT_TRUE(axes.outer_axis != af::kIdNone, "IndirectLoad template axes are missing, node = %s", | ||
| 120 | + node->GetNamePtr()); | ||
| 121 | + return af::SUCCESS; | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +af::Status SetTemplateLogicalView(const af::AscNodePtr &node, const TemplateLogicalView &view) { | ||
| 125 | + GE_ASSERT_NOTNULL(node); | ||
| 126 | + GE_ASSERT_TRUE(IsValidLogicalTensorView(view.data) && IsValidLogicalTensorView(view.index) && | ||
| 127 | + IsValidLogicalTensorView(view.output), | ||
| 128 | + "IndirectLoad logical view is invalid, node = %s", node->GetNamePtr()); | ||
| 129 | + auto op_desc = node->GetOpDesc(); | ||
| 130 | + GE_ASSERT_NOTNULL(op_desc); | ||
| 131 | + GE_ASSERT_TRUE(op_desc->SetExtAttr(kTemplateLogicalViewAttr, view), "Set IndirectLoad logical view failed, node = %s", | ||
| 132 | + node->GetNamePtr()); | ||
| 133 | + return af::SUCCESS; | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +af::Status GetTemplateLogicalView(const af::AscNodePtr &node, TemplateLogicalView &view) { | ||
| 137 | + GE_ASSERT_NOTNULL(node); | ||
| 138 | + auto op_desc = node->GetOpDesc(); | ||
| 139 | + GE_ASSERT_NOTNULL(op_desc); | ||
| 140 | + view = op_desc->TryGetExtAttr(kTemplateLogicalViewAttr, TemplateLogicalView{}); | ||
| 141 | + GE_ASSERT_TRUE(IsValidLogicalTensorView(view.data) && IsValidLogicalTensorView(view.index) && | ||
| 142 | + IsValidLogicalTensorView(view.output), | ||
| 143 | + "IndirectLoad logical view is missing or invalid, node = %s", node->GetNamePtr()); | ||
| 144 | + return af::SUCCESS; | ||
| 145 | +} | ||
| 146 | + | ||
| 147 | +bool ShouldSkipMainScheduleTiling(const af::AscNodePtr &node) { | ||
| 148 | + return GetTemplateBehavior(node).skips_main_schedule_tiling; | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +bool ShouldPreserveVectorizedAxis(const af::AscNodePtr &node) { | ||
| 152 | + return GetTemplateBehavior(node).preserves_vectorized_axis; | ||
| 153 | +} | ||
| 154 | + | ||
| 155 | +bool ShouldApplyInputInnerVectorization(const af::AscNodePtr &node) { | ||
| 156 | + return GetTemplateRole(node) == TemplateRole::kSimdInputPre; | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +bool ShouldDisableRegularVectorFunc(const af::AscNodePtr &node) { | ||
| 160 | + const TemplateRole role = GetTemplateRole(node); | ||
| 161 | + return role == TemplateRole::kSimtInputBoundary || role == TemplateRole::kSimtDirectGmBoundary || | ||
| 162 | + role == TemplateRole::kSimtInlineTransform || role == TemplateRole::kSimtOp; | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +af::AscNodePtr GetInputProducer(const af::AscNodePtr &node, size_t input_index) { | ||
| 166 | + auto input_anchor = node == nullptr ? nullptr : node->GetInDataAnchor(input_index); | ||
| 167 | + if (input_anchor == nullptr || input_anchor->GetPeerOutAnchor() == nullptr) { | ||
| 168 | + return nullptr; | ||
| 169 | + } | ||
| 170 | + return std::dynamic_pointer_cast<af::AscNode>(input_anchor->GetPeerOutAnchor()->GetOwnerNode()); | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +af::AscNodePtr GetOnlyOutputConsumer(const af::AscNodePtr &node) { | ||
| 174 | + if (node == nullptr || node->GetOutDataNodesSize() != 1UL) { | ||
| 175 | + return nullptr; | ||
| 176 | + } | ||
| 177 | + return std::dynamic_pointer_cast<af::AscNode>(*node->GetOutDataNodes().begin()); | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +af::AscNodePtr FindIndirectLoadNode(const af::AscGraph &graph) { | ||
| 181 | + for (const af::AscNodePtr &node : graph.GetAllNodes()) { | ||
| 182 | + if (af::ops::IsOps<af::ascir_op::IndirectLoad>(node)) { | ||
| 183 | + return node; | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + return nullptr; | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +af::Status ValidateSingleIndirectLoadNode(const af::AscGraph &graph, af::AscNodePtr &node) { | ||
| 190 | + node = nullptr; | ||
| 191 | + for (const af::AscNodePtr &candidate : graph.GetAllNodes()) { | ||
| 192 | + if (!af::ops::IsOps<af::ascir_op::IndirectLoad>(candidate)) { | ||
| 193 | + continue; | ||
| 194 | + } | ||
| 195 | + if (node != nullptr) { | ||
| 196 | + GELOGE(af::FAILED, "[IndirectLoad] Graph[%s] contains multiple IndirectLoad nodes, first[%s], next[%s].", | ||
| 197 | + graph.GetName().c_str(), node->GetNamePtr(), candidate->GetNamePtr()); | ||
| 198 | + } | ||
| 199 | + GE_ASSERT_TRUE(node == nullptr, "Graph contains multiple IndirectLoad nodes, only one is supported."); | ||
| 200 | + node = candidate; | ||
| 201 | + } | ||
| 202 | + if (node != nullptr) { | ||
| 203 | + GELOGD("[IndirectLoad] Graph[%s] found IndirectLoad node[%s].", graph.GetName().c_str(), node->GetNamePtr()); | ||
| 204 | + } | ||
| 205 | + return af::SUCCESS; | ||
| 206 | +} | ||
| 207 | + | ||
| 208 | +af::Status GetPrebuiltYTilingCase(const af::AscGraph &graph, bool &has_case, af::AxisId &tile_id, | ||
| 209 | + std::pair<af::AxisPtr, af::AxisPtr> &tiling) { | ||
| 210 | + has_case = false; | ||
| 211 | + tile_id = af::kIdNone; | ||
| 212 | + tiling = {nullptr, nullptr}; | ||
| 213 | + | ||
| 214 | + const af::AscNodePtr indirect_load = FindIndirectLoadNode(graph); | ||
| 215 | + if (indirect_load == nullptr) { | ||
| 216 | + return af::SUCCESS; | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + TemplateAxes axes; | ||
| 220 | + GE_ASSERT_SUCCESS(GetTemplateAxes(indirect_load, axes), "[IndirectLoad] Failed to get template axes for node[%s].", | ||
| 221 | + indirect_load->GetNamePtr()); | ||
| 222 | + | ||
| 223 | + for (const auto &axis : graph.GetAllAxis()) { | ||
| 224 | + if (axis == nullptr || axis->type != af::Axis::Type::kAxisTypeTileOuter || axis->from.size() != 1UL || | ||
| 225 | + axis->from[0] != axes.outer_axis) { | ||
| 226 | + continue; | ||
| 227 | + } | ||
| 228 | + GE_ASSERT_TRUE( | ||
| 229 | + axis->split_pair_other_id >= 0L && static_cast<size_t>(axis->split_pair_other_id) < graph.GetAllAxis().size(), | ||
| 230 | + "[IndirectLoad] Invalid split pair axis[%ld] for graph[%s].", axis->split_pair_other_id, | ||
| 231 | + graph.GetName().c_str()); | ||
| 232 | + tiling.first = axis; | ||
| 233 | + tiling.second = graph.GetAllAxis()[static_cast<size_t>(axis->split_pair_other_id)]; | ||
| 234 | + has_case = true; | ||
| 235 | + tile_id = axes.outer_axis; | ||
| 236 | + return af::SUCCESS; | ||
| 237 | + } | ||
| 238 | + GELOGE(af::FAILED, "[IndirectLoad] Template fixed split is missing for axis[%ld] in graph[%s].", axes.outer_axis, | ||
| 239 | + graph.GetName().c_str()); | ||
| 240 | + return af::FAILED; | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +} // namespace ascgen_utils::indirect_load | ||
| @@ -0,0 +1,77 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +namespace ascgen_utils::indirect_load { | ||
| 21 | +enum class TemplateRole : int64_t { | ||
| 22 | + kNone, | ||
| 23 | + kSimdInputPre, | ||
| 24 | + kSimdOp, | ||
| 25 | + kSimtInputBoundary, | ||
| 26 | + kSimtDirectGmBoundary, | ||
| 27 | + kSimtInlineTransform, | ||
| 28 | + kSimtOp, | ||
| 29 | +}; | ||
| 30 | + | ||
| 31 | +struct TemplateBehavior { | ||
| 32 | + bool skips_main_schedule_tiling = false; | ||
| 33 | + bool skips_api_emit = false; | ||
| 34 | + bool uses_direct_gm_pipeline = false; | ||
| 35 | + bool skips_ub_lifecycle = false; | ||
| 36 | + bool skips_ub_expr = false; | ||
| 37 | + bool preserves_vectorized_axis = false; | ||
| 38 | +}; | ||
| 39 | + | ||
| 40 | +struct TemplateAxes { | ||
| 41 | + af::AxisId outer_axis = af::kIdNone; | ||
| 42 | + af::AxisId inner_axis = af::kIdNone; | ||
| 43 | + af::AxisId input_inner_axis = af::kIdNone; | ||
| 44 | +}; | ||
| 45 | + | ||
| 46 | +struct LogicalTensorView { | ||
| 47 | + std::vector<af::AxisId> axis_ids; | ||
| 48 | + std::vector<af::Expression> strides; | ||
| 49 | +}; | ||
| 50 | + | ||
| 51 | +struct TemplateLogicalView { | ||
| 52 | + LogicalTensorView data; | ||
| 53 | + LogicalTensorView index; | ||
| 54 | + LogicalTensorView output; | ||
| 55 | +}; | ||
| 56 | + | ||
| 57 | +TemplateBehavior GetTemplateBehavior(const af::AscNodePtr &node); | ||
| 58 | +TemplateRole GetTemplateRole(const af::AscNodePtr &node); | ||
| 59 | +af::Status InheritTemplateRoleIfIL(af::AscGraph &graph, const std::string &vf_node_name, const af::AscNodePtr &src); | ||
| 60 | +af::Status SetTemplateRole(const af::AscNodePtr &node, TemplateRole role); | ||
| 61 | +af::Status SetTemplateAxes(const af::AscNodePtr &node, const TemplateAxes &axes); | ||
| 62 | +af::Status GetTemplateAxes(const af::AscNodePtr &node, TemplateAxes &axes); | ||
| 63 | +af::Status SetTemplateLogicalView(const af::AscNodePtr &node, const TemplateLogicalView &view); | ||
| 64 | +af::Status GetTemplateLogicalView(const af::AscNodePtr &node, TemplateLogicalView &view); | ||
| 65 | +bool ShouldSkipMainScheduleTiling(const af::AscNodePtr &node); | ||
| 66 | +bool ShouldPreserveVectorizedAxis(const af::AscNodePtr &node); | ||
| 67 | +bool ShouldApplyInputInnerVectorization(const af::AscNodePtr &node); | ||
| 68 | +bool ShouldDisableRegularVectorFunc(const af::AscNodePtr &node); | ||
| 69 | +af::AscNodePtr GetInputProducer(const af::AscNodePtr &node, size_t input_index); | ||
| 70 | +af::AscNodePtr GetOnlyOutputConsumer(const af::AscNodePtr &node); | ||
| 71 | +af::AscNodePtr FindIndirectLoadNode(const af::AscGraph &graph); | ||
| 72 | +af::Status ValidateSingleIndirectLoadNode(const af::AscGraph &graph, af::AscNodePtr &node); | ||
| 73 | +af::Status GetPrebuiltYTilingCase(const af::AscGraph &graph, bool &has_case, af::AxisId &tile_id, | ||
| 74 | + std::pair<af::AxisPtr, af::AxisPtr> &tiling); | ||
| 75 | +} // namespace ascgen_utils::indirect_load | ||
| 76 | + | ||
| 77 | + | ||
| @@ -12,6 +12,13 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +namespace { | ||
| 18 | +constexpr char kTemplateIdAttr[] = "af.internal.template.id"; | ||
| 19 | +constexpr char kTemplateRoleAttr[] = "af.internal.indirect_load.role"; | ||
| 20 | +constexpr char kDcacheSizeAttr[] = "af.internal.template.dcache_size"; | ||
| 21 | +} // namespace | ||
| 15 | 22 | ||
| 16 | namespace ascir { | 23 | namespace ascir { |
| 17 | struct ScheduleGroup { | 24 | struct ScheduleGroup { |
| @@ -46,6 +53,60 @@ struct FusedScheduledResult { | |||
| 46 | std::vector<af::Expression> origin_vars; | 53 | std::vector<af::Expression> origin_vars; |
| 47 | std::vector<std::vector<ScheduledResult>> node_idx_to_scheduled_results; | 54 | std::vector<std::vector<ScheduledResult>> node_idx_to_scheduled_results; |
| 48 | }; | 55 | }; |
| 56 | + | ||
| 57 | +enum class TemplateId : int64_t { | ||
| 58 | + kDefault = -1, | ||
| 59 | + kIndirectLoadSimd = 0, | ||
| 60 | + kIndirectLoadSimt = 1, | ||
| 61 | +}; | ||
| 62 | + | ||
| 63 | +inline af::Status SetTemplateId(const af::AscNodePtr &node, TemplateId template_id) { | ||
| 64 | + GE_ASSERT_NOTNULL(node); | ||
| 65 | + auto op_desc = node->GetOpDesc(); | ||
| 66 | + GE_ASSERT_NOTNULL(op_desc); | ||
| 67 | + GE_ASSERT_TRUE(op_desc->SetExtAttr(kTemplateIdAttr, static_cast<int64_t>(template_id)), | ||
| 68 | + "Set internal template id failed, node = %s", node->GetNamePtr()); | ||
| 69 | + return af::SUCCESS; | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +inline af::Status SetTemplateRole(const af::AscNodePtr &node, int64_t role) { | ||
| 73 | + GE_ASSERT_NOTNULL(node); | ||
| 74 | + auto op_desc = node->GetOpDesc(); | ||
| 75 | + GE_ASSERT_NOTNULL(op_desc); | ||
| 76 | + GE_ASSERT_TRUE(op_desc->SetExtAttr(kTemplateRoleAttr, role), "Set internal template role failed, node = %s", | ||
| 77 | + node->GetNamePtr()); | ||
| 78 | + return af::SUCCESS; | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +inline int64_t GetTemplateRoleOrDefault(const af::AscNode &node, int64_t default_role = -1) { | ||
| 82 | + if (node.GetOpDesc() == nullptr) { | ||
| 83 | + return default_role; | ||
| 84 | + } | ||
| 85 | + return node.GetOpDesc()->TryGetExtAttr(kTemplateRoleAttr, default_role); | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +inline TemplateId GetTemplateIdOrDefault(const af::AscNode &node, TemplateId default_id = TemplateId::kDefault) { | ||
| 89 | + if (node.GetOpDesc() == nullptr) { | ||
| 90 | + return default_id; | ||
| 91 | + } | ||
| 92 | + return static_cast<TemplateId>(node.GetOpDesc()->TryGetExtAttr(kTemplateIdAttr, static_cast<int64_t>(default_id))); | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +inline af::Status SetDcacheSize(const af::AscNodePtr &node, int64_t dcache_size) { | ||
| 96 | + GE_ASSERT_NOTNULL(node); | ||
| 97 | + auto op_desc = node->GetOpDesc(); | ||
| 98 | + GE_ASSERT_NOTNULL(op_desc); | ||
| 99 | + GE_ASSERT_TRUE(op_desc->SetExtAttr(kDcacheSizeAttr, dcache_size), | ||
| 100 | + "Set internal template dcache size failed, node = %s", node->GetNamePtr()); | ||
| 101 | + return af::SUCCESS; | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +inline int64_t GetDcacheSize(const af::AscNode &node) { | ||
| 105 | + if (node.GetOpDesc() == nullptr) { | ||
| 106 | + return 0; | ||
| 107 | + } | ||
| 108 | + return node.GetOpDesc()->TryGetExtAttr(kDcacheSizeAttr, int64_t{0}); | ||
| 109 | +} | ||
| 49 | } // namespace ascir | 110 | } // namespace ascir |
| 50 | 111 | ||
| 51 | 112 | ||
| @@ -17,11 +17,12 @@ | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | + | ||
| 21 | + | ||
| 20 | 22 | ||
| 21 | namespace ascir { | 23 | namespace ascir { |
| 22 | namespace { | 24 | namespace { |
| 23 | constexpr int64_t kMinTmpBufferSize = 8 * 1024; | 25 | constexpr int64_t kMinTmpBufferSize = 8 * 1024; |
| 24 | -constexpr int64_t kSimtDcacheSize = 32 * 1024; | ||
| 25 | constexpr int64_t kBlockAlignBytes = 32; | 26 | constexpr int64_t kBlockAlignBytes = 32; |
| 26 | 27 | ||
| 27 | std::string MakeQueueName(int64_t id) { | 28 | std::string MakeQueueName(int64_t id) { |
| @@ -220,8 +221,9 @@ UbExpr CalcReservedUbSize(const af::AscGraph &graph) { | |||
| 220 | UbExpr reserved_ub_size = af::Symbol(ascgen_utils::CalcReservedTmpBufSizeForAscGraph(graph)); | 221 | UbExpr reserved_ub_size = af::Symbol(ascgen_utils::CalcReservedTmpBufSizeForAscGraph(graph)); |
| 221 | for (const auto &node : graph.GetAllNodes()) { | 222 | for (const auto &node : graph.GetAllNodes()) { |
| 222 | GE_ASSERT_NOTNULL(node); | 223 | GE_ASSERT_NOTNULL(node); |
| 223 | - if (node->GetType() == af::ascir_op::Gather::Type) { | 224 | + const int64_t simt_dcache_size = ::ascir::GetDcacheSize(*node); |
| 224 | - reserved_ub_size = reserved_ub_size + af::Symbol(kSimtDcacheSize); | 225 | + if (simt_dcache_size > 0) { |
| 226 | + reserved_ub_size = reserved_ub_size + af::Symbol(simt_dcache_size); | ||
| 225 | break; | 227 | break; |
| 226 | } | 228 | } |
| 227 | } | 229 | } |
| @@ -305,6 +307,9 @@ af::Status AscGraphUbExprBuilder::Build(const af::AscGraph &graph, UbExprContext | |||
| 305 | std::map<int64_t, ContainerState> buffer_bytes; | 307 | std::map<int64_t, ContainerState> buffer_bytes; |
| 306 | for (const auto &node : graph.GetAllNodes()) { | 308 | for (const auto &node : graph.GetAllNodes()) { |
| 307 | GE_ASSERT_NOTNULL(node); | 309 | GE_ASSERT_NOTNULL(node); |
| 310 | + if (ascgen_utils::indirect_load::GetTemplateBehavior(node).skips_ub_expr) { | ||
| 311 | + continue; | ||
| 312 | + } | ||
| 308 | for (const auto &output : node->outputs()) { | 313 | for (const auto &output : node->outputs()) { |
| 309 | AddTensor(graph, output->attr, queue_bytes, buffer_bytes); | 314 | AddTensor(graph, output->attr, queue_bytes, buffer_bytes); |
| 310 | } | 315 | } |
| @@ -52,6 +52,8 @@ inline constexpr char kScalarDataOpType[] = "ScalarData"; | |||
| 52 | inline constexpr char kAscGraphAttr[] = "ascgraph"; | 52 | inline constexpr char kAscGraphAttr[] = "ascgraph"; |
| 53 | inline constexpr char kNegativeSlopeAttr[] = "negative_slope"; | 53 | inline constexpr char kNegativeSlopeAttr[] = "negative_slope"; |
| 54 | inline constexpr char kNegativeIndexSupportAttr[] = "negative_index_support"; | 54 | inline constexpr char kNegativeIndexSupportAttr[] = "negative_index_support"; |
| 55 | +inline constexpr char kNeedCheckBoundAttr[] = "need_check_bound"; | ||
| 56 | +inline constexpr char kMaxAttr[] = "max"; | ||
| 55 | inline constexpr char kAlphaAttr[] = "alpha"; | 57 | inline constexpr char kAlphaAttr[] = "alpha"; |
| 56 | inline constexpr char kNAttr[] = "n"; | 58 | inline constexpr char kNAttr[] = "n"; |
| 57 | inline constexpr char kErrorMsgAttr[] = "error_msg"; | 59 | inline constexpr char kErrorMsgAttr[] = "error_msg"; |
| @@ -1150,6 +1152,14 @@ DEFINE_IR_ATTR_ACCESSORS(IndexExpr, AscIndexExprIrAttrDef, kExprAttr, int64_t, P | |||
| 1150 | PyLong_AsLong, SetExpr, GetExpr) | 1152 | PyLong_AsLong, SetExpr, GetExpr) |
| 1151 | DEFINE_IR_ATTR_ACCESSORS(Gather, AscGatherIrAttrDef, kAxisAttr, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, | 1153 | DEFINE_IR_ATTR_ACCESSORS(Gather, AscGatherIrAttrDef, kAxisAttr, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, |
| 1152 | SetAxis, GetAxis) | 1154 | SetAxis, GetAxis) |
| 1155 | +DEFINE_IR_ATTR_ACCESSORS(IndirectLoad, AscIndirectLoadIrAttrDef, kAxisAttr, int64_t, PyLong_Check, PyLong_FromLong, | ||
| 1156 | + PyLong_AsLong, SetAxis, GetAxis) | ||
| 1157 | +DEFINE_IR_ATTR_ACCESSORS(IndirectLoad, AscIndirectLoadIrAttrDef, kNegativeIndexSupportAttr, bool, PyBool_Check, | ||
| 1158 | + PyBool_FromLong, PyObject_IsTrue, SetNegative_index_support, GetNegative_index_support) | ||
| 1159 | +DEFINE_IR_ATTR_ACCESSORS(IndirectLoad, AscIndirectLoadIrAttrDef, kNeedCheckBoundAttr, bool, PyBool_Check, | ||
| 1160 | + PyBool_FromLong, PyObject_IsTrue, SetNeed_check_bound, GetNeed_check_bound) | ||
| 1161 | +DEFINE_IR_ATTR_ACCESSORS(IndirectLoad, AscIndirectLoadIrAttrDef, kMaxAttr, int64_t, PyLong_Check, PyLong_FromLong, | ||
| 1162 | + PyLong_AsLong, SetMax, GetMax) | ||
| 1153 | DEFINE_IR_ATTR_ACCESSORS(MatMul, AscMatMulIrAttrDef, kHasRelu, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, | 1163 | DEFINE_IR_ATTR_ACCESSORS(MatMul, AscMatMulIrAttrDef, kHasRelu, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, |
| 1154 | SetHas_relu, GetHas_relu) | 1164 | SetHas_relu, GetHas_relu) |
| 1155 | DEFINE_IR_ATTR_ACCESSORS(MatMul, AscMatMulIrAttrDef, kTransposeX1, int64_t, PyLong_Check, PyLong_FromLong, | 1165 | DEFINE_IR_ATTR_ACCESSORS(MatMul, AscMatMulIrAttrDef, kTransposeX1, int64_t, PyLong_Check, PyLong_FromLong, |
| @@ -1287,6 +1297,8 @@ const std::map<std::string, typename IrAttr<OpType>::handler> IrAttr<OpType>::at | |||
| 1287 | {"Load", AutoRegAttrHandle<af::ascir_op::Load, kOffsetAttr>::RegHandle}, | 1297 | {"Load", AutoRegAttrHandle<af::ascir_op::Load, kOffsetAttr>::RegHandle}, |
| 1288 | {"Store", AutoRegAttrHandle<af::ascir_op::Store, kOffsetAttr>::RegHandle}, | 1298 | {"Store", AutoRegAttrHandle<af::ascir_op::Store, kOffsetAttr>::RegHandle}, |
| 1289 | {"Gather", AutoRegAttrHandle<af::ascir_op::Gather, kAxisAttr>::RegHandle}, | 1299 | {"Gather", AutoRegAttrHandle<af::ascir_op::Gather, kAxisAttr>::RegHandle}, |
| 1300 | + {"IndirectLoad", AutoRegAttrHandle<af::ascir_op::IndirectLoad, kAxisAttr, kNegativeIndexSupportAttr, | ||
| 1301 | + kNeedCheckBoundAttr, kMaxAttr>::RegHandle}, | ||
| 1290 | {"MatMul", | 1302 | {"MatMul", |
| 1291 | AutoRegAttrHandle<af::ascir_op::MatMul, kHasRelu, kOffsetX, kTransposeX1, kTransposeX2, kEnableHf32>::RegHandle}, | 1303 | AutoRegAttrHandle<af::ascir_op::MatMul, kHasRelu, kOffsetX, kTransposeX1, kTransposeX2, kEnableHf32>::RegHandle}, |
| 1292 | {"MatMulBias", AutoRegAttrHandle<af::ascir_op::MatMulBias, kHasRelu, kOffsetX, kTransposeX1, kTransposeX2, | 1304 | {"MatMulBias", AutoRegAttrHandle<af::ascir_op::MatMulBias, kHasRelu, kOffsetX, kTransposeX1, kTransposeX2, |
| @@ -116,6 +116,7 @@ PyMODINIT_FUNC PyInit_pyautofuse(void); | |||
| 116 | OP(BatchMatMulBias) \ | 116 | OP(BatchMatMulBias) \ |
| 117 | OP(Where) \ | 117 | OP(Where) \ |
| 118 | OP(Gather) \ | 118 | OP(Gather) \ |
| 119 | + OP(IndirectLoad) \ | ||
| 119 | OP(Transpose) \ | 120 | OP(Transpose) \ |
| 120 | OP(BitwiseAnd) \ | 121 | OP(BitwiseAnd) \ |
| 121 | OP(Ln) \ | 122 | OP(Ln) \ |
| @@ -1837,6 +1837,30 @@ def Gather( | |||
| 1837 | return op.y | 1837 | return op.y |
| 1838 | 1838 | ||
| 1839 | 1839 | ||
| 1840 | +def IndirectLoad( | ||
| 1841 | + owner_graph: ascir.HintGraph, | ||
| 1842 | + x1: ascir.OpsOperatorOutput, | ||
| 1843 | + x2: ascir.OpsOperatorOutput, | ||
| 1844 | + *, | ||
| 1845 | + axis: int, | ||
| 1846 | + sched_axis: List[ascir.Axis], | ||
| 1847 | + size: Optional[List[ascir.SizeExpr]] = None, | ||
| 1848 | + stride: Optional[List[ascir.SizeExpr]] = None, | ||
| 1849 | +) -> ascir.OpsOperatorOutput: | ||
| 1850 | + name = _generate_op_name(owner_graph, "indirectload") | ||
| 1851 | + op = ascir.ops.IndirectLoad(name) | ||
| 1852 | + meta = _get_metadata(owner_graph) | ||
| 1853 | + meta.ops.append(op) | ||
| 1854 | + | ||
| 1855 | + op.attr.ir_attr.axis = axis | ||
| 1856 | + op.attr.sched.axis = sched_axis | ||
| 1857 | + op.x1 = x1 | ||
| 1858 | + op.x2 = x2 | ||
| 1859 | + _infer_or_set_view(op.y, sched_axis, size, stride) | ||
| 1860 | + op.infer_dtype() | ||
| 1861 | + return op.y | ||
| 1862 | + | ||
| 1863 | + | ||
| 1840 | def BitwiseAnd( | 1864 | def BitwiseAnd( |
| 1841 | owner_graph: ascir.HintGraph, | 1865 | owner_graph: ascir.HintGraph, |
| 1842 | x1: ascir.OpsOperatorOutput, | 1866 | x1: ascir.OpsOperatorOutput, |
| @@ -188,6 +188,19 @@ class AscIrCodegen { | |||
| 188 | (void)node; | 188 | (void)node; |
| 189 | return true; | 189 | return true; |
| 190 | } | 190 | } |
| 191 | + | ||
| 192 | + virtual bool IsSimtScalarSupported(const AscNode &node) const { | ||
| 193 | + (void)node; | ||
| 194 | + return false; | ||
| 195 | + } | ||
| 196 | + | ||
| 197 | + virtual ge::graphStatus GenerateSimtScalarExpr(const AscNode &node, const std::vector<std::string> &inputs, | ||
| 198 | + std::string &expr) const { | ||
| 199 | + (void)node; | ||
| 200 | + (void)inputs; | ||
| 201 | + (void)expr; | ||
| 202 | + return af::FAILED; | ||
| 203 | + } | ||
| 191 | }; | 204 | }; |
| 192 | 205 | ||
| 193 | class AscIrAtt { | 206 | class AscIrAtt { |
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | + | ||
| 13 | 14 | ||
| 14 | 15 | ||
| 15 | 16 | ||
| @@ -18,6 +19,7 @@ | |||
| 18 | 19 | ||
| 19 | 20 | ||
| 20 | 21 | ||
| 22 | + | ||
| 21 | 23 | ||
| 22 | 24 | ||
| 23 | 25 | ||
| @@ -179,6 +181,33 @@ void AppendIdIfNotDefault(std::stringstream &ss, const std::string &prefix, int6 | |||
| 179 | ss << prefix << id; | 181 | ss << prefix << id; |
| 180 | } | 182 | } |
| 181 | } | 183 | } |
| 184 | + | ||
| 185 | +// Returns true if an IndirectLoad prebuilt tiling case was generated, and the caller should return early. | ||
| 186 | +bool TryGenIndirectLoadTilingCase(ascir::ImplGraph &graph, | ||
| 187 | + std::vector<optimize::autoschedule::TilingCase> &tiling_cases) { | ||
| 188 | + bool has_indirect_load_case = false; | ||
| 189 | + af::AxisId indirect_load_tile_id = kDefaultAxisId; | ||
| 190 | + std::pair<af::AxisPtr, af::AxisPtr> indirect_load_tiling; | ||
| 191 | + if (ascgen_utils::indirect_load::GetPrebuiltYTilingCase(graph, has_indirect_load_case, indirect_load_tile_id, | ||
| 192 | + indirect_load_tiling) != af::SUCCESS) { | ||
| 193 | + GELOGE(af::FAILED, "[IndirectLoad] Failed to generate prebuilt tiling case for graph[%s].", | ||
| 194 | + graph.GetName().c_str()); | ||
| 195 | + return true; | ||
| 196 | + } | ||
| 197 | + if (!has_indirect_load_case) { | ||
| 198 | + return false; | ||
| 199 | + } | ||
| 200 | + optimize::autoschedule::TilingCase tiling_case; | ||
| 201 | + if (indirect_load_tile_id != kDefaultAxisId) { | ||
| 202 | + tiling_case.ub_tiling_id_y = indirect_load_tile_id; | ||
| 203 | + } | ||
| 204 | + tiling_case.ub_tiling_y = indirect_load_tiling; | ||
| 205 | + tiling_case.block_tiling_id = 0; | ||
| 206 | + tiling_cases.push_back(tiling_case); | ||
| 207 | + GELOGD("[IndirectLoad] Graph[%s] generate prebuilt outer tiling case for axis[%ld].", graph.GetName().c_str(), | ||
| 208 | + indirect_load_tile_id); | ||
| 209 | + return true; | ||
| 210 | +} | ||
| 182 | } // namespace | 211 | } // namespace |
| 183 | 212 | ||
| 184 | namespace optimize::autoschedule { | 213 | namespace optimize::autoschedule { |
| @@ -187,7 +216,8 @@ Status AutoSchedule::SelectLoopAxis(ascir::ImplGraph &impl_graph, bool is_reduce | |||
| 187 | for (auto node : impl_graph.GetAllNodes()) { | 216 | for (auto node : impl_graph.GetAllNodes()) { |
| 188 | GE_ASSERT_NOTNULL(node); | 217 | GE_ASSERT_NOTNULL(node); |
| 189 | node->attr.sched.loop_axis = af::kIdNone; | 218 | node->attr.sched.loop_axis = af::kIdNone; |
| 190 | - if (node->attr.api.type != af::ApiType::kAPITypeCompute) { | 219 | + const auto behavior = ascgen_utils::indirect_load::GetTemplateBehavior(node); |
| 220 | + if (node->attr.api.type != af::ApiType::kAPITypeCompute || behavior.skips_main_schedule_tiling) { | ||
| 191 | continue; | 221 | continue; |
| 192 | } | 222 | } |
| 193 | if (ScheduleUtils::IsReduce(node) && !is_reduce_fullload) { | 223 | if (ScheduleUtils::IsReduce(node) && !is_reduce_fullload) { |
| @@ -220,6 +250,10 @@ void AutoSchedule::GenTilingCase(std::vector<TilingCase> &tiling_cases) { | |||
| 220 | } | 250 | } |
| 221 | }; | 251 | }; |
| 222 | 252 | ||
| 253 | + if (TryGenIndirectLoadTilingCase(graph_, tiling_cases)) { | ||
| 254 | + return; | ||
| 255 | + } | ||
| 256 | + | ||
| 223 | if (cube_template_ != ascir::CubeTemplateType::kDefault) { | 257 | if (cube_template_ != ascir::CubeTemplateType::kDefault) { |
| 224 | for (const auto &y_id : axes_group_.y_group) { | 258 | for (const auto &y_id : axes_group_.y_group) { |
| 225 | TilingCase tiling_case; | 259 | TilingCase tiling_case; |
| @@ -305,6 +339,27 @@ static std::string GetTilingCaseStr(const std::string &graph_name, const TilingC | |||
| 305 | return ss.str(); | 339 | return ss.str(); |
| 306 | } | 340 | } |
| 307 | 341 | ||
| 342 | +Status BuildIndirectLoadAxisGroup(const ascir::ImplGraph &graph, const AxisGroup &base_group, AxisGroup &axis_group) { | ||
| 343 | + const af::AscNodePtr indirect_load = ascgen_utils::indirect_load::FindIndirectLoadNode(graph); | ||
| 344 | + if (indirect_load == nullptr) { | ||
| 345 | + axis_group = base_group; | ||
| 346 | + return af::SUCCESS; | ||
| 347 | + } | ||
| 348 | + | ||
| 349 | + ascgen_utils::indirect_load::TemplateAxes axes; | ||
| 350 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, axes)); | ||
| 351 | + axis_group = {}; | ||
| 352 | + axis_group.y_group.push_back(axes.outer_axis); | ||
| 353 | + if (axes.inner_axis != af::kIdNone) { | ||
| 354 | + axis_group.y_group.push_back(axes.inner_axis); | ||
| 355 | + } | ||
| 356 | + axis_group.axes_order.resize(axis_group.y_group.size()); | ||
| 357 | + std::iota(axis_group.axes_order.begin(), axis_group.axes_order.end(), 0UL); | ||
| 358 | + GELOGD("[IndirectLoad] Graph[%s] use template axis group[%s].", graph.GetName().c_str(), | ||
| 359 | + axis_group.ToString().c_str()); | ||
| 360 | + return af::SUCCESS; | ||
| 361 | +} | ||
| 362 | + | ||
| 308 | Status AutoSchedule::DoAutoSchedule() { | 363 | Status AutoSchedule::DoAutoSchedule() { |
| 309 | graph_.SetGraphType(af::AscGraphType::kImplGraph); | 364 | graph_.SetGraphType(af::AscGraphType::kImplGraph); |
| 310 | ReorderBroadcastAxesInner(graph_); | 365 | ReorderBroadcastAxesInner(graph_); |
| @@ -356,7 +411,11 @@ Status AutoSchedule::ProcessOneTilingCase(TilingCase &tiling_case, size_t index, | |||
| 356 | GE_ASSERT_TRUE(output.scheduled_graph.CopyFrom(graph_), "Failed to copy graph for tiling case %zu in graph: [%s]", | 411 | GE_ASSERT_TRUE(output.scheduled_graph.CopyFrom(graph_), "Failed to copy graph for tiling case %zu in graph: [%s]", |
| 357 | index, graph_.GetName().c_str()); | 412 | index, graph_.GetName().c_str()); |
| 358 | 413 | ||
| 359 | - Scheduler scheduler(output.scheduled_graph, axes_group_, tiling_case, is_last_axis_reduce, reduce_template_, | 414 | + AxisGroup axis_group; |
| 415 | + GE_CHK_STATUS_RET(BuildIndirectLoadAxisGroup(output.scheduled_graph, axes_group_, axis_group), | ||
| 416 | + "Failed to build scheduler axis group for tiling case %zu in graph: [%s]", index, | ||
| 417 | + graph_name.c_str()); | ||
| 418 | + Scheduler scheduler(output.scheduled_graph, axis_group, tiling_case, is_last_axis_reduce, reduce_template_, | ||
| 360 | cube_template_); | 419 | cube_template_); |
| 361 | 420 | ||
| 362 | auto ret = scheduler.DoScheduler(); | 421 | auto ret = scheduler.DoScheduler(); |
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | + | ||
| 14 | 15 | ||
| 15 | 16 | ||
| 16 | 17 | ||
| @@ -179,12 +180,6 @@ void AppendAxisOrder(const std::vector<size_t> &axes_order, size_t group_axis_si | |||
| 179 | axes_order.begin() + inner_axis_end_idx); | 180 | axes_order.begin() + inner_axis_end_idx); |
| 180 | } | 181 | } |
| 181 | 182 | ||
| 182 | -void AdjustAxisOrderOffsets(std::vector<size_t> &axes_order, size_t start_idx, size_t end_idx, size_t offset) { | ||
| 183 | - for (size_t i = start_idx; i < end_idx; ++i) { | ||
| 184 | - axes_order[i] += offset; | ||
| 185 | - } | ||
| 186 | -} | ||
| 187 | - | ||
| 188 | void GetOuterAxes(const std::vector<ascir::AxisId> &axes_group, const ascir::AxisId &ub_tiling_id, | 183 | void GetOuterAxes(const std::vector<ascir::AxisId> &axes_group, const ascir::AxisId &ub_tiling_id, |
| 189 | const ascir::Axis &ub_tiling_outer_axis, const std::vector<size_t> &axes_order, | 184 | const ascir::Axis &ub_tiling_outer_axis, const std::vector<size_t> &axes_order, |
| 190 | std::vector<ascir::AxisId> &outer_axes, std::vector<size_t> &outer_axes_index, | 185 | std::vector<ascir::AxisId> &outer_axes, std::vector<size_t> &outer_axes_index, |
| @@ -405,8 +400,25 @@ Status Scheduler::ApplyBlockSplitToNode(ascir::NodeView &node, bool is_store_aft | |||
| 405 | return af::SUCCESS; | 400 | return af::SUCCESS; |
| 406 | } | 401 | } |
| 407 | 402 | ||
| 403 | +void Scheduler::AdjustVectorizedAxesOrderOffsets(std::vector<size_t> &vectorized_axes_order, size_t split_point, | ||
| 404 | + size_t end, size_t offset) const { | ||
| 405 | + const size_t start_idx = is_last_axis_reduce_ ? split_point : 0; | ||
| 406 | + const size_t end_idx = is_last_axis_reduce_ ? end : split_point; | ||
| 407 | + for (size_t i = start_idx; i < end_idx; ++i) { | ||
| 408 | + vectorized_axes_order[i] += offset; | ||
| 409 | + } | ||
| 410 | +} | ||
| 411 | + | ||
| 408 | void Scheduler::FindVectorizedAxes(std::vector<ascir::AxisId> &vectorized_axes, | 412 | void Scheduler::FindVectorizedAxes(std::vector<ascir::AxisId> &vectorized_axes, |
| 409 | std::vector<size_t> &vectorized_axes_order) { | 413 | std::vector<size_t> &vectorized_axes_order) { |
| 414 | + if (is_indirect_load_schedule_case_) { | ||
| 415 | + if (indirect_load_axes_.inner_axis != af::kIdNone) { | ||
| 416 | + vectorized_axes.push_back(indirect_load_axes_.inner_axis); | ||
| 417 | + vectorized_axes_order.push_back(0UL); | ||
| 418 | + } | ||
| 419 | + return; | ||
| 420 | + } | ||
| 421 | + | ||
| 410 | size_t last_ub_size = 0UL; | 422 | size_t last_ub_size = 0UL; |
| 411 | size_t group_axis_size = 0UL; | 423 | size_t group_axis_size = 0UL; |
| 412 | const auto &axes_order = axes_group_.axes_order; | 424 | const auto &axes_order = axes_group_.axes_order; |
| @@ -436,11 +448,7 @@ void Scheduler::FindVectorizedAxes(std::vector<ascir::AxisId> &vectorized_axes, | |||
| 436 | 448 | ||
| 437 | // 带reduce 需要在ub内确保向量化轴是RA或者AR排布 | 449 | // 带reduce 需要在ub内确保向量化轴是RA或者AR排布 |
| 438 | const size_t offset = axes_order.size() + axes_group_.n_group.size(); | 450 | const size_t offset = axes_order.size() + axes_group_.n_group.size(); |
| 439 | - if (is_last_axis_reduce_) { | 451 | + AdjustVectorizedAxesOrderOffsets(vectorized_axes_order, prev_ub_size, current_ub_size, offset); |
| 440 | - AdjustAxisOrderOffsets(vectorized_axes_order, prev_ub_size, current_ub_size, offset); | ||
| 441 | - } else { | ||
| 442 | - AdjustAxisOrderOffsets(vectorized_axes_order, 0, prev_ub_size, offset); | ||
| 443 | - } | ||
| 444 | last_ub_size = current_ub_size; | 452 | last_ub_size = current_ub_size; |
| 445 | } | 453 | } |
| 446 | 454 | ||
| @@ -453,11 +461,7 @@ void Scheduler::FindVectorizedAxes(std::vector<ascir::AxisId> &vectorized_axes, | |||
| 453 | } | 461 | } |
| 454 | 462 | ||
| 455 | const size_t offset = axes_order.size() + vectorized_axes.size(); | 463 | const size_t offset = axes_order.size() + vectorized_axes.size(); |
| 456 | - if (is_last_axis_reduce_) { | 464 | + AdjustVectorizedAxesOrderOffsets(vectorized_axes_order, non_reduce_axis_size, vectorized_axes.size(), offset); |
| 457 | - AdjustAxisOrderOffsets(vectorized_axes_order, non_reduce_axis_size, vectorized_axes.size(), offset); | ||
| 458 | - } else { | ||
| 459 | - AdjustAxisOrderOffsets(vectorized_axes_order, 0, non_reduce_axis_size, offset); | ||
| 460 | - } | ||
| 461 | } | 465 | } |
| 462 | } | 466 | } |
| 463 | 467 | ||
| @@ -496,15 +500,123 @@ Status Scheduler::RemoveRedundantBroadcastNode(const ascir::ImplGraph &impl_grap | |||
| 496 | return af::SUCCESS; | 500 | return af::SUCCESS; |
| 497 | } | 501 | } |
| 498 | 502 | ||
| 499 | -Status Scheduler::TileSplit() { | 503 | +namespace { |
| 500 | - // split ub | 504 | +Status AddIndirectLoadSyntheticOuterAxis(const af::AscNodePtr &node, ascir::AxisId outer_axis_id, |
| 501 | - TileTiling(tiling_case_.ub_tiling_id_x, tiling_case_.ub_tiling_x); | 505 | + bool has_synthetic_outer_axis) { |
| 502 | - TileTiling(tiling_case_.ub_tiling_id_y, tiling_case_.ub_tiling_y); | 506 | + if (!has_synthetic_outer_axis) { |
| 503 | - TileTiling(tiling_case_.ub_tiling_id_r, tiling_case_.ub_tiling_r); | 507 | + return af::SUCCESS; |
| 508 | + } | ||
| 504 | 509 | ||
| 510 | + node->attr.sched.axis.insert(node->attr.sched.axis.begin(), outer_axis_id); | ||
| 511 | + for (const auto &output : node->outputs()) { | ||
| 512 | + ascir::SizeExpr stride = af::sym::kSymbolOne; | ||
| 513 | + if (!output->attr.axis.empty()) { | ||
| 514 | + stride = af::sym::Mul(output->attr.repeats.front(), output->attr.strides.front()); | ||
| 515 | + } | ||
| 516 | + output->attr.axis.insert(output->attr.axis.begin(), outer_axis_id); | ||
| 517 | + output->attr.repeats.insert(output->attr.repeats.begin(), af::sym::kSymbolOne); | ||
| 518 | + output->attr.strides.insert(output->attr.strides.begin(), stride); | ||
| 519 | + } | ||
| 520 | + return af::SUCCESS; | ||
| 521 | +} | ||
| 522 | + | ||
| 523 | +Status ApplyIndirectLoadTemplateMerge(ascir::ImplGraph &graph, const af::AscNodePtr &node, ascir::AxisId axis_id) { | ||
| 524 | + if (axis_id == af::kIdNone) { | ||
| 525 | + return af::SUCCESS; | ||
| 526 | + } | ||
| 527 | + const auto axis = graph.FindAxis(axis_id); | ||
| 528 | + GE_ASSERT_NOTNULL(axis, "IndirectLoad template axis[%ld] is not found.", axis_id); | ||
| 529 | + if (axis->type == ascir::Axis::Type::kAxisTypeMerged) { | ||
| 530 | + GELOGD("[IndirectLoad] Graph[%s] apply template merge axis[%ld] for node[%s].", graph.GetName().c_str(), axis_id, | ||
| 531 | + node->GetNamePtr()); | ||
| 532 | + GE_ASSERT_TRUE(graph.ApplySchedAxisMerge(node, axis_id), "Failed to merge schedule axis[%ld] for node[%s].", | ||
| 533 | + axis_id, node->GetNamePtr()); | ||
| 534 | + GE_ASSERT_TRUE(graph.ApplyTensorAxisMerge(node, axis_id), "Failed to merge tensor axis[%ld] for node[%s].", axis_id, | ||
| 535 | + node->GetNamePtr()); | ||
| 536 | + } else { | ||
| 537 | + GELOGD("[IndirectLoad] Graph[%s] apply single-axis template merge axis[%ld] for node[%s].", graph.GetName().c_str(), | ||
| 538 | + axis_id, node->GetNamePtr()); | ||
| 539 | + GE_ASSERT_TRUE(graph.ApplySchedAxisMerge(node, axis_id, {axis_id}), | ||
| 540 | + "Failed to merge schedule axis[%ld] for node[%s].", axis_id, node->GetNamePtr()); | ||
| 541 | + GE_ASSERT_TRUE(graph.ApplyTensorAxisMerge(node, axis_id, {axis_id}), | ||
| 542 | + "Failed to merge tensor axis[%ld] for node[%s].", axis_id, node->GetNamePtr()); | ||
| 543 | + } | ||
| 544 | + return af::SUCCESS; | ||
| 545 | +} | ||
| 546 | + | ||
| 547 | +Status ApplyInputInnerVectorizedAxis(ascir::ImplGraph &graph, const af::AscNodePtr &node, | ||
| 548 | + ascir::AxisId input_inner_axis_id) { | ||
| 549 | + if (input_inner_axis_id == af::kIdNone) { | ||
| 550 | + return af::SUCCESS; | ||
| 551 | + } | ||
| 552 | + GE_ASSERT_TRUE(!node->outputs().empty(), "IndirectLoad input-pre node[%s] has no output.", node->GetNamePtr()); | ||
| 553 | + for (const auto &output : node->outputs()) { | ||
| 554 | + output->attr.vectorized_axis = {input_inner_axis_id}; | ||
| 555 | + output->attr.vectorized_strides = {af::sym::kSymbolOne}; | ||
| 556 | + } | ||
| 557 | + | ||
| 558 | + const auto input_inner_axis = graph.FindAxis(input_inner_axis_id); | ||
| 559 | + GE_ASSERT_NOTNULL(input_inner_axis, "IndirectLoad input inner axis[%ld] is not found.", input_inner_axis_id); | ||
| 560 | + if (input_inner_axis->type != ascir::Axis::Type::kAxisTypeMerged) { | ||
| 561 | + return af::SUCCESS; | ||
| 562 | + } | ||
| 563 | + GELOGD("[IndirectLoad] Graph[%s] apply input inner vectorized axis[%ld] for node[%s].", graph.GetName().c_str(), | ||
| 564 | + input_inner_axis_id, node->GetNamePtr()); | ||
| 565 | + GE_ASSERT_TRUE(graph.ApplySchedAxisMerge(node, input_inner_axis_id), | ||
| 566 | + "Failed to merge input inner schedule axis[%ld] for node[%s].", input_inner_axis_id, | ||
| 567 | + node->GetNamePtr()); | ||
| 568 | + GE_ASSERT_TRUE(graph.ApplyTensorAxisMerge(node, input_inner_axis_id), | ||
| 569 | + "Failed to merge input inner tensor axis[%ld] for node[%s].", input_inner_axis_id, node->GetNamePtr()); | ||
| 570 | + return af::SUCCESS; | ||
| 571 | +} | ||
| 572 | +} // namespace | ||
| 573 | + | ||
| 574 | +Status Scheduler::InitIndirectLoadScheduleCase() { | ||
| 575 | + is_indirect_load_schedule_case_ = false; | ||
| 576 | + has_indirect_load_synthetic_outer_axis_ = false; | ||
| 577 | + if (tiling_case_.ub_tiling_y.first == nullptr) { | ||
| 578 | + return af::SUCCESS; | ||
| 579 | + } | ||
| 580 | + const af::AscNodePtr indirect_load = ascgen_utils::indirect_load::FindIndirectLoadNode(graph_); | ||
| 581 | + if (indirect_load == nullptr) { | ||
| 582 | + return af::SUCCESS; | ||
| 583 | + } | ||
| 584 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, indirect_load_axes_)); | ||
| 585 | + const auto outer_axis = graph_.FindAxis(indirect_load_axes_.outer_axis); | ||
| 586 | + GE_ASSERT_NOTNULL(outer_axis, "IndirectLoad outer axis[%ld] is not found.", indirect_load_axes_.outer_axis); | ||
| 587 | + has_indirect_load_synthetic_outer_axis_ = | ||
| 588 | + outer_axis->from.empty() && | ||
| 589 | + std::find(indirect_load->attr.sched.axis.begin(), indirect_load->attr.sched.axis.end(), | ||
| 590 | + indirect_load_axes_.outer_axis) == indirect_load->attr.sched.axis.end(); | ||
| 591 | + is_indirect_load_schedule_case_ = true; | ||
| 592 | + return af::SUCCESS; | ||
| 593 | +} | ||
| 594 | + | ||
| 595 | +Status Scheduler::ApplyIndirectLoadNodeAxes(const af::AscNodePtr &node, bool &skip_main_tiling) const { | ||
| 596 | + skip_main_tiling = false; | ||
| 597 | + if (!is_indirect_load_schedule_case_) { | ||
| 598 | + return af::SUCCESS; | ||
| 599 | + } | ||
| 600 | + if (ascgen_utils::indirect_load::ShouldApplyInputInnerVectorization(node)) { | ||
| 601 | + skip_main_tiling = true; | ||
| 602 | + GE_ASSERT_SUCCESS(ApplyInputInnerVectorizedAxis(graph_, node, indirect_load_axes_.input_inner_axis)); | ||
| 603 | + return af::SUCCESS; | ||
| 604 | + } | ||
| 605 | + if (ascgen_utils::indirect_load::ShouldSkipMainScheduleTiling(node)) { | ||
| 606 | + skip_main_tiling = true; | ||
| 607 | + return af::SUCCESS; | ||
| 608 | + } | ||
| 609 | + GE_ASSERT_SUCCESS( | ||
| 610 | + AddIndirectLoadSyntheticOuterAxis(node, indirect_load_axes_.outer_axis, has_indirect_load_synthetic_outer_axis_)); | ||
| 611 | + GE_ASSERT_SUCCESS(ApplyIndirectLoadTemplateMerge(graph_, node, indirect_load_axes_.outer_axis)); | ||
| 612 | + GE_ASSERT_SUCCESS(ApplyIndirectLoadTemplateMerge(graph_, node, indirect_load_axes_.inner_axis)); | ||
| 613 | + return af::SUCCESS; | ||
| 614 | +} | ||
| 615 | + | ||
| 616 | +std::vector<ascir::AxisId> Scheduler::GetSortedNodeVectorizedAxes(Scheduler &scheduler) { | ||
| 505 | std::vector<ascir::AxisId> vectorized_axes; | 617 | std::vector<ascir::AxisId> vectorized_axes; |
| 506 | std::vector<size_t> vectorized_axes_order; | 618 | std::vector<size_t> vectorized_axes_order; |
| 507 | - FindVectorizedAxes(vectorized_axes, vectorized_axes_order); | 619 | + scheduler.FindVectorizedAxes(vectorized_axes, vectorized_axes_order); |
| 508 | 620 | ||
| 509 | // reorder vectorized axis by original node axis order | 621 | // reorder vectorized axis by original node axis order |
| 510 | std::vector<size_t> base_order(vectorized_axes.size()); | 622 | std::vector<size_t> base_order(vectorized_axes.size()); |
| @@ -513,17 +625,32 @@ Status Scheduler::TileSplit() { | |||
| 513 | return vectorized_axes_order[a] < vectorized_axes_order[b]; | 625 | return vectorized_axes_order[a] < vectorized_axes_order[b]; |
| 514 | }); | 626 | }); |
| 515 | 627 | ||
| 516 | - std::vector<ascir::AxisId> sorted_node_vectorized_axes; | 628 | + std::vector<ascir::AxisId> sorted; |
| 517 | - sorted_node_vectorized_axes.reserve(base_order.size()); | 629 | + sorted.reserve(base_order.size()); |
| 518 | for (const size_t index : base_order) { | 630 | for (const size_t index : base_order) { |
| 519 | - sorted_node_vectorized_axes.push_back(vectorized_axes[index]); | 631 | + sorted.push_back(vectorized_axes[index]); |
| 520 | } | 632 | } |
| 633 | + return sorted; | ||
| 634 | +} | ||
| 635 | + | ||
| 636 | +Status Scheduler::TileSplit() { | ||
| 637 | + // split ub | ||
| 638 | + TileTiling(tiling_case_.ub_tiling_id_x, tiling_case_.ub_tiling_x); | ||
| 639 | + TileTiling(tiling_case_.ub_tiling_id_y, tiling_case_.ub_tiling_y); | ||
| 640 | + TileTiling(tiling_case_.ub_tiling_id_r, tiling_case_.ub_tiling_r); | ||
| 641 | + | ||
| 642 | + auto sorted_node_vectorized_axes = GetSortedNodeVectorizedAxes(*this); | ||
| 521 | 643 | ||
| 522 | bool has_reduce = graph_cache_.HasComputeType(af::ComputeType::kComputeReduce); | 644 | bool has_reduce = graph_cache_.HasComputeType(af::ComputeType::kComputeReduce); |
| 523 | for (auto node : graph_.GetAllNodes()) { | 645 | for (auto node : graph_.GetAllNodes()) { |
| 524 | if (ScheduleUtils::IsBuffer(node)) { | 646 | if (ScheduleUtils::IsBuffer(node)) { |
| 525 | continue; | 647 | continue; |
| 526 | } | 648 | } |
| 649 | + bool skip_main_tiling = false; | ||
| 650 | + GE_CHK_STATUS_RET(ApplyIndirectLoadNodeAxes(node, skip_main_tiling)); | ||
| 651 | + if (skip_main_tiling) { | ||
| 652 | + continue; | ||
| 653 | + } | ||
| 527 | ApplyTiling(node, tiling_case_.ub_tiling_id_x, tiling_case_.ub_tiling_x); | 654 | ApplyTiling(node, tiling_case_.ub_tiling_id_x, tiling_case_.ub_tiling_x); |
| 528 | ApplyTiling(node, tiling_case_.ub_tiling_id_y, tiling_case_.ub_tiling_y); | 655 | ApplyTiling(node, tiling_case_.ub_tiling_id_y, tiling_case_.ub_tiling_y); |
| 529 | ApplyTiling(node, tiling_case_.ub_tiling_id_r, tiling_case_.ub_tiling_r); | 656 | ApplyTiling(node, tiling_case_.ub_tiling_id_r, tiling_case_.ub_tiling_r); |
| @@ -543,6 +670,9 @@ Status Scheduler::TileSplit() { | |||
| 543 | // 非reduce场景应该将向量化轴调整为tensor中的相对顺序, 带reduce场景由于tiling策略已经做了特别的reorder,需要跳过 | 670 | // 非reduce场景应该将向量化轴调整为tensor中的相对顺序, 带reduce场景由于tiling策略已经做了特别的reorder,需要跳过 |
| 544 | // tiling策略暂时无法支持具有reduce和transpose融合的场景 | 671 | // tiling策略暂时无法支持具有reduce和transpose融合的场景 |
| 545 | for (auto &output : node->outputs()) { | 672 | for (auto &output : node->outputs()) { |
| 673 | + if (is_indirect_load_schedule_case_ && ascgen_utils::indirect_load::ShouldPreserveVectorizedAxis(node)) { | ||
| 674 | + continue; | ||
| 675 | + } | ||
| 546 | output->attr.vectorized_axis = node_vectorized_axes; | 676 | output->attr.vectorized_axis = node_vectorized_axes; |
| 547 | if (!has_reduce) { | 677 | if (!has_reduce) { |
| 548 | auto tensor_axis = output->attr.axis; | 678 | auto tensor_axis = output->attr.axis; |
| @@ -561,6 +691,8 @@ Status Scheduler::DoScheduler() { | |||
| 561 | ascir::utils::DumpGraph(graph_, "AfterDoTiling"); | 691 | ascir::utils::DumpGraph(graph_, "AfterDoTiling"); |
| 562 | return af::SUCCESS; | 692 | return af::SUCCESS; |
| 563 | } | 693 | } |
| 694 | + GE_CHK_STATUS_RET(InitIndirectLoadScheduleCase(), "Failed to init IndirectLoad schedule case for graph[%s].", | ||
| 695 | + graph_.GetName().c_str()); | ||
| 564 | RemoveDuplicatedAxisFromGroup(); | 696 | RemoveDuplicatedAxisFromGroup(); |
| 565 | // Tile Split | 697 | // Tile Split |
| 566 | TileSplit(); | 698 | TileSplit(); |
| @@ -586,7 +718,8 @@ Status Scheduler::DoScheduler() { | |||
| 586 | Status Scheduler::ApplyBlockSplit(const std::vector<ascir::AxisId> &new_sched_axes) { | 718 | Status Scheduler::ApplyBlockSplit(const std::vector<ascir::AxisId> &new_sched_axes) { |
| 587 | bool is_reduce_after = false; | 719 | bool is_reduce_after = false; |
| 588 | for (auto node : graph_.GetAllNodes()) { | 720 | for (auto node : graph_.GetAllNodes()) { |
| 589 | - if (ScheduleUtils::IsBuffer(node)) { | 721 | + if (ScheduleUtils::IsBuffer(node) || |
| 722 | + (is_indirect_load_schedule_case_ && ascgen_utils::indirect_load::ShouldSkipMainScheduleTiling(node))) { | ||
| 590 | continue; | 723 | continue; |
| 591 | } | 724 | } |
| 592 | if ((!is_reduce_after) && ScheduleUtils::IsReduce(node)) { | 725 | if ((!is_reduce_after) && ScheduleUtils::IsReduce(node)) { |
| @@ -596,9 +729,14 @@ Status Scheduler::ApplyBlockSplit(const std::vector<ascir::AxisId> &new_sched_ax | |||
| 596 | std::vector<ascir::AxisId> node_new_sched_axes = new_sched_axes; | 729 | std::vector<ascir::AxisId> node_new_sched_axes = new_sched_axes; |
| 597 | GE_ASSERT_TRUE(!node->outputs.operator()().empty()); | 730 | GE_ASSERT_TRUE(!node->outputs.operator()().empty()); |
| 598 | auto &vectorized_axis = node->outputs[0].attr.vectorized_axis; | 731 | auto &vectorized_axis = node->outputs[0].attr.vectorized_axis; |
| 599 | - node_new_sched_axes.insert(node_new_sched_axes.end(), vectorized_axis.begin(), vectorized_axis.end()); | 732 | + if (!is_indirect_load_schedule_case_) { |
| 733 | + node_new_sched_axes.insert(node_new_sched_axes.end(), vectorized_axis.begin(), vectorized_axis.end()); | ||
| 734 | + } | ||
| 600 | bool is_store_after_reduce = is_reduce_after && ScheduleUtils::IsStore(node); | 735 | bool is_store_after_reduce = is_reduce_after && ScheduleUtils::IsStore(node); |
| 601 | GE_ASSERT_SUCCESS(ApplyBlockSplitToNode(node, is_store_after_reduce)); | 736 | GE_ASSERT_SUCCESS(ApplyBlockSplitToNode(node, is_store_after_reduce)); |
| 737 | + if (is_indirect_load_schedule_case_) { | ||
| 738 | + continue; | ||
| 739 | + } | ||
| 602 | graph_.ApplySchedAxisReorder(node, node_new_sched_axes); | 740 | graph_.ApplySchedAxisReorder(node, node_new_sched_axes); |
| 603 | } | 741 | } |
| 604 | return af::SUCCESS; | 742 | return af::SUCCESS; |
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 17 | 18 | ||
| 18 | 19 | ||
| 19 | 20 | ||
| @@ -66,6 +67,7 @@ class Scheduler { | |||
| 66 | private: | 67 | private: |
| 67 | // ub 切分 | 68 | // ub 切分 |
| 68 | Status TileSplit(); | 69 | Status TileSplit(); |
| 70 | + static std::vector<ascir::AxisId> GetSortedNodeVectorizedAxes(Scheduler &scheduler); | ||
| 69 | // block 切分 | 71 | // block 切分 |
| 70 | Status BlockSplit(std::vector<ascir::AxisId> &tile_out_axes); | 72 | Status BlockSplit(std::vector<ascir::AxisId> &tile_out_axes); |
| 71 | void FuseTileOutAxes(const std::vector<ascir::AxisId> &non_reduce_outer_axes, | 73 | void FuseTileOutAxes(const std::vector<ascir::AxisId> &non_reduce_outer_axes, |
| @@ -74,10 +76,12 @@ class Scheduler { | |||
| 74 | const std::vector<ascir::AxisId> &non_reduce_outer_axes, | 76 | const std::vector<ascir::AxisId> &non_reduce_outer_axes, |
| 75 | const std::vector<ascir::AxisId> &reduce_outer_axes); | 77 | const std::vector<ascir::AxisId> &reduce_outer_axes); |
| 76 | void RemoveDuplicatedAxisFromGroup(); | 78 | void RemoveDuplicatedAxisFromGroup(); |
| 79 | + Status InitIndirectLoadScheduleCase(); | ||
| 80 | + Status ApplyIndirectLoadNodeAxes(const af::AscNodePtr &node, bool &skip_main_tiling) const; | ||
| 77 | Status ModifyStoreAfterReduce(ascir::NodeView &node, ascir::AxisId reduce_block_id); | 81 | Status ModifyStoreAfterReduce(ascir::NodeView &node, ascir::AxisId reduce_block_id); |
| 78 | Status ApplyBlockSplitToNode(ascir::NodeView &node, bool is_store_after_reduce); | 82 | Status ApplyBlockSplitToNode(ascir::NodeView &node, bool is_store_after_reduce); |
| 79 | void TileTiling(ascir::AxisId tile_id, std::pair<af::AxisPtr, af::AxisPtr> &tiled_axes) const { | 83 | void TileTiling(ascir::AxisId tile_id, std::pair<af::AxisPtr, af::AxisPtr> &tiled_axes) const { |
| 80 | - if (tile_id != kDefaultAxisId) { | 84 | + if (tile_id != kDefaultAxisId && tiled_axes.first == nullptr) { |
| 81 | tiled_axes = graph_.TileSplit(tile_id); | 85 | tiled_axes = graph_.TileSplit(tile_id); |
| 82 | } | 86 | } |
| 83 | } | 87 | } |
| @@ -96,12 +100,18 @@ class Scheduler { | |||
| 96 | bool HasRGroup() const { | 100 | bool HasRGroup() const { |
| 97 | return tiling_case_.ub_tiling_id_r != kDefaultAxisId; | 101 | return tiling_case_.ub_tiling_id_r != kDefaultAxisId; |
| 98 | } | 102 | } |
| 103 | + | ||
| 104 | + void AdjustVectorizedAxesOrderOffsets(std::vector<size_t> &vectorized_axes_order, size_t split_point, size_t end, | ||
| 105 | + size_t offset) const; | ||
| 99 | ascir::ImplGraph &graph_; | 106 | ascir::ImplGraph &graph_; |
| 100 | AxisGroup axes_group_; | 107 | AxisGroup axes_group_; |
| 101 | TilingCase &tiling_case_; | 108 | TilingCase &tiling_case_; |
| 102 | bool is_last_axis_reduce_; | 109 | bool is_last_axis_reduce_; |
| 103 | optimize::ReduceTemplateType reduce_template_; | 110 | optimize::ReduceTemplateType reduce_template_; |
| 104 | ascir::CubeTemplateType cube_template_; | 111 | ascir::CubeTemplateType cube_template_; |
| 112 | + bool is_indirect_load_schedule_case_ = false; | ||
| 113 | + bool has_indirect_load_synthetic_outer_axis_ = false; | ||
| 114 | + ascgen_utils::indirect_load::TemplateAxes indirect_load_axes_; | ||
| 105 | GraphPropertiesCache graph_cache_; // 图属性缓存,避免重复遍历 | 115 | GraphPropertiesCache graph_cache_; // 图属性缓存,避免重复遍历 |
| 106 | }; | 116 | }; |
| 107 | } // namespace optimize::autoschedule | 117 | } // namespace optimize::autoschedule |
| @@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 15 | 16 | ||
| 16 | 17 | ||
| 17 | 18 | ||
| @@ -483,7 +484,8 @@ Status TilingGroup::GenTilingGroup(const ascir::ImplGraph &impl_graph, AxisGroup | |||
| 483 | std::vector<std::pair<std::string, AxisGroup>> node_name_to_tiling_group; | 484 | std::vector<std::pair<std::string, AxisGroup>> node_name_to_tiling_group; |
| 484 | std::set<af::AxisId> n_groupset; | 485 | std::set<af::AxisId> n_groupset; |
| 485 | for (const auto &node : impl_graph.GetAllNodes()) { | 486 | for (const auto &node : impl_graph.GetAllNodes()) { |
| 486 | - if (ScheduleUtils::IsBuffer(node)) { | 487 | + if (ScheduleUtils::IsBuffer(node) || |
| 488 | + ascgen_utils::indirect_load::GetTemplateBehavior(node).skips_main_schedule_tiling) { | ||
| 487 | continue; | 489 | continue; |
| 488 | } | 490 | } |
| 489 | AxisGroup single_node_axes_group; | 491 | AxisGroup single_node_axes_group; |
| @@ -16,6 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | + | ||
| 19 | 20 | ||
| 20 | 21 | ||
| 21 | 22 | ||
| @@ -382,6 +383,10 @@ Status BufQueAllocator::SetOutputTensorAttr(const af::AscGraph &impl_graph) cons | |||
| 382 | } | 383 | } |
| 383 | 384 | ||
| 384 | Status BufQueAllocator::GetAndSetNodeTempBuffer(const af::AscNodePtr &node) { | 385 | Status BufQueAllocator::GetAndSetNodeTempBuffer(const af::AscNodePtr &node) { |
| 386 | + if (ascgen_utils::indirect_load::GetTemplateBehavior(node).skips_api_emit) { | ||
| 387 | + node->attr.tmp_buffers.clear(); | ||
| 388 | + return af::SUCCESS; | ||
| 389 | + } | ||
| 385 | auto impl = ascgen_utils::GetAscIrCodegenImpl(node->GetType()); | 390 | auto impl = ascgen_utils::GetAscIrCodegenImpl(node->GetType()); |
| 386 | GE_ASSERT_NOTNULL(impl, "GetAscIrCodegenImpl of node %s[%s] is null", node->GetTypePtr(), node->GetNamePtr()); | 391 | GE_ASSERT_NOTNULL(impl, "GetAscIrCodegenImpl of node %s[%s] is null", node->GetTypePtr(), node->GetNamePtr()); |
| 387 | std::vector<std::unique_ptr<af::TmpBufDesc>> buffers = | 392 | std::vector<std::unique_ptr<af::TmpBufDesc>> buffers = |
| @@ -401,7 +406,7 @@ Status BufQueAllocator::GetAndSetNodeTempBuffer(const af::AscNodePtr &node) { | |||
| 401 | } | 406 | } |
| 402 | 407 | ||
| 403 | bool BufQueAllocator::IsTensorUsedByOtherUnit(const af::AscNodePtr &node, const af::AscTensor *output) { | 408 | bool BufQueAllocator::IsTensorUsedByOtherUnit(const af::AscNodePtr &node, const af::AscTensor *output) { |
| 404 | - if (ScheduleUtils::IsLoad(node) || IsOps<Gather>(node)) { | 409 | + if (ScheduleUtils::IsLoad(node) || ScheduleUtils::IsGatherLikeLoad(node)) { |
| 405 | return true; | 410 | return true; |
| 406 | } | 411 | } |
| 407 | for (const auto &input : output->anchor.GetPeerInDataAnchorsPtr()) { | 412 | for (const auto &input : output->anchor.GetPeerInDataAnchorsPtr()) { |
| @@ -24,6 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | 26 | ||
| 27 | + | ||
| 27 | 28 | ||
| 28 | 29 | ||
| 29 | 30 | ||
| @@ -802,7 +803,7 @@ Status Optimizer::RemoveAllZeroStrideLoopAxis(ascir::ImplGraph &owner_graph) { | |||
| 802 | 803 | ||
| 803 | Status Optimizer::MergeContinuousAxis(ascir::ImplGraph &impl_graph, ascir::CubeTemplateType cube_type) { | 804 | Status Optimizer::MergeContinuousAxis(ascir::ImplGraph &impl_graph, ascir::CubeTemplateType cube_type) { |
| 804 | auto all_axis = impl_graph.GetAllAxis(); | 805 | auto all_axis = impl_graph.GetAllAxis(); |
| 805 | - if (all_axis.size() <= 1UL) { | 806 | + if (all_axis.size() <= 1UL || ascgen_utils::indirect_load::FindIndirectLoadNode(impl_graph) != nullptr) { |
| 806 | return af::SUCCESS; | 807 | return af::SUCCESS; |
| 807 | } | 808 | } |
| 808 | // concat等场景,会有多套轴, 只能先用循环轴的index来生成潜在连续组, 后续根据连续组会找到多个连续轴 | 809 | // concat等场景,会有多套轴, 只能先用循环轴的index来生成潜在连续组, 后续根据连续组会找到多个连续轴 |
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | + | ||
| 14 | 15 | ||
| 15 | 16 | ||
| 16 | namespace optimize { | 17 | namespace optimize { |
| @@ -301,7 +302,8 @@ af::Status BaseAlignmentStrategy::AddPadForAlignmentConflictNode(ascir::ImplGrap | |||
| 301 | bool inserted = false; | 302 | bool inserted = false; |
| 302 | for (const auto &node : impl_graph.GetAllNodes()) { | 303 | for (const auto &node : impl_graph.GetAllNodes()) { |
| 303 | GE_ASSERT_NOTNULL(node); | 304 | GE_ASSERT_NOTNULL(node); |
| 304 | - if (ScheduleUtils::IsBuffer(node)) { | 305 | + const auto indirect_load_behavior = ascgen_utils::indirect_load::GetTemplateBehavior(node); |
| 306 | + if (ScheduleUtils::IsBuffer(node) || indirect_load_behavior.uses_direct_gm_pipeline) { | ||
| 305 | continue; | 307 | continue; |
| 306 | } | 308 | } |
| 307 | 309 | ||
| @@ -383,7 +385,9 @@ af::Status BaseAlignmentStrategy::AlignVectorizedStrides(ascir::ImplGraph &impl_ | |||
| 383 | 385 | ||
| 384 | for (const auto &node : impl_graph.GetAllNodes()) { | 386 | for (const auto &node : impl_graph.GetAllNodes()) { |
| 385 | GE_ASSERT_NOTNULL(node); | 387 | GE_ASSERT_NOTNULL(node); |
| 386 | - if (ScheduleUtils::IsBuffer(node)) { | 388 | + const auto indirect_load_behavior = ascgen_utils::indirect_load::GetTemplateBehavior(node); |
| 389 | + if (ScheduleUtils::IsBuffer(node) || indirect_load_behavior.uses_direct_gm_pipeline || | ||
| 390 | + indirect_load_behavior.skips_api_emit) { | ||
| 387 | continue; | 391 | continue; |
| 388 | } | 392 | } |
| 389 | GE_ASSERT_SUCCESS(SetVectorizedStridesForOneNode(node)); | 393 | GE_ASSERT_SUCCESS(SetVectorizedStridesForOneNode(node)); |
| @@ -19,6 +19,7 @@ | |||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | + | ||
| 22 | 23 | ||
| 23 | namespace optimize { | 24 | namespace optimize { |
| 24 | class ScheduleUtils { | 25 | class ScheduleUtils { |
| @@ -66,6 +67,11 @@ class ScheduleUtils { | |||
| 66 | return node->attr.api.compute_type == af::ComputeType::kComputeGather; | 67 | return node->attr.api.compute_type == af::ComputeType::kComputeGather; |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 70 | + static bool IsGatherLikeLoad(const af::AscNodePtr &node) { | ||
| 71 | + return af::ops::IsOps<af::ascir_op::Gather>(node) || | ||
| 72 | + ::ascir::GetTemplateIdOrDefault(*node) == ascir::TemplateId::kIndirectLoadSimd; | ||
| 73 | + } | ||
| 74 | + | ||
| 69 | static bool IsBuffer(const af::AscNodePtr &node) { | 75 | static bool IsBuffer(const af::AscNodePtr &node) { |
| 70 | return node->attr.api.type == af::ApiType::kAPITypeBuffer; | 76 | return node->attr.api.type == af::ApiType::kAPITypeBuffer; |
| 71 | } | 77 | } |
| @@ -0,0 +1,526 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | +namespace optimize { | ||
| 27 | +namespace { | ||
| 28 | +constexpr int64_t kIndirectLoadSimtDcacheSize = 32 * 1024; | ||
| 29 | +using NodePath = std::vector<af::AscNodePtr>; | ||
| 30 | + | ||
| 31 | +struct IndirectLoadGraphPaths { | ||
| 32 | + NodePath data_input; | ||
| 33 | + NodePath index_input; | ||
| 34 | + NodePath output; | ||
| 35 | +}; | ||
| 36 | + | ||
| 37 | +NodePath CollectInputPath(const af::AscNodePtr &indirect_load, size_t input_index) { | ||
| 38 | + NodePath path; | ||
| 39 | + for (auto current = ascgen_utils::indirect_load::GetInputProducer(indirect_load, input_index); current != nullptr; | ||
| 40 | + current = current->inputs.Size() == 1UL ? ascgen_utils::indirect_load::GetInputProducer(current, 0UL) | ||
| 41 | + : nullptr) { | ||
| 42 | + path.emplace_back(current); | ||
| 43 | + } | ||
| 44 | + return path; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +NodePath CollectOutputPath(const af::AscNodePtr &indirect_load) { | ||
| 48 | + NodePath path; | ||
| 49 | + for (auto current = ascgen_utils::indirect_load::GetOnlyOutputConsumer(indirect_load); current != nullptr; | ||
| 50 | + current = ascgen_utils::indirect_load::GetOnlyOutputConsumer(current)) { | ||
| 51 | + path.emplace_back(current); | ||
| 52 | + } | ||
| 53 | + return path; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +IndirectLoadGraphPaths CollectGraphPaths(const af::AscNodePtr &indirect_load) { | ||
| 57 | + return {CollectInputPath(indirect_load, 0UL), CollectInputPath(indirect_load, 1UL), CollectOutputPath(indirect_load)}; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +std::string GenerateScoreFunc(ascir::TemplateId template_id, bool prefer_simd) { | ||
| 61 | + const bool is_simd = template_id == ascir::TemplateId::kIndirectLoadSimd; | ||
| 62 | + std::stringstream ss; | ||
| 63 | + ss << "int32_t CalcScore(AutofuseTilingData &tiling_data) {" << std::endl; | ||
| 64 | + ss << " (void)tiling_data;" << std::endl; | ||
| 65 | + ss << " return " << (is_simd == prefer_simd ? 1 : 0) << ";" << std::endl; | ||
| 66 | + ss << "}" << std::endl; | ||
| 67 | + return ss.str(); | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +bool HasExp2(const af::AscGraph &graph) { | ||
| 71 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 72 | + if (af::ops::IsOps<af::ascir_op::Exp2>(node)) { | ||
| 73 | + return true; | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + return false; | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +bool IsTemplateCandidateLegal(ascir::TemplateId template_id, const IndirectLoadGraphPaths &paths) { | ||
| 80 | + if (template_id != ascir::TemplateId::kIndirectLoadSimt) { | ||
| 81 | + return true; | ||
| 82 | + } | ||
| 83 | + if (paths.data_input.empty()) { | ||
| 84 | + return false; | ||
| 85 | + } | ||
| 86 | + const auto &input_node = paths.data_input.front(); | ||
| 87 | + return af::ops::IsOps<af::ascir_op::Data>(input_node) || af::ops::IsOps<af::ascir_op::Load>(input_node); | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +af::Status GetIndirectLoadAxis(const af::AscNodePtr &node, int64_t &axis) { | ||
| 91 | + GE_ASSERT_NOTNULL(node, "IndirectLoad node is null."); | ||
| 92 | + GE_ASSERT_NOTNULL(node->attr.ir_attr, "IndirectLoad ir attr is null, node = %s", node->GetNamePtr()); | ||
| 93 | + const auto *ir_attr = node->attr.ir_attr->DownCastTo<af::ascir_op::IndirectLoad::AscIndirectLoadIrAttrDef>(); | ||
| 94 | + GE_ASSERT_NOTNULL(ir_attr, "IndirectLoad ir attr type is invalid, node = %s", node->GetNamePtr()); | ||
| 95 | + GE_ASSERT_GRAPH_SUCCESS(ir_attr->GetAxis(axis), "Failed to get IndirectLoad axis, node = %s", node->GetNamePtr()); | ||
| 96 | + return af::SUCCESS; | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +af::Status ValidateIndirectLoadInputRank(const af::AscNodePtr &indirect_load, size_t input_idx, size_t output_rank) { | ||
| 100 | + auto input_anchor = indirect_load->GetInDataAnchor(input_idx); | ||
| 101 | + GE_ASSERT_NOTNULL(input_anchor, "IndirectLoad input%zu anchor is null.", input_idx); | ||
| 102 | + auto peer_out_anchor = input_anchor->GetPeerOutAnchor(); | ||
| 103 | + GE_ASSERT_NOTNULL(peer_out_anchor, "IndirectLoad input%zu peer anchor is null.", input_idx); | ||
| 104 | + | ||
| 105 | + auto input_node = std::dynamic_pointer_cast<af::AscNode>(peer_out_anchor->GetOwnerNode()); | ||
| 106 | + GE_ASSERT_NOTNULL(input_node, "IndirectLoad input%zu node is invalid.", input_idx); | ||
| 107 | + const size_t output_idx = static_cast<size_t>(peer_out_anchor->GetIdx()); | ||
| 108 | + const auto input_outputs = input_node->outputs(); | ||
| 109 | + GE_ASSERT_TRUE(output_idx < input_outputs.size(), | ||
| 110 | + "IndirectLoad input%zu output index %zu is out of range, output num:%zu.", input_idx, output_idx, | ||
| 111 | + input_outputs.size()); | ||
| 112 | + const auto input_output = input_outputs[output_idx]; | ||
| 113 | + GE_ASSERT_NOTNULL(input_output, "IndirectLoad input%zu output tensor is null.", input_idx); | ||
| 114 | + const size_t input_rank = input_output->attr.axis.size(); | ||
| 115 | + GE_ASSERT_TRUE(input_rank == output_rank, | ||
| 116 | + "IndirectLoad input%zu rank must equal output rank, input rank:%zu, output rank:%zu.", input_idx, | ||
| 117 | + input_rank, output_rank); | ||
| 118 | + return af::SUCCESS; | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +af::Status ValidateIndirectLoadNode(const af::AscNodePtr &indirect_load) { | ||
| 122 | + GE_ASSERT_NOTNULL(indirect_load, "IndirectLoad node is null."); | ||
| 123 | + const auto outputs = indirect_load->outputs(); | ||
| 124 | + GE_ASSERT_TRUE(!outputs.empty(), "IndirectLoad graph is invalid."); | ||
| 125 | + const auto output = outputs[0]; | ||
| 126 | + GE_ASSERT_NOTNULL(output, "IndirectLoad output tensor is null."); | ||
| 127 | + const size_t output_rank = output->attr.axis.size(); | ||
| 128 | + int64_t axis = 0L; | ||
| 129 | + GE_ASSERT_SUCCESS(GetIndirectLoadAxis(indirect_load, axis)); | ||
| 130 | + const int64_t rank = static_cast<int64_t>(output_rank); | ||
| 131 | + GE_ASSERT_TRUE(axis >= -rank && axis < rank, "IndirectLoad axis %ld is out of range for output rank %zu.", axis, | ||
| 132 | + output_rank); | ||
| 133 | + GE_ASSERT_SUCCESS(ValidateIndirectLoadInputRank(indirect_load, 0UL, output_rank)); | ||
| 134 | + GE_ASSERT_SUCCESS(ValidateIndirectLoadInputRank(indirect_load, 1UL, output_rank)); | ||
| 135 | + return af::SUCCESS; | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +af::Status MergeAxesForTemplate(af::AscGraph &graph, const std::vector<af::AxisId> &axes, const std::string &name, | ||
| 139 | + af::AxisId &merged_axis) { | ||
| 140 | + GE_ASSERT_TRUE(!axes.empty(), "IndirectLoad merge axis source is empty, name:%s.", name.c_str()); | ||
| 141 | + merged_axis = axes.size() == 1UL ? axes.front() : graph.MergeAxis(axes, name)->id; | ||
| 142 | + return af::SUCCESS; | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | +af::Status CreateFixedTileSplit(af::AscGraph &graph, af::AxisId axis_id, af::AxisId &outer_id, af::AxisId &inner_id) { | ||
| 146 | + const auto *axis = graph.FindAxis(axis_id); | ||
| 147 | + GE_ASSERT_NOTNULL(axis, "IndirectLoad fixed tile axis %ld is not found.", axis_id); | ||
| 148 | + outer_id = | ||
| 149 | + graph.CreateAxis(axis->name + "T", ascir::Axis::Type::kAxisTypeTileOuter, axis->size, {axis_id}, af::kIdNone).id; | ||
| 150 | + inner_id = | ||
| 151 | + graph | ||
| 152 | + .CreateAxis(axis->name + "t", ascir::Axis::Type::kAxisTypeTileInner, af::sym::kSymbolOne, {axis_id}, outer_id) | ||
| 153 | + .id; | ||
| 154 | + auto *outer_axis = graph.FindAxis(outer_id); | ||
| 155 | + GE_ASSERT_NOTNULL(outer_axis, "IndirectLoad fixed tile outer axis %ld is not found.", outer_id); | ||
| 156 | + outer_axis->split_pair_other_id = inner_id; | ||
| 157 | + return af::SUCCESS; | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +af::Status BuildSimdInputInnerAxis(af::AscGraph &graph, const af::AscNodePtr &input_producer, size_t axis_index, | ||
| 161 | + ascir::AxisId &input_inner_axis) { | ||
| 162 | + GE_ASSERT_NOTNULL(input_producer, "IndirectLoad SIMD input0 producer is null."); | ||
| 163 | + GE_ASSERT_TRUE(!input_producer->outputs().empty(), "IndirectLoad SIMD input0 producer has no output."); | ||
| 164 | + const auto input_axes = input_producer->outputs()[0]->attr.axis; | ||
| 165 | + GE_ASSERT_TRUE(axis_index < input_axes.size(), "IndirectLoad SIMD input axis index is out of range."); | ||
| 166 | + std::vector<ascir::AxisId> input_inner_axes(input_axes.begin() + static_cast<int64_t>(axis_index), input_axes.end()); | ||
| 167 | + GE_ASSERT_SUCCESS(MergeAxesForTemplate(graph, input_inner_axes, "indirect_load_input_inner", input_inner_axis)); | ||
| 168 | + return af::SUCCESS; | ||
| 169 | +} | ||
| 170 | +af::Status BuildAxisViewByBoundary(af::AscGraph &graph, const std::vector<af::AxisId> &axes, size_t boundary, | ||
| 171 | + af::AxisId &outer_axis, af::AxisId &inner_axis, std::vector<af::AxisId> &outer_axes, | ||
| 172 | + std::vector<af::AxisId> &inner_axes) { | ||
| 173 | + GE_ASSERT_TRUE(!axes.empty(), "IndirectLoad output axis is empty."); | ||
| 174 | + const size_t split = std::min(boundary, axes.size()); | ||
| 175 | + outer_axes.assign(axes.begin(), axes.begin() + static_cast<int64_t>(split)); | ||
| 176 | + inner_axes.assign(axes.begin() + static_cast<int64_t>(split), axes.end()); | ||
| 177 | + if (outer_axes.empty()) { | ||
| 178 | + outer_axis = graph.CreateAxis("indirect_load_single_outer", af::sym::kSymbolOne).id; | ||
| 179 | + } else { | ||
| 180 | + GE_ASSERT_SUCCESS(MergeAxesForTemplate(graph, outer_axes, "indirect_load_outer", outer_axis)); | ||
| 181 | + } | ||
| 182 | + if (!inner_axes.empty()) { | ||
| 183 | + GE_ASSERT_SUCCESS(MergeAxesForTemplate(graph, inner_axes, "indirect_load_inner", inner_axis)); | ||
| 184 | + } | ||
| 185 | + return af::SUCCESS; | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +af::Status NormalizeAxesForTemplate(af::AscGraph &graph, const af::AscNodePtr &indirect_load, int64_t axis, | ||
| 189 | + int64_t rank, ascir::AxisId input_inner_axis) { | ||
| 190 | + const auto output_axes = indirect_load->outputs()[0]->attr.axis; | ||
| 191 | + GE_ASSERT_TRUE(!output_axes.empty(), "IndirectLoad output axis is empty."); | ||
| 192 | + const size_t boundary = static_cast<size_t>(axis < 0L ? axis + rank : axis); | ||
| 193 | + ascir::AxisId outer_axis = af::kIdNone; | ||
| 194 | + ascir::AxisId inner_axis = af::kIdNone; | ||
| 195 | + std::vector<ascir::AxisId> outer_axes; | ||
| 196 | + std::vector<ascir::AxisId> inner_axes; | ||
| 197 | + GE_ASSERT_SUCCESS( | ||
| 198 | + BuildAxisViewByBoundary(graph, output_axes, boundary, outer_axis, inner_axis, outer_axes, inner_axes)); | ||
| 199 | + ascir::AxisId fixed_tile_outer_axis = af::kIdNone; | ||
| 200 | + ascir::AxisId fixed_tile_inner_axis = af::kIdNone; | ||
| 201 | + GE_ASSERT_SUCCESS(CreateFixedTileSplit(graph, outer_axis, fixed_tile_outer_axis, fixed_tile_inner_axis)); | ||
| 202 | + GE_ASSERT_SUCCESS( | ||
| 203 | + ascgen_utils::indirect_load::SetTemplateAxes(indirect_load, {outer_axis, inner_axis, input_inner_axis})); | ||
| 204 | + return af::SUCCESS; | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +af::Status NormalizeSimdAxesForTemplate(af::AscGraph &graph, const af::AscNodePtr &indirect_load, | ||
| 208 | + const IndirectLoadGraphPaths &paths) { | ||
| 209 | + const auto output_axes = indirect_load->outputs()[0]->attr.axis; | ||
| 210 | + GE_ASSERT_TRUE(!output_axes.empty(), "IndirectLoad SIMD output axis is empty."); | ||
| 211 | + int64_t axis = 0L; | ||
| 212 | + GE_ASSERT_SUCCESS(GetIndirectLoadAxis(indirect_load, axis)); | ||
| 213 | + const int64_t rank = static_cast<int64_t>(output_axes.size()); | ||
| 214 | + const size_t axis_index = static_cast<size_t>(axis < 0L ? axis + rank : axis); | ||
| 215 | + ascir::AxisId input_inner_axis = af::kIdNone; | ||
| 216 | + const af::AscNodePtr input_producer = paths.data_input.empty() ? nullptr : paths.data_input.front(); | ||
| 217 | + if (input_producer != nullptr && !af::ops::IsOps<af::ascir_op::Data>(input_producer)) { | ||
| 218 | + GE_ASSERT_SUCCESS(BuildSimdInputInnerAxis(graph, input_producer, axis_index, input_inner_axis)); | ||
| 219 | + } | ||
| 220 | + return NormalizeAxesForTemplate(graph, indirect_load, axis, rank, input_inner_axis); | ||
| 221 | +} | ||
| 222 | + | ||
| 223 | +af::Status NormalizeSimtAxesForTemplate(af::AscGraph &graph, const af::AscNodePtr &indirect_load) { | ||
| 224 | + const auto output_axes = indirect_load->outputs()[0]->attr.axis; | ||
| 225 | + GE_ASSERT_TRUE(!output_axes.empty(), "IndirectLoad SIMT output axis is empty."); | ||
| 226 | + const int64_t rank = static_cast<int64_t>(output_axes.size()); | ||
| 227 | + return NormalizeAxesForTemplate(graph, indirect_load, rank, rank, af::kIdNone); | ||
| 228 | +} | ||
| 229 | + | ||
| 230 | +bool CanEmitSimtScalar(const af::AscNodePtr &node) { | ||
| 231 | + if (node == nullptr) { | ||
| 232 | + return false; | ||
| 233 | + } | ||
| 234 | + const auto impl = ascgen_utils::GetAscIrCodegenImpl(node->GetType()); | ||
| 235 | + return impl != nullptr && impl->IsSimtScalarSupported(*node); | ||
| 236 | +} | ||
| 237 | + | ||
| 238 | +bool CollectMovableSimtInputPreNodes(const af::AscNodePtr &indirect_load, const NodePath &data_input, NodePath &nodes) { | ||
| 239 | + af::AscNodePtr consumer = indirect_load; | ||
| 240 | + for (const auto ¤t : data_input) { | ||
| 241 | + if (af::ops::IsOps<af::ascir_op::Data>(current) || af::ops::IsOps<af::ascir_op::Load>(current)) { | ||
| 242 | + return true; | ||
| 243 | + } | ||
| 244 | + if (current->inputs.Size() != 1UL || current->outputs().empty() || !CanEmitSimtScalar(current) || | ||
| 245 | + current->GetInControlNodesSize() != 0UL || current->GetOutControlNodesSize() != 0UL || | ||
| 246 | + ascgen_utils::indirect_load::GetOnlyOutputConsumer(current) != consumer) { | ||
| 247 | + return false; | ||
| 248 | + } | ||
| 249 | + nodes.emplace_back(current); | ||
| 250 | + consumer = current; | ||
| 251 | + } | ||
| 252 | + return false; | ||
| 253 | +} | ||
| 254 | + | ||
| 255 | +af::Status MoveSimtInputPreNode(const af::AscNodePtr &node, const af::AscNodePtr &indirect_load) { | ||
| 256 | + const auto producer = ascgen_utils::indirect_load::GetInputProducer(node, 0UL); | ||
| 257 | + GE_ASSERT_NOTNULL(producer); | ||
| 258 | + const auto producer_out = producer->GetOutDataAnchor(0UL); | ||
| 259 | + const auto node_in = node->GetInDataAnchor(0UL); | ||
| 260 | + const auto node_out = node->GetOutDataAnchor(0UL); | ||
| 261 | + const auto indirect_in = indirect_load->GetInDataAnchor(0UL); | ||
| 262 | + const auto indirect_out = indirect_load->GetOutDataAnchor(0UL); | ||
| 263 | + GE_ASSERT_NOTNULL(producer_out); | ||
| 264 | + GE_ASSERT_NOTNULL(node_in); | ||
| 265 | + GE_ASSERT_NOTNULL(node_out); | ||
| 266 | + GE_ASSERT_NOTNULL(indirect_in); | ||
| 267 | + GE_ASSERT_NOTNULL(indirect_out); | ||
| 268 | + GE_ASSERT_GRAPH_SUCCESS(af::GraphUtils::ReplaceEdgeSrc(node_out, indirect_in, producer_out)); | ||
| 269 | + GE_ASSERT_GRAPH_SUCCESS(af::GraphUtils::ReplaceEdgeSrc(producer_out, node_in, indirect_out)); | ||
| 270 | + const auto peer_inputs = indirect_out->GetPeerInDataAnchors(); | ||
| 271 | + for (const auto &peer_in : peer_inputs) { | ||
| 272 | + if (peer_in != node_in) { | ||
| 273 | + GE_ASSERT_GRAPH_SUCCESS(af::GraphUtils::ReplaceEdgeSrc(indirect_out, peer_in, node_out)); | ||
| 274 | + } | ||
| 275 | + } | ||
| 276 | + | ||
| 277 | + const auto indirect_output = indirect_load->outputs()[0]; | ||
| 278 | + const auto node_output = node->outputs()[0]; | ||
| 279 | + node->attr.sched.axis = indirect_load->attr.sched.axis; | ||
| 280 | + node_output->attr.axis = indirect_output->attr.axis; | ||
| 281 | + node_output->attr.repeats = indirect_output->attr.repeats; | ||
| 282 | + node_output->attr.strides = indirect_output->attr.strides; | ||
| 283 | + indirect_output->attr.dtype = producer->outputs()[0]->attr.dtype; | ||
| 284 | + GELOGD("[IndirectLoad] Move SIMT input pre node[%s] after IndirectLoad[%s].", node->GetNamePtr(), | ||
| 285 | + indirect_load->GetNamePtr()); | ||
| 286 | + return af::SUCCESS; | ||
| 287 | +} | ||
| 288 | + | ||
| 289 | +af::Status MoveSimtInputPreNodes(const af::AscNodePtr &indirect_load, IndirectLoadGraphPaths &paths) { | ||
| 290 | + NodePath nodes; | ||
| 291 | + if (!CollectMovableSimtInputPreNodes(indirect_load, paths.data_input, nodes) || | ||
| 292 | + (!nodes.empty() && | ||
| 293 | + (indirect_load->GetInControlNodesSize() != 0UL || indirect_load->GetOutControlNodesSize() != 0UL)) || | ||
| 294 | + ascgen_utils::indirect_load::GetOnlyOutputConsumer(indirect_load) == nullptr) { | ||
| 295 | + return af::SUCCESS; | ||
| 296 | + } | ||
| 297 | + for (const auto &node : nodes) { | ||
| 298 | + GE_ASSERT_SUCCESS(MoveSimtInputPreNode(node, indirect_load)); | ||
| 299 | + } | ||
| 300 | + paths.data_input.erase(paths.data_input.begin(), paths.data_input.begin() + static_cast<int64_t>(nodes.size())); | ||
| 301 | + paths.output.insert(paths.output.begin(), nodes.rbegin(), nodes.rend()); | ||
| 302 | + return af::SUCCESS; | ||
| 303 | +} | ||
| 304 | + | ||
| 305 | +af::Status PropagateInputTensorAttrs(const IndirectLoadGraphPaths &paths) { | ||
| 306 | + for (const NodePath *path : {&paths.data_input, &paths.index_input}) { | ||
| 307 | + for (size_t i = 0UL; i < path->size(); ++i) { | ||
| 308 | + const auto &load = (*path)[i]; | ||
| 309 | + if (!af::ops::IsOps<af::ascir_op::Load>(load)) { | ||
| 310 | + continue; | ||
| 311 | + } | ||
| 312 | + const af::AscNodePtr data = i + 1UL < path->size() ? (*path)[i + 1UL] : nullptr; | ||
| 313 | + if (data == nullptr || !af::ops::IsOps<af::ascir_op::Data>(data) || load->outputs().empty() || | ||
| 314 | + data->outputs().empty()) { | ||
| 315 | + break; | ||
| 316 | + } | ||
| 317 | + const auto load_out = load->outputs()[0]; | ||
| 318 | + const auto data_out = data->outputs()[0]; | ||
| 319 | + if (data_out->attr.axis.empty() && !load_out->attr.axis.empty()) { | ||
| 320 | + GELOGD("[IndirectLoad] Propagate tensor attrs from Load[%s] to Data[%s].", load->GetNamePtr(), | ||
| 321 | + data->GetNamePtr()); | ||
| 322 | + data_out->attr.axis = load_out->attr.axis; | ||
| 323 | + data_out->attr.repeats = load_out->attr.repeats; | ||
| 324 | + data_out->attr.strides = load_out->attr.strides; | ||
| 325 | + data_out->attr.dtype = load_out->attr.dtype; | ||
| 326 | + } | ||
| 327 | + break; | ||
| 328 | + } | ||
| 329 | + } | ||
| 330 | + return af::SUCCESS; | ||
| 331 | +} | ||
| 332 | + | ||
| 333 | +af::Status RecordTemplateLogicalView(const af::AscNodePtr &indirect_load) { | ||
| 334 | + GE_ASSERT_TRUE(indirect_load->inputs.Size() == 2UL && indirect_load->outputs().size() == 1UL, | ||
| 335 | + "IndirectLoad expects 2 inputs and 1 output."); | ||
| 336 | + ascgen_utils::indirect_load::TemplateLogicalView view; | ||
| 337 | + view.data.axis_ids = indirect_load->inputs()[0]->attr.axis; | ||
| 338 | + view.data.strides = indirect_load->inputs()[0]->attr.strides; | ||
| 339 | + view.index.axis_ids = indirect_load->inputs()[1]->attr.axis; | ||
| 340 | + view.index.strides = indirect_load->inputs()[1]->attr.strides; | ||
| 341 | + view.output.axis_ids = indirect_load->outputs()[0]->attr.axis; | ||
| 342 | + view.output.strides = indirect_load->outputs()[0]->attr.strides; | ||
| 343 | + return ascgen_utils::indirect_load::SetTemplateLogicalView(indirect_load, view); | ||
| 344 | +} | ||
| 345 | + | ||
| 346 | +af::Status PrepareCandidateGraph(const af::AscNodePtr &indirect_load, ascir::TemplateId template_id, | ||
| 347 | + IndirectLoadGraphPaths &paths, bool &is_candidate_legal) { | ||
| 348 | + paths = CollectGraphPaths(indirect_load); | ||
| 349 | + if (template_id == ascir::TemplateId::kIndirectLoadSimt) { | ||
| 350 | + GE_ASSERT_SUCCESS(MoveSimtInputPreNodes(indirect_load, paths)); | ||
| 351 | + } | ||
| 352 | + is_candidate_legal = IsTemplateCandidateLegal(template_id, paths); | ||
| 353 | + if (!is_candidate_legal) { | ||
| 354 | + return af::SUCCESS; | ||
| 355 | + } | ||
| 356 | + GE_ASSERT_SUCCESS(PropagateInputTensorAttrs(paths)); | ||
| 357 | + GE_ASSERT_SUCCESS(RecordTemplateLogicalView(indirect_load)); | ||
| 358 | + GE_ASSERT_SUCCESS(::ascir::SetTemplateId(indirect_load, template_id)); | ||
| 359 | + return af::SUCCESS; | ||
| 360 | +} | ||
| 361 | + | ||
| 362 | +af::Status AnnotateSimdTemplateRoles(const af::AscNodePtr &indirect_load, const IndirectLoadGraphPaths &paths) { | ||
| 363 | + GE_ASSERT_NOTNULL(indirect_load); | ||
| 364 | + GE_ASSERT_SUCCESS( | ||
| 365 | + ascgen_utils::indirect_load::SetTemplateRole(indirect_load, ascgen_utils::indirect_load::TemplateRole::kSimdOp)); | ||
| 366 | + | ||
| 367 | + for (const auto &node : paths.data_input) { | ||
| 368 | + if (af::ops::IsOps<af::ascir_op::Data>(node)) { | ||
| 369 | + break; | ||
| 370 | + } | ||
| 371 | + if (af::ops::IsOps<af::ascir_op::VectorFunc>(node)) { | ||
| 372 | + if (ascgen_utils::indirect_load::GetOnlyOutputConsumer(node).get() == indirect_load.get()) { | ||
| 373 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( | ||
| 374 | + node, ascgen_utils::indirect_load::TemplateRole::kSimdInputPre)); | ||
| 375 | + } | ||
| 376 | + break; | ||
| 377 | + } | ||
| 378 | + if (!ascgen_utils::IsNodeSupportsVectorFunction(node) && !af::ops::IsOps<af::ascir_op::Load>(node)) { | ||
| 379 | + break; | ||
| 380 | + } | ||
| 381 | + GE_ASSERT_SUCCESS( | ||
| 382 | + ascgen_utils::indirect_load::SetTemplateRole(node, ascgen_utils::indirect_load::TemplateRole::kSimdInputPre)); | ||
| 383 | + } | ||
| 384 | + return af::SUCCESS; | ||
| 385 | +} | ||
| 386 | + | ||
| 387 | +af::Status AnnotateSimtInputRoles(const NodePath &path, bool &is_candidate_legal) { | ||
| 388 | + is_candidate_legal = false; | ||
| 389 | + for (const auto &node : path) { | ||
| 390 | + if (af::ops::IsOps<af::ascir_op::Data>(node)) { | ||
| 391 | + is_candidate_legal = true; | ||
| 392 | + return af::SUCCESS; | ||
| 393 | + } | ||
| 394 | + if (af::ops::IsOps<af::ascir_op::Load>(node)) { | ||
| 395 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( | ||
| 396 | + node, ascgen_utils::indirect_load::TemplateRole::kSimtInputBoundary)); | ||
| 397 | + is_candidate_legal = true; | ||
| 398 | + return af::SUCCESS; | ||
| 399 | + } | ||
| 400 | + return af::SUCCESS; | ||
| 401 | + } | ||
| 402 | + return af::SUCCESS; | ||
| 403 | +} | ||
| 404 | + | ||
| 405 | +af::Status AnnotateSimtTransformRoles(const NodePath &path, const std::string &terminal_type, | ||
| 406 | + const std::string &direct_gm_boundary_type, bool &is_candidate_legal) { | ||
| 407 | + is_candidate_legal = false; | ||
| 408 | + for (const auto &node : path) { | ||
| 409 | + if (node->GetType() == direct_gm_boundary_type) { | ||
| 410 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( | ||
| 411 | + node, ascgen_utils::indirect_load::TemplateRole::kSimtDirectGmBoundary)); | ||
| 412 | + is_candidate_legal = true; | ||
| 413 | + return af::SUCCESS; | ||
| 414 | + } | ||
| 415 | + if (node->GetType() == terminal_type) { | ||
| 416 | + is_candidate_legal = true; | ||
| 417 | + return af::SUCCESS; | ||
| 418 | + } | ||
| 419 | + if (af::ops::IsOps<af::ascir_op::VectorFunc>(node) || !CanEmitSimtScalar(node)) { | ||
| 420 | + return af::SUCCESS; | ||
| 421 | + } | ||
| 422 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( | ||
| 423 | + node, ascgen_utils::indirect_load::TemplateRole::kSimtInlineTransform)); | ||
| 424 | + } | ||
| 425 | + return af::SUCCESS; | ||
| 426 | +} | ||
| 427 | + | ||
| 428 | +af::Status AnnotateSimtTemplateRoles(const af::AscNodePtr &indirect_load, const IndirectLoadGraphPaths &paths, | ||
| 429 | + bool &is_candidate_legal) { | ||
| 430 | + GE_ASSERT_NOTNULL(indirect_load); | ||
| 431 | + is_candidate_legal = false; | ||
| 432 | + GE_ASSERT_SUCCESS( | ||
| 433 | + ascgen_utils::indirect_load::SetTemplateRole(indirect_load, ascgen_utils::indirect_load::TemplateRole::kSimtOp)); | ||
| 434 | + GE_ASSERT_SUCCESS(AnnotateSimtInputRoles(paths.data_input, is_candidate_legal)); | ||
| 435 | + if (!is_candidate_legal) { | ||
| 436 | + return af::SUCCESS; | ||
| 437 | + } | ||
| 438 | + GE_ASSERT_SUCCESS(AnnotateSimtTransformRoles(paths.index_input, af::ascir_op::Data::Type, af::ascir_op::Load::Type, | ||
| 439 | + is_candidate_legal)); | ||
| 440 | + if (!is_candidate_legal) { | ||
| 441 | + return af::SUCCESS; | ||
| 442 | + } | ||
| 443 | + GE_ASSERT_SUCCESS(AnnotateSimtTransformRoles(paths.output, af::ascir_op::Output::Type, af::ascir_op::Store::Type, | ||
| 444 | + is_candidate_legal)); | ||
| 445 | + return af::SUCCESS; | ||
| 446 | +} | ||
| 447 | + | ||
| 448 | +af::Status ApplySimdGraphPass(af::AscGraph &graph, const af::AscNodePtr &indirect_load, bool &is_candidate_legal) { | ||
| 449 | + IndirectLoadGraphPaths paths; | ||
| 450 | + GE_ASSERT_SUCCESS( | ||
| 451 | + PrepareCandidateGraph(indirect_load, ascir::TemplateId::kIndirectLoadSimd, paths, is_candidate_legal)); | ||
| 452 | + if (!is_candidate_legal) { | ||
| 453 | + return af::SUCCESS; | ||
| 454 | + } | ||
| 455 | + GE_ASSERT_SUCCESS(NormalizeSimdAxesForTemplate(graph, indirect_load, paths)); | ||
| 456 | + GE_ASSERT_SUCCESS(AnnotateSimdTemplateRoles(indirect_load, paths)); | ||
| 457 | + return af::SUCCESS; | ||
| 458 | +} | ||
| 459 | + | ||
| 460 | +af::Status ApplySimtGraphPass(af::AscGraph &graph, const af::AscNodePtr &indirect_load, bool &is_candidate_legal) { | ||
| 461 | + GELOGD("[IndirectLoad] Apply SIMT graph pass for node[%s], dcache_size[%ld].", indirect_load->GetNamePtr(), | ||
| 462 | + kIndirectLoadSimtDcacheSize); | ||
| 463 | + IndirectLoadGraphPaths paths; | ||
| 464 | + GE_ASSERT_SUCCESS( | ||
| 465 | + PrepareCandidateGraph(indirect_load, ascir::TemplateId::kIndirectLoadSimt, paths, is_candidate_legal)); | ||
| 466 | + if (!is_candidate_legal) { | ||
| 467 | + return af::SUCCESS; | ||
| 468 | + } | ||
| 469 | + GE_ASSERT_SUCCESS(AnnotateSimtTemplateRoles(indirect_load, paths, is_candidate_legal)); | ||
| 470 | + if (!is_candidate_legal) { | ||
| 471 | + return af::SUCCESS; | ||
| 472 | + } | ||
| 473 | + GE_ASSERT_SUCCESS(::ascir::SetDcacheSize(indirect_load, kIndirectLoadSimtDcacheSize)); | ||
| 474 | + GE_ASSERT_SUCCESS(NormalizeSimtAxesForTemplate(graph, indirect_load)); | ||
| 475 | + return af::SUCCESS; | ||
| 476 | +} | ||
| 477 | + | ||
| 478 | +af::Status ApplyGraphPass(af::AscGraph &graph, const af::AscNodePtr &indirect_load, ascir::TemplateId template_id, | ||
| 479 | + bool &is_candidate_legal) { | ||
| 480 | + GELOGD("[IndirectLoad] Apply graph pass for node[%s], template_id[%d].", indirect_load->GetNamePtr(), | ||
| 481 | + static_cast<int32_t>(template_id)); | ||
| 482 | + is_candidate_legal = false; | ||
| 483 | + if (template_id == ascir::TemplateId::kIndirectLoadSimd) { | ||
| 484 | + return ApplySimdGraphPass(graph, indirect_load, is_candidate_legal); | ||
| 485 | + } | ||
| 486 | + if (template_id == ascir::TemplateId::kIndirectLoadSimt) { | ||
| 487 | + return ApplySimtGraphPass(graph, indirect_load, is_candidate_legal); | ||
| 488 | + } | ||
| 489 | + return af::SUCCESS; | ||
| 490 | +} | ||
| 491 | +} // namespace | ||
| 492 | + | ||
| 493 | +Status IndirectLoadScheduleCaseGenerator::Generate(ascir::HintGraph &graph, std::vector<ascir::ImplGraph> &graphs, | ||
| 494 | + std::vector<std::string> &score_functions) { | ||
| 495 | + af::AscNodePtr indirect_load; | ||
| 496 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::ValidateSingleIndirectLoadNode(graph, indirect_load)); | ||
| 497 | + if (indirect_load == nullptr) { | ||
| 498 | + return af::SUCCESS; | ||
| 499 | + } | ||
| 500 | + GE_ASSERT_SUCCESS(ValidateIndirectLoadNode(indirect_load)); | ||
| 501 | + GELOGI("[IndirectLoad] Generate schedule candidates for graph[%s], node[%s].", graph.GetName().c_str(), | ||
| 502 | + indirect_load->GetNamePtr()); | ||
| 503 | + const std::string indirect_load_name = indirect_load->GetName(); | ||
| 504 | + const bool prefer_simd = HasExp2(graph); | ||
| 505 | + for (ascir::TemplateId template_id : {ascir::TemplateId::kIndirectLoadSimd, ascir::TemplateId::kIndirectLoadSimt}) { | ||
| 506 | + ascir::ImplGraph candidate_graph(graph.GetName().c_str()); | ||
| 507 | + GE_ASSERT_TRUE(candidate_graph.CopyFrom(graph), "Failed to copy graph [%s].", graph.GetName().c_str()); | ||
| 508 | + const af::AscNodePtr candidate_indirect_load = candidate_graph.FindNode(indirect_load_name.c_str()); | ||
| 509 | + GE_ASSERT_NOTNULL(candidate_indirect_load, "Failed to find copied IndirectLoad node[%s].", | ||
| 510 | + indirect_load_name.c_str()); | ||
| 511 | + bool is_candidate_legal = false; | ||
| 512 | + GE_ASSERT_SUCCESS(ApplyGraphPass(candidate_graph, candidate_indirect_load, template_id, is_candidate_legal)); | ||
| 513 | + if (!is_candidate_legal) { | ||
| 514 | + GELOGW("[IndirectLoad] Skip illegal template candidate[%d] for node[%s].", static_cast<int32_t>(template_id), | ||
| 515 | + candidate_indirect_load->GetNamePtr()); | ||
| 516 | + continue; | ||
| 517 | + } | ||
| 518 | + graphs.emplace_back(std::move(candidate_graph)); | ||
| 519 | + score_functions.emplace_back(GenerateScoreFunc(template_id, prefer_simd)); | ||
| 520 | + GELOGI("[IndirectLoad] Add schedule candidate[%d] for node[%s].", static_cast<int32_t>(template_id), | ||
| 521 | + candidate_indirect_load->GetNamePtr()); | ||
| 522 | + } | ||
| 523 | + return af::SUCCESS; | ||
| 524 | +} | ||
| 525 | + | ||
| 526 | +} // namespace optimize | ||
| @@ -0,0 +1,24 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace optimize { | ||
| 17 | +class IndirectLoadScheduleCaseGenerator : public FusionCaseGenerator { | ||
| 18 | + public: | ||
| 19 | + Status Generate(ascir::HintGraph &graph, std::vector<ascir::ImplGraph> &graphs, | ||
| 20 | + std::vector<std::string> &score_functions) override; | ||
| 21 | +}; | ||
| 22 | +} // namespace optimize | ||
| 23 | + | ||
| 24 | + | ||
| @@ -161,6 +161,31 @@ class TestAscir: | |||
| 161 | == "Check dtype failed for cast_0 Cast; input_dtypes: [DT_INT8], output_dytpes: [DT_INT4]" | 161 | == "Check dtype failed for cast_0 Cast; input_dtypes: [DT_INT8], output_dytpes: [DT_INT4]" |
| 162 | ) | 162 | ) |
| 163 | 163 | ||
| 164 | + | ||
| 165 | + def test_graph_create_node_with_indirect_load_api(): | ||
| 166 | + ascir.utils.set_platform("3510", 1, 245760) | ||
| 167 | + try: | ||
| 168 | + graph = ascir.HintGraph("test_indirect_load_api") | ||
| 169 | + s0 = graph.create_size("s0") | ||
| 170 | + s1 = graph.create_size("s1") | ||
| 171 | + s2 = graph.create_size("s2") | ||
| 172 | + z0 = graph.create_axis("z0", s0) | ||
| 173 | + z1 = graph.create_axis("z1", s1) | ||
| 174 | + z2 = graph.create_axis("z2", s2) | ||
| 175 | + | ||
| 176 | + x = ascir_api.Data(graph, dtype=ascir.dtypes.float32) | ||
| 177 | + x.axis = [z0, z1, z2] | ||
| 178 | + index = ascir_api.Data(graph, dtype=ascir.dtypes.int32) | ||
| 179 | + index.axis = [z0, z1, z2] | ||
| 180 | + y = ascir_api.IndirectLoad(graph, x, index, axis=1, sched_axis=[z0, z1, z2]) | ||
| 181 | + | ||
| 182 | + assert y.dtype == ascir.dtypes.float32 | ||
| 183 | + debug_str = ascir.utils.debug_str(graph) | ||
| 184 | + assert "IndirectLoad" in debug_str | ||
| 185 | + assert "axis" in debug_str | ||
| 186 | + finally: | ||
| 187 | + ascir.utils.set_platform("2201", 1, 245760) | ||
| 188 | + | ||
| 164 | 189 | ||
| 165 | def test_graph_create_const_node_with_value_str_attr(): | 190 | def test_graph_create_const_node_with_value_str_attr(): |
| 166 | graph = ascir.HintGraph("test") | 191 | graph = ascir.HintGraph("test") |
| @@ -9,6 +9,7 @@ | |||
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | + | ||
| 12 | 13 | ||
| 13 | 14 | ||
| 14 | 15 | ||
| @@ -247,6 +248,7 @@ TEST_F(TestAscendGraphParser, case1) { | |||
| 247 | TEST_F(TestAscendGraphParser, test_gather_graph_parse) { | 248 | TEST_F(TestAscendGraphParser, test_gather_graph_parse) { |
| 248 | af::AscGraph graph1("gather_graph"); | 249 | af::AscGraph graph1("gather_graph"); |
| 249 | ASSERT_EQ(af::ascir::cg::BuildGatherAscendGraphND(graph1), af::SUCCESS); | 250 | ASSERT_EQ(af::ascir::cg::BuildGatherAscendGraphND(graph1), af::SUCCESS); |
| 251 | + ASSERT_EQ(optimize::AscGraphInfoComplete::CompleteApiInfo(graph1), af::SUCCESS); | ||
| 250 | att::TuningSpacePtr tuning_space = std::make_shared<att::TuningSpace>(); | 252 | att::TuningSpacePtr tuning_space = std::make_shared<att::TuningSpace>(); |
| 251 | EXPECT_NE(tuning_space, nullptr); | 253 | EXPECT_NE(tuning_space, nullptr); |
| 252 | att::AscendGraphParser ascend_graph_parser(tuning_space); | 254 | att::AscendGraphParser ascend_graph_parser(tuning_space); |
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | + | ||
| 14 | 15 | ||
| 15 | 16 | ||
| 16 | 17 | ||
| @@ -331,6 +332,7 @@ TEST(AscGraphUbExprBuilderTest, BuildIncludesReservedUbForGather) { | |||
| 331 | af::Operator op("Gather", "Gather"); | 332 | af::Operator op("Gather", "Gather"); |
| 332 | auto node = graph.AddNode(op); | 333 | auto node = graph.AddNode(op); |
| 333 | ASSERT_NE(node, nullptr); | 334 | ASSERT_NE(node, nullptr); |
| 335 | + ASSERT_EQ(optimize::AscGraphInfoComplete::CompleteApiInfo(graph), af::SUCCESS); | ||
| 334 | 336 | ||
| 335 | ascir::UbExprContext context; | 337 | ascir::UbExprContext context; |
| 336 | EXPECT_EQ(ascir::AscGraphUbExprBuilder().Build(graph, context), af::SUCCESS); | 338 | EXPECT_EQ(ascir::AscGraphUbExprBuilder().Build(graph, context), af::SUCCESS); |
| @@ -0,0 +1,442 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +namespace { | ||
| 26 | +constexpr int64_t kSimtDcacheSize = 32 * 1024; | ||
| 27 | + | ||
| 28 | +std::vector<std::string> AxisNames(af::AscGraph &graph, const std::vector<af::AxisId> &axis_ids) { | ||
| 29 | + std::vector<std::string> names; | ||
| 30 | + names.reserve(axis_ids.size()); | ||
| 31 | + for (af::AxisId axis_id : axis_ids) { | ||
| 32 | + const auto *axis = graph.FindAxis(axis_id); | ||
| 33 | + names.push_back(axis == nullptr ? "" : axis->name); | ||
| 34 | + } | ||
| 35 | + return names; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +af::AxisId FindAxisByName(af::AscGraph &graph, const std::string &name) { | ||
| 39 | + for (const auto &axis : graph.GetAllAxis()) { | ||
| 40 | + if (axis != nullptr && axis->name == name) { | ||
| 41 | + return axis->id; | ||
| 42 | + } | ||
| 43 | + } | ||
| 44 | + return af::kIdNone; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +void ExpectAxisNames(af::AscGraph &graph, const std::vector<af::AxisId> &axis_ids, | ||
| 48 | + const std::vector<std::string> &expected) { | ||
| 49 | + EXPECT_EQ(AxisNames(graph, axis_ids), expected); | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +void ExpectMergedFrom(af::AscGraph &graph, const std::string &merged_name, | ||
| 53 | + const std::vector<std::string> &expected_from) { | ||
| 54 | + const af::AxisId merged_axis = FindAxisByName(graph, merged_name); | ||
| 55 | + ASSERT_NE(merged_axis, af::kIdNone); | ||
| 56 | + const auto *axis = graph.FindAxis(merged_axis); | ||
| 57 | + ASSERT_NE(axis, nullptr); | ||
| 58 | + EXPECT_EQ(axis->type, af::Axis::Type::kAxisTypeMerged); | ||
| 59 | + ExpectAxisNames(graph, axis->from, expected_from); | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +void ExpectFixedTileSplit(af::AscGraph &graph, const std::string &axis_name) { | ||
| 63 | + const af::AxisId outer_axis_id = FindAxisByName(graph, axis_name); | ||
| 64 | + const af::AxisId tile_outer_axis_id = FindAxisByName(graph, axis_name + "T"); | ||
| 65 | + const af::AxisId tile_inner_axis_id = FindAxisByName(graph, axis_name + "t"); | ||
| 66 | + ASSERT_NE(outer_axis_id, af::kIdNone); | ||
| 67 | + ASSERT_NE(tile_outer_axis_id, af::kIdNone); | ||
| 68 | + ASSERT_NE(tile_inner_axis_id, af::kIdNone); | ||
| 69 | + | ||
| 70 | + const auto *tile_outer_axis = graph.FindAxis(tile_outer_axis_id); | ||
| 71 | + const auto *tile_inner_axis = graph.FindAxis(tile_inner_axis_id); | ||
| 72 | + ASSERT_NE(tile_outer_axis, nullptr); | ||
| 73 | + ASSERT_NE(tile_inner_axis, nullptr); | ||
| 74 | + EXPECT_EQ(tile_outer_axis->type, af::Axis::Type::kAxisTypeTileOuter); | ||
| 75 | + EXPECT_EQ(tile_inner_axis->type, af::Axis::Type::kAxisTypeTileInner); | ||
| 76 | + ExpectAxisNames(graph, tile_outer_axis->from, {axis_name}); | ||
| 77 | + ExpectAxisNames(graph, tile_inner_axis->from, {axis_name}); | ||
| 78 | + EXPECT_EQ(tile_outer_axis->split_pair_other_id, tile_inner_axis_id); | ||
| 79 | + EXPECT_EQ(tile_inner_axis->split_pair_other_id, tile_outer_axis_id); | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +af::AscGraph BuildIndirectLoadGraph(int64_t axis, bool has_input_pre_node = false) { | ||
| 83 | + af::AscGraph graph("indirect_load_ut_graph"); | ||
| 84 | + const af::Expression s0 = graph.CreateSizeVar("s0"); | ||
| 85 | + const af::Expression s1 = graph.CreateSizeVar("s1"); | ||
| 86 | + const af::Expression s2 = graph.CreateSizeVar("s2"); | ||
| 87 | + const af::Expression s3 = graph.CreateSizeVar("s3"); | ||
| 88 | + const af::Expression s4 = graph.CreateSizeVar("s4"); | ||
| 89 | + const af::Expression s5 = graph.CreateSizeVar("s5"); | ||
| 90 | + const af::Expression s6 = graph.CreateSizeVar("s6"); | ||
| 91 | + const af::Expression s7 = graph.CreateSizeVar("s7"); | ||
| 92 | + const auto z0 = graph.CreateAxis("z0", s0); | ||
| 93 | + const auto z1 = graph.CreateAxis("z1", s1); | ||
| 94 | + const auto z2 = graph.CreateAxis("z2", s2); | ||
| 95 | + const auto z3 = graph.CreateAxis("z3", s3); | ||
| 96 | + const auto z4 = graph.CreateAxis("z4", s4); | ||
| 97 | + const auto z5 = graph.CreateAxis("z5", s5); | ||
| 98 | + const auto z6 = graph.CreateAxis("z6", s6); | ||
| 99 | + const auto z7 = graph.CreateAxis("z7", s7); | ||
| 100 | + const std::vector<af::AxisId> input_axes = {z0.id, z1.id, z2.id, z3.id}; | ||
| 101 | + const std::vector<af::AxisId> output_axes = {z4.id, z5.id, z6.id, z7.id}; | ||
| 102 | + const std::vector<af::Expression> input_repeats = {s0, s1, s2, s3}; | ||
| 103 | + const std::vector<af::Expression> output_repeats = {s4, s5, s6, s7}; | ||
| 104 | + const std::vector<af::Expression> input_strides = {s1 * s2 * s3, s2 * s3, s3, af::sym::kSymbolOne}; | ||
| 105 | + const std::vector<af::Expression> output_strides = {s5 * s6 * s7, s6 * s7, s7, af::sym::kSymbolOne}; | ||
| 106 | + | ||
| 107 | + af::ascir_op::Data x("x", graph); | ||
| 108 | + x.y.dtype = ge::DT_FLOAT16; | ||
| 109 | + x.attr.api.compute_type = af::ComputeType::kComputeInvalid; | ||
| 110 | + x.attr.api.type = af::ApiType::kAPITypeBuffer; | ||
| 111 | + x.ir_attr.SetIndex(0); | ||
| 112 | + x.attr.sched.axis = input_axes; | ||
| 113 | + *x.y.axis = input_axes; | ||
| 114 | + *x.y.repeats = input_repeats; | ||
| 115 | + *x.y.strides = input_strides; | ||
| 116 | + | ||
| 117 | + af::ascir_op::Data index("index", graph); | ||
| 118 | + index.y.dtype = ge::DT_INT32; | ||
| 119 | + index.attr.api.compute_type = af::ComputeType::kComputeInvalid; | ||
| 120 | + index.attr.api.type = af::ApiType::kAPITypeBuffer; | ||
| 121 | + index.ir_attr.SetIndex(1); | ||
| 122 | + index.attr.sched.axis = output_axes; | ||
| 123 | + *index.y.axis = output_axes; | ||
| 124 | + *index.y.repeats = output_repeats; | ||
| 125 | + *index.y.strides = output_strides; | ||
| 126 | + | ||
| 127 | + af::ascir_op::IndirectLoad indirect_load("indirect_load"); | ||
| 128 | + if (has_input_pre_node) { | ||
| 129 | + af::ascir_op::Abs pre_abs("pre_abs"); | ||
| 130 | + pre_abs.x = x.y; | ||
| 131 | + pre_abs.y.dtype = ge::DT_FLOAT16; | ||
| 132 | + pre_abs.attr.sched.axis = input_axes; | ||
| 133 | + *pre_abs.y.axis = input_axes; | ||
| 134 | + *pre_abs.y.repeats = input_repeats; | ||
| 135 | + *pre_abs.y.strides = input_strides; | ||
| 136 | + indirect_load.x1 = pre_abs.y; | ||
| 137 | + } else { | ||
| 138 | + indirect_load.x1 = x.y; | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + indirect_load.x2 = index.y; | ||
| 142 | + indirect_load.y.dtype = ge::DT_FLOAT16; | ||
| 143 | + indirect_load.attr.sched.axis = output_axes; | ||
| 144 | + indirect_load.ir_attr.SetAxis(axis); | ||
| 145 | + *indirect_load.y.axis = output_axes; | ||
| 146 | + *indirect_load.y.repeats = output_repeats; | ||
| 147 | + *indirect_load.y.strides = output_strides; | ||
| 148 | + | ||
| 149 | + af::ascir_op::Output y("y"); | ||
| 150 | + y.x = indirect_load.y; | ||
| 151 | + y.y.dtype = ge::DT_FLOAT16; | ||
| 152 | + y.attr.api.compute_type = af::ComputeType::kComputeInvalid; | ||
| 153 | + y.attr.api.type = af::ApiType::kAPITypeBuffer; | ||
| 154 | + y.ir_attr.SetIndex(0); | ||
| 155 | + y.attr.sched.axis = output_axes; | ||
| 156 | + *y.y.axis = output_axes; | ||
| 157 | + *y.y.repeats = output_repeats; | ||
| 158 | + *y.y.strides = output_strides; | ||
| 159 | + | ||
| 160 | + return graph; | ||
| 161 | +} | ||
| 162 | + | ||
| 163 | +template <typename Op> | ||
| 164 | +void SetNodeView(Op &op, af::DataType dtype, const std::vector<af::AxisId> &axes, | ||
| 165 | + const std::vector<af::Expression> &repeats, const std::vector<af::Expression> &strides) { | ||
| 166 | + op.y.dtype = dtype; | ||
| 167 | + op.attr.sched.axis = axes; | ||
| 168 | + *op.y.axis = axes; | ||
| 169 | + *op.y.repeats = repeats; | ||
| 170 | + *op.y.strides = strides; | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +af::AscGraph BuildIndirectLoadPrecisionCastGraph() { | ||
| 174 | + af::AscGraph graph("indirect_load_precision_cast_ut_graph"); | ||
| 175 | + const af::Expression s0 = graph.CreateSizeVar("s0"); | ||
| 176 | + const af::Expression s1 = graph.CreateSizeVar("s1"); | ||
| 177 | + const af::Expression s2 = graph.CreateSizeVar("s2"); | ||
| 178 | + const af::Expression s3 = graph.CreateSizeVar("s3"); | ||
| 179 | + const auto z0 = graph.CreateAxis("z0", s0); | ||
| 180 | + const auto z1 = graph.CreateAxis("z1", s1); | ||
| 181 | + const auto z2 = graph.CreateAxis("z2", s2); | ||
| 182 | + const auto z3 = graph.CreateAxis("z3", s3); | ||
| 183 | + const std::vector<af::AxisId> input_axes = {z0.id, z1.id}; | ||
| 184 | + const std::vector<af::AxisId> output_axes = {z2.id, z3.id}; | ||
| 185 | + const std::vector<af::Expression> input_repeats = {s0, s1}; | ||
| 186 | + const std::vector<af::Expression> output_repeats = {s2, s3}; | ||
| 187 | + const std::vector<af::Expression> input_strides = {s1, af::sym::kSymbolOne}; | ||
| 188 | + const std::vector<af::Expression> output_strides = {s3, af::sym::kSymbolOne}; | ||
| 189 | + | ||
| 190 | + af::ascir_op::Data x("x", graph); | ||
| 191 | + x.ir_attr.SetIndex(0); | ||
| 192 | + SetNodeView(x, af::DT_FLOAT16, input_axes, input_repeats, input_strides); | ||
| 193 | + af::ascir_op::Load input_load("input_load"); | ||
| 194 | + input_load.x = x.y; | ||
| 195 | + SetNodeView(input_load, af::DT_FLOAT16, input_axes, input_repeats, input_strides); | ||
| 196 | + af::ascir_op::Cast input_cast("input_cast"); | ||
| 197 | + input_cast.x = input_load.y; | ||
| 198 | + SetNodeView(input_cast, af::DT_FLOAT, input_axes, input_repeats, input_strides); | ||
| 199 | + | ||
| 200 | + af::ascir_op::Data index("index", graph); | ||
| 201 | + index.ir_attr.SetIndex(1); | ||
| 202 | + SetNodeView(index, af::DT_INT32, output_axes, output_repeats, output_strides); | ||
| 203 | + | ||
| 204 | + af::ascir_op::IndirectLoad indirect_load("indirect_load"); | ||
| 205 | + indirect_load.x1 = input_cast.y; | ||
| 206 | + indirect_load.x2 = index.y; | ||
| 207 | + indirect_load.ir_attr.SetAxis(1); | ||
| 208 | + SetNodeView(indirect_load, af::DT_FLOAT, output_axes, output_repeats, output_strides); | ||
| 209 | + af::ascir_op::Exp output_exp("output_exp"); | ||
| 210 | + output_exp.x = indirect_load.y; | ||
| 211 | + SetNodeView(output_exp, af::DT_FLOAT, output_axes, output_repeats, output_strides); | ||
| 212 | + af::ascir_op::Cast output_cast("output_cast"); | ||
| 213 | + output_cast.x = output_exp.y; | ||
| 214 | + SetNodeView(output_cast, af::DT_FLOAT16, output_axes, output_repeats, output_strides); | ||
| 215 | + af::ascir_op::Store store("store"); | ||
| 216 | + store.x = output_cast.y; | ||
| 217 | + SetNodeView(store, af::DT_FLOAT16, output_axes, output_repeats, output_strides); | ||
| 218 | + af::ascir_op::Output y("y"); | ||
| 219 | + y.x = store.y; | ||
| 220 | + y.ir_attr.SetIndex(0); | ||
| 221 | + SetNodeView(y, af::DT_FLOAT16, output_axes, output_repeats, output_strides); | ||
| 222 | + return graph; | ||
| 223 | +} | ||
| 224 | + | ||
| 225 | +std::vector<af::AscGraph> GenerateIndirectLoadCases(int64_t axis) { | ||
| 226 | + auto graph = BuildIndirectLoadGraph(axis); | ||
| 227 | + optimize::IndirectLoadScheduleCaseGenerator generator; | ||
| 228 | + std::vector<af::AscGraph> graphs; | ||
| 229 | + std::vector<std::string> score_functions; | ||
| 230 | + EXPECT_EQ(generator.Generate(graph, graphs, score_functions), af::SUCCESS); | ||
| 231 | + EXPECT_EQ(graphs.size(), 2UL); | ||
| 232 | + EXPECT_EQ(score_functions.size(), 2UL); | ||
| 233 | + return graphs; | ||
| 234 | +} | ||
| 235 | + | ||
| 236 | +af::AscGraph &FindGeneratedGraphByTemplate(std::vector<af::AscGraph> &graphs, ascir::TemplateId template_id) { | ||
| 237 | + const auto iter = std::find_if(graphs.begin(), graphs.end(), [template_id](const af::AscGraph &graph) { | ||
| 238 | + const auto node = graph.FindNode("indirect_load"); | ||
| 239 | + return node != nullptr && ascir::GetTemplateIdOrDefault(*node) == template_id; | ||
| 240 | + }); | ||
| 241 | + EXPECT_NE(iter, graphs.end()); | ||
| 242 | + return *iter; | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +class IndirectLoadScheduleCaseGeneratorTest : public ::testing::TestWithParam<int64_t> {}; | ||
| 246 | + | ||
| 247 | +TEST(IndirectLoadScheduleCaseGeneratorTest, SimtSetsDcacheAndUsesUnifiedVectorizedAxisWithoutReduce) { | ||
| 248 | + auto graphs = GenerateIndirectLoadCases(2); | ||
| 249 | + auto &simt_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimt); | ||
| 250 | + const auto indirect_load = simt_graph.FindNode("indirect_load"); | ||
| 251 | + ASSERT_NE(indirect_load, nullptr); | ||
| 252 | + | ||
| 253 | + EXPECT_EQ(ascir::GetDcacheSize(*indirect_load), kSimtDcacheSize); | ||
| 254 | + ExpectMergedFrom(simt_graph, "indirect_load_outer", {"z4", "z5", "z6", "z7"}); | ||
| 255 | + ascgen_utils::indirect_load::TemplateAxes axes; | ||
| 256 | + ASSERT_EQ(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, axes), af::SUCCESS); | ||
| 257 | + ExpectAxisNames(simt_graph, indirect_load->attr.sched.axis, {"z4", "z5", "z6", "z7"}); | ||
| 258 | + ExpectAxisNames(simt_graph, {axes.outer_axis}, {"indirect_load_outer"}); | ||
| 259 | + EXPECT_TRUE(indirect_load->outputs()[0]->attr.vectorized_axis.empty()); | ||
| 260 | + EXPECT_TRUE(indirect_load->outputs()[0]->attr.vectorized_strides.empty()); | ||
| 261 | +} | ||
| 262 | + | ||
| 263 | +TEST(IndirectLoadScheduleCaseGeneratorTest, StoresTemplateAxesWithoutOverwritingSchedAxis) { | ||
| 264 | + auto graphs = GenerateIndirectLoadCases(2); | ||
| 265 | + auto &simd_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimd); | ||
| 266 | + const auto indirect_load = simd_graph.FindNode("indirect_load"); | ||
| 267 | + ASSERT_NE(indirect_load, nullptr); | ||
| 268 | + | ||
| 269 | + ascgen_utils::indirect_load::TemplateAxes axes; | ||
| 270 | + ASSERT_EQ(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, axes), af::SUCCESS); | ||
| 271 | + ExpectAxisNames(simd_graph, indirect_load->attr.sched.axis, {"z4", "z5", "z6", "z7"}); | ||
| 272 | + ExpectAxisNames(simd_graph, {axes.outer_axis}, {"indirect_load_outer"}); | ||
| 273 | + ExpectAxisNames(simd_graph, {axes.inner_axis}, {"indirect_load_inner"}); | ||
| 274 | + ExpectFixedTileSplit(simd_graph, "indirect_load_outer"); | ||
| 275 | + | ||
| 276 | + ascgen_utils::indirect_load::TemplateLogicalView logical_view; | ||
| 277 | + ASSERT_EQ(ascgen_utils::indirect_load::GetTemplateLogicalView(indirect_load, logical_view), af::SUCCESS); | ||
| 278 | + ExpectAxisNames(simd_graph, logical_view.data.axis_ids, {"z0", "z1", "z2", "z3"}); | ||
| 279 | + ExpectAxisNames(simd_graph, logical_view.index.axis_ids, {"z4", "z5", "z6", "z7"}); | ||
| 280 | + ExpectAxisNames(simd_graph, logical_view.output.axis_ids, {"z4", "z5", "z6", "z7"}); | ||
| 281 | + EXPECT_EQ(logical_view.data.strides.size(), 4UL); | ||
| 282 | + EXPECT_EQ(logical_view.index.strides.size(), 4UL); | ||
| 283 | + EXPECT_EQ(logical_view.output.strides.size(), 4UL); | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | +TEST(IndirectLoadScheduleCaseGeneratorTest, DoesNotStoreFixedTileAxesAsTemplateMetadata) { | ||
| 287 | + auto graphs = GenerateIndirectLoadCases(2); | ||
| 288 | + auto &simd_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimd); | ||
| 289 | + const auto indirect_load = simd_graph.FindNode("indirect_load"); | ||
| 290 | + ASSERT_NE(indirect_load, nullptr); | ||
| 291 | + const auto op_desc = indirect_load->GetOpDesc(); | ||
| 292 | + ASSERT_NE(op_desc, nullptr); | ||
| 293 | + | ||
| 294 | + EXPECT_EQ(op_desc->TryGetExtAttr("af.internal.indirect_load.tile_outer_axis", static_cast<int64_t>(af::kIdNone)), | ||
| 295 | + af::kIdNone); | ||
| 296 | + EXPECT_EQ(op_desc->TryGetExtAttr("af.internal.indirect_load.tile_inner_axis", static_cast<int64_t>(af::kIdNone)), | ||
| 297 | + af::kIdNone); | ||
| 298 | +} | ||
| 299 | + | ||
| 300 | +TEST(IndirectLoadScheduleCaseGeneratorTest, StoresInputVectorizedAxisAsTemplateMetadata) { | ||
| 301 | + auto graph = BuildIndirectLoadGraph(2, true); | ||
| 302 | + optimize::IndirectLoadScheduleCaseGenerator generator; | ||
| 303 | + std::vector<af::AscGraph> graphs; | ||
| 304 | + std::vector<std::string> score_functions; | ||
| 305 | + ASSERT_EQ(generator.Generate(graph, graphs, score_functions), af::SUCCESS); | ||
| 306 | + auto &simd_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimd); | ||
| 307 | + const auto indirect_load = simd_graph.FindNode("indirect_load"); | ||
| 308 | + const auto pre_abs = simd_graph.FindNode("pre_abs"); | ||
| 309 | + ASSERT_NE(indirect_load, nullptr); | ||
| 310 | + ASSERT_NE(pre_abs, nullptr); | ||
| 311 | + | ||
| 312 | + ascgen_utils::indirect_load::TemplateAxes axes; | ||
| 313 | + ASSERT_EQ(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, axes), af::SUCCESS); | ||
| 314 | + ExpectAxisNames(simd_graph, pre_abs->attr.sched.axis, {"z0", "z1", "z2", "z3"}); | ||
| 315 | + ExpectAxisNames(simd_graph, pre_abs->outputs()[0]->attr.axis, {"z0", "z1", "z2", "z3"}); | ||
| 316 | + EXPECT_TRUE(pre_abs->outputs()[0]->attr.vectorized_axis.empty()); | ||
| 317 | + ExpectAxisNames(simd_graph, {axes.input_inner_axis}, {"indirect_load_input_inner"}); | ||
| 318 | +} | ||
| 319 | + | ||
| 320 | +TEST(IndirectLoadScheduleCaseGeneratorTest, GeneratedSimdCandidateKeepsPublicBehavior) { | ||
| 321 | + auto graph = BuildIndirectLoadGraph(2, true); | ||
| 322 | + optimize::IndirectLoadScheduleCaseGenerator generator; | ||
| 323 | + std::vector<af::AscGraph> graphs; | ||
| 324 | + std::vector<std::string> score_functions; | ||
| 325 | + ASSERT_EQ(generator.Generate(graph, graphs, score_functions), af::SUCCESS); | ||
| 326 | + auto &simd_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimd); | ||
| 327 | + const auto indirect_load = simd_graph.FindNode("indirect_load"); | ||
| 328 | + ASSERT_NE(indirect_load, nullptr); | ||
| 329 | + | ||
| 330 | + const auto il_behavior = ascgen_utils::indirect_load::GetTemplateBehavior(indirect_load); | ||
| 331 | + EXPECT_FALSE(il_behavior.skips_main_schedule_tiling); | ||
| 332 | + EXPECT_FALSE(il_behavior.skips_api_emit); | ||
| 333 | + EXPECT_FALSE(il_behavior.uses_direct_gm_pipeline); | ||
| 334 | + EXPECT_FALSE(il_behavior.skips_ub_lifecycle); | ||
| 335 | + EXPECT_FALSE(il_behavior.skips_ub_expr); | ||
| 336 | + EXPECT_FALSE(il_behavior.preserves_vectorized_axis); | ||
| 337 | + EXPECT_FALSE(ascgen_utils::indirect_load::ShouldDisableRegularVectorFunc(indirect_load)); | ||
| 338 | +} | ||
| 339 | + | ||
| 340 | +TEST(IndirectLoadScheduleCaseGeneratorTest, GeneratedSimtCandidateKeepsPublicBehavior) { | ||
| 341 | + auto graphs = GenerateIndirectLoadCases(2); | ||
| 342 | + auto &simt_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimt); | ||
| 343 | + const auto indirect_load = simt_graph.FindNode("indirect_load"); | ||
| 344 | + ASSERT_NE(indirect_load, nullptr); | ||
| 345 | + | ||
| 346 | + const auto behavior = ascgen_utils::indirect_load::GetTemplateBehavior(indirect_load); | ||
| 347 | + EXPECT_FALSE(behavior.skips_main_schedule_tiling); | ||
| 348 | + EXPECT_FALSE(behavior.skips_api_emit); | ||
| 349 | + EXPECT_TRUE(behavior.uses_direct_gm_pipeline); | ||
| 350 | + EXPECT_TRUE(behavior.skips_ub_lifecycle); | ||
| 351 | + EXPECT_TRUE(behavior.skips_ub_expr); | ||
| 352 | + EXPECT_TRUE(behavior.preserves_vectorized_axis); | ||
| 353 | + EXPECT_FALSE(ascgen_utils::indirect_load::ShouldApplyInputInnerVectorization(indirect_load)); | ||
| 354 | + EXPECT_FALSE(ascgen_utils::indirect_load::ShouldSkipMainScheduleTiling(indirect_load)); | ||
| 355 | + EXPECT_TRUE(ascgen_utils::indirect_load::ShouldPreserveVectorizedAxis(indirect_load)); | ||
| 356 | + EXPECT_TRUE(ascgen_utils::indirect_load::ShouldDisableRegularVectorFunc(indirect_load)); | ||
| 357 | +} | ||
| 358 | + | ||
| 359 | +TEST(IndirectLoadScheduleCaseGeneratorTest, SimtMovesInputPrecisionCastAfterIndirectLoad) { | ||
| 360 | + auto graph = BuildIndirectLoadPrecisionCastGraph(); | ||
| 361 | + optimize::IndirectLoadScheduleCaseGenerator generator; | ||
| 362 | + std::vector<af::AscGraph> graphs; | ||
| 363 | + std::vector<std::string> score_functions; | ||
| 364 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 365 | + const auto status = generator.Generate(graph, graphs, score_functions); | ||
| 366 | + ge::PlatformContext::GetInstance().Reset(); | ||
| 367 | + ASSERT_EQ(status, af::SUCCESS); | ||
| 368 | + ASSERT_EQ(graphs.size(), 2UL); | ||
| 369 | + | ||
| 370 | + auto &simd_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimd); | ||
| 371 | + EXPECT_NE(simd_graph.FindNode("input_cast"), nullptr); | ||
| 372 | + EXPECT_NE(simd_graph.FindNode("output_cast"), nullptr); | ||
| 373 | + ASSERT_NE(simd_graph.FindNode("indirect_load"), nullptr); | ||
| 374 | + ASSERT_NE(simd_graph.FindNode("output_exp"), nullptr); | ||
| 375 | + EXPECT_EQ(simd_graph.FindNode("indirect_load")->outputs()[0]->attr.dtype, af::DT_FLOAT); | ||
| 376 | + EXPECT_EQ(simd_graph.FindNode("output_exp")->outputs()[0]->attr.dtype, af::DT_FLOAT); | ||
| 377 | + | ||
| 378 | + auto &simt_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimt); | ||
| 379 | + const auto input_cast = simt_graph.FindNode("input_cast"); | ||
| 380 | + EXPECT_NE(input_cast, nullptr); | ||
| 381 | + EXPECT_NE(simt_graph.FindNode("output_cast"), nullptr); | ||
| 382 | + const auto simt_indirect_load = simt_graph.FindNode("indirect_load"); | ||
| 383 | + const auto output_exp = simt_graph.FindNode("output_exp"); | ||
| 384 | + ASSERT_NE(simt_indirect_load, nullptr); | ||
| 385 | + ASSERT_NE(output_exp, nullptr); | ||
| 386 | + EXPECT_EQ(simt_indirect_load->outputs()[0]->attr.dtype, af::DT_FLOAT16); | ||
| 387 | + EXPECT_EQ(output_exp->outputs()[0]->attr.dtype, af::DT_FLOAT); | ||
| 388 | + const auto input_producer = ascgen_utils::indirect_load::GetInputProducer(simt_indirect_load, 0UL); | ||
| 389 | + const auto cast_producer = ascgen_utils::indirect_load::GetInputProducer(input_cast, 0UL); | ||
| 390 | + const auto cast_consumer = ascgen_utils::indirect_load::GetOnlyOutputConsumer(input_cast); | ||
| 391 | + ASSERT_NE(input_producer, nullptr); | ||
| 392 | + EXPECT_EQ(input_producer->GetName(), "input_load"); | ||
| 393 | + EXPECT_EQ(cast_producer, simt_indirect_load); | ||
| 394 | + EXPECT_EQ(cast_consumer, output_exp); | ||
| 395 | +} | ||
| 396 | + | ||
| 397 | +TEST_P(IndirectLoadScheduleCaseGeneratorTest, SimdSplitsOuterAndInnerAxesByNormalizedAxis) { | ||
| 398 | + const int64_t axis = GetParam(); | ||
| 399 | + auto graphs = GenerateIndirectLoadCases(axis); | ||
| 400 | + auto &simd_graph = FindGeneratedGraphByTemplate(graphs, ascir::TemplateId::kIndirectLoadSimd); | ||
| 401 | + const auto indirect_load = simd_graph.FindNode("indirect_load"); | ||
| 402 | + ASSERT_NE(indirect_load, nullptr); | ||
| 403 | + | ||
| 404 | + const int64_t output_rank = 4L; | ||
| 405 | + const size_t expected_axis_index = static_cast<size_t>(axis < 0L ? axis + output_rank : axis); | ||
| 406 | + const std::vector<std::string> output_axis_names = {"z4", "z5", "z6", "z7"}; | ||
| 407 | + std::vector<std::string> expected_outer; | ||
| 408 | + std::vector<std::string> expected_inner; | ||
| 409 | + expected_outer.assign(output_axis_names.begin(), | ||
| 410 | + output_axis_names.begin() + static_cast<int64_t>(expected_axis_index)); | ||
| 411 | + expected_inner.assign(output_axis_names.begin() + static_cast<int64_t>(expected_axis_index), output_axis_names.end()); | ||
| 412 | + | ||
| 413 | + ascgen_utils::indirect_load::TemplateAxes axes; | ||
| 414 | + ASSERT_EQ(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, axes), af::SUCCESS); | ||
| 415 | + ExpectAxisNames(simd_graph, indirect_load->attr.sched.axis, output_axis_names); | ||
| 416 | + if (expected_outer.empty()) { | ||
| 417 | + EXPECT_NE(FindAxisByName(simd_graph, "indirect_load_single_outer"), af::kIdNone); | ||
| 418 | + ExpectAxisNames(simd_graph, {axes.outer_axis}, {"indirect_load_single_outer"}); | ||
| 419 | + } else { | ||
| 420 | + ExpectMergedFrom(simd_graph, "indirect_load_outer", expected_outer); | ||
| 421 | + ExpectAxisNames(simd_graph, {axes.outer_axis}, {"indirect_load_outer"}); | ||
| 422 | + } | ||
| 423 | + | ||
| 424 | + if (expected_inner.size() > 1UL) { | ||
| 425 | + ExpectMergedFrom(simd_graph, "indirect_load_inner", expected_inner); | ||
| 426 | + } | ||
| 427 | +} | ||
| 428 | + | ||
| 429 | +TEST(IndirectLoadScheduleCaseGeneratorTest, GenerateFailsWhenAxisOutOfRange) { | ||
| 430 | + for (int64_t axis : {-5L, 8L}) { | ||
| 431 | + auto graph = BuildIndirectLoadGraph(axis); | ||
| 432 | + optimize::IndirectLoadScheduleCaseGenerator generator; | ||
| 433 | + std::vector<af::AscGraph> graphs; | ||
| 434 | + std::vector<std::string> score_functions; | ||
| 435 | + EXPECT_NE(generator.Generate(graph, graphs, score_functions), af::SUCCESS) << "axis=" << axis; | ||
| 436 | + EXPECT_TRUE(graphs.empty()); | ||
| 437 | + EXPECT_TRUE(score_functions.empty()); | ||
| 438 | + } | ||
| 439 | +} | ||
| 440 | + | ||
| 441 | +INSTANTIATE_TEST_SUITE_P(AxisBoundary, IndirectLoadScheduleCaseGeneratorTest, ::testing::Values(-1, 0, 2, 3)); | ||
| 442 | +} // namespace | ||
| @@ -2200,6 +2200,38 @@ TEST_F(TestOptimizer, MergeAxesGatherOnlyOneDim) { | |||
| 2200 | EXPECT_EQ(new_axis[3]->size, s1 * s2); | 2200 | EXPECT_EQ(new_axis[3]->size, s1 * s2); |
| 2201 | } | 2201 | } |
| 2202 | 2202 | ||
| 2203 | +TEST_F(TestOptimizer, CompleteGatherApiInfoSetsDcacheSize) { | ||
| 2204 | + constexpr int64_t kSimtDcacheSize = 32 * 1024; | ||
| 2205 | + af::AscGraph graph("GatherDcacheGraph"); | ||
| 2206 | + | ||
| 2207 | + af::ascir_op::Data data0("data0", graph); | ||
| 2208 | + data0.y.dtype = ge::DT_FLOAT16; | ||
| 2209 | + data0.ir_attr.SetIndex(0); | ||
| 2210 | + | ||
| 2211 | + af::ascir_op::Data data1("data1", graph); | ||
| 2212 | + data1.y.dtype = ge::DT_INT32; | ||
| 2213 | + data1.ir_attr.SetIndex(1); | ||
| 2214 | + | ||
| 2215 | + af::ascir_op::Gather gather("gather"); | ||
| 2216 | + gather.x1 = data0.y; | ||
| 2217 | + gather.x2 = data1.y; | ||
| 2218 | + gather.ir_attr.SetAxis(0); | ||
| 2219 | + gather.y.dtype = ge::DT_FLOAT16; | ||
| 2220 | + | ||
| 2221 | + af::ascir_op::Output output("output"); | ||
| 2222 | + output.x = gather.y; | ||
| 2223 | + output.y.dtype = ge::DT_FLOAT16; | ||
| 2224 | + output.ir_attr.SetIndex(0); | ||
| 2225 | + | ||
| 2226 | + auto gather_node = graph.FindNode("gather"); | ||
| 2227 | + ASSERT_NE(gather_node, nullptr); | ||
| 2228 | + gather_node->attr.api.compute_type = af::ComputeType::kComputeGather; | ||
| 2229 | + EXPECT_EQ(::ascir::GetDcacheSize(*gather_node), 0); | ||
| 2230 | + | ||
| 2231 | + ASSERT_EQ(optimize::AscGraphInfoComplete::CompleteApiInfo(graph), af::SUCCESS); | ||
| 2232 | + EXPECT_EQ(::ascir::GetDcacheSize(*gather_node), kSimtDcacheSize); | ||
| 2233 | +} | ||
| 2234 | + | ||
| 2203 | TEST_F(TestOptimizer, MergeAxesReduce) { | 2235 | TEST_F(TestOptimizer, MergeAxesReduce) { |
| 2204 | af::AscGraph graph("LoadAbsStore"); | 2236 | af::AscGraph graph("LoadAbsStore"); |
| 2205 | auto s0 = graph.CreateSizeVar("s0"); | 2237 | auto s0 = graph.CreateSizeVar("s0"); |
| @@ -10,10 +10,12 @@ | |||
| 10 | # ----------------------------------------------------------------------------------------------------------- | 10 | # ----------------------------------------------------------------------------------------------------------- |
| 11 | 11 | ||
| 12 | import pytest | 12 | import pytest |
| 13 | +import importlib.util | ||
| 13 | import json | 14 | import json |
| 14 | import time | 15 | import time |
| 15 | import os | 16 | import os |
| 16 | import shutil | 17 | import shutil |
| 18 | +import sys | ||
| 17 | from autofuse.pyautofuse import ascir, Autofuser, AutofuserOptions, Schedule, CodeGen | 19 | from autofuse.pyautofuse import ascir, Autofuser, AutofuserOptions, Schedule, CodeGen |
| 18 | 20 | ||
| 19 | try: | 21 | try: |
| @@ -161,6 +163,53 @@ class TestAscir: | |||
| 161 | == "Check dtype failed for cast_0 Cast; input_dtypes: [DT_INT8], output_dytpes: [DT_INT4]" | 163 | == "Check dtype failed for cast_0 Cast; input_dtypes: [DT_INT8], output_dytpes: [DT_INT4]" |
| 162 | ) | 164 | ) |
| 163 | 165 | ||
| 166 | + | ||
| 167 | + def test_graph_create_node_with_indirect_load_api(): | ||
| 168 | + import pyautofuse | ||
| 169 | + | ||
| 170 | + origin_pyautofuse = sys.modules.get("autofuse.pyautofuse") | ||
| 171 | + sys.modules["autofuse.pyautofuse"] = pyautofuse | ||
| 172 | + ascir_mod = pyautofuse.ascir | ||
| 173 | + ascir_mod.utils.set_platform("3510", 1, 245760) | ||
| 174 | + try: | ||
| 175 | + source_path = os.path.abspath( | ||
| 176 | + os.path.join(PYF_PATH, "../../..", "compiler", "python", "ascir_api.py") | ||
| 177 | + ) | ||
| 178 | + spec = importlib.util.spec_from_file_location( | ||
| 179 | + "source_ascir_api", source_path | ||
| 180 | + ) | ||
| 181 | + source_ascir_api = importlib.util.module_from_spec(spec) | ||
| 182 | + try: | ||
| 183 | + spec.loader.exec_module(source_ascir_api) | ||
| 184 | + finally: | ||
| 185 | + if origin_pyautofuse is None: | ||
| 186 | + sys.modules.pop("autofuse.pyautofuse", None) | ||
| 187 | + else: | ||
| 188 | + sys.modules["autofuse.pyautofuse"] = origin_pyautofuse | ||
| 189 | + | ||
| 190 | + graph = ascir_mod.HintGraph("test_indirect_load_api") | ||
| 191 | + s0 = graph.create_size("s0") | ||
| 192 | + s1 = graph.create_size("s1") | ||
| 193 | + s2 = graph.create_size("s2") | ||
| 194 | + z0 = graph.create_axis("z0", s0) | ||
| 195 | + z1 = graph.create_axis("z1", s1) | ||
| 196 | + z2 = graph.create_axis("z2", s2) | ||
| 197 | + | ||
| 198 | + x = source_ascir_api.Data(graph, dtype=ascir_mod.dtypes.float32) | ||
| 199 | + x.axis = [z0, z1, z2] | ||
| 200 | + index = source_ascir_api.Data(graph, dtype=ascir_mod.dtypes.int32) | ||
| 201 | + index.axis = [z0, z1, z2] | ||
| 202 | + y = source_ascir_api.IndirectLoad( | ||
| 203 | + graph, x, index, axis=1, sched_axis=[z0, z1, z2] | ||
| 204 | + ) | ||
| 205 | + | ||
| 206 | + assert y.dtype == ascir_mod.dtypes.float32 | ||
| 207 | + debug_str = ascir_mod.utils.debug_str(graph) | ||
| 208 | + assert "IndirectLoad" in debug_str | ||
| 209 | + assert "axis" in debug_str | ||
| 210 | + finally: | ||
| 211 | + ascir_mod.utils.set_platform("2201", 1, 245760) | ||
| 212 | + | ||
| 164 | 213 | ||
| 165 | def test_graph_create_const_node_with_value_str_attr(): | 214 | def test_graph_create_const_node_with_value_str_attr(): |
| 166 | graph = ascir.HintGraph("test") | 215 | graph = ascir.HintGraph("test") |
| @@ -134,6 +134,7 @@ add_subdirectory(load_logicaland_store_test) | |||
| 134 | add_subdirectory(load_gather_split_b_t_abs_store_test) | 134 | add_subdirectory(load_gather_split_b_t_abs_store_test) |
| 135 | add_subdirectory(load_gather_tail_split_b_t_abs_store_test) | 135 | add_subdirectory(load_gather_tail_split_b_t_abs_store_test) |
| 136 | add_subdirectory(load_gather_one_axis_split_b_t_abs_store_test) | 136 | add_subdirectory(load_gather_one_axis_split_b_t_abs_store_test) |
| 137 | +add_subdirectory(indirect_load_store_test) | ||
| 137 | # add_subdirectory(split_test) | 138 | # add_subdirectory(split_test) |
| 138 | add_subdirectory(load_where_x2_x3_is_ubscalar_store_test) | 139 | add_subdirectory(load_where_x2_x3_is_ubscalar_store_test) |
| 139 | add_subdirectory(gather_reduce_store_test) | 140 | add_subdirectory(gather_reduce_store_test) |
| @@ -0,0 +1,41 @@ | |||
| 1 | +function(add_indirect_load_e2e_case test_name rank axis use_exp2 tiling_key x0 x1 x2 x3 index0 index1 index2 index3) | ||
| 2 | + set(case_workdir ${CMAKE_CURRENT_BINARY_DIR}/${test_name}) | ||
| 3 | + file(MAKE_DIRECTORY ${case_workdir}) | ||
| 4 | + do_backend_e2e_st_test(${test_name} | ||
| 5 | + WORKDIR ${case_workdir} | ||
| 6 | + CODEGEN indirect_load_store_backend_generator.cpp | ||
| 7 | + TILING_KEY ${tiling_key} | ||
| 8 | + KERNEL_SRC | ||
| 9 | + indirect_load_store_test_kernel.cpp | ||
| 10 | + indirect_load_store_test_tiling.cpp | ||
| 11 | + autofuse_tiling_data.h | ||
| 12 | + TEST_SRC test_e2e_indirect_load_store_kernel.cpp) | ||
| 13 | + | ||
| 14 | + set(case_definitions | ||
| 15 | + IL_RANK=${rank} | ||
| 16 | + IL_AXIS=${axis} | ||
| 17 | + IL_HAS_INPUT_PRE=1 | ||
| 18 | + IL_USE_EXP2=${use_exp2} | ||
| 19 | + IL_TILING_KEY=${tiling_key} | ||
| 20 | + IL_X_S0=${x0} | ||
| 21 | + IL_X_S1=${x1} | ||
| 22 | + IL_X_S2=${x2} | ||
| 23 | + IL_X_S3=${x3} | ||
| 24 | + IL_INDEX_S0=${index0} | ||
| 25 | + IL_INDEX_S1=${index1} | ||
| 26 | + IL_INDEX_S2=${index2} | ||
| 27 | + IL_INDEX_S3=${index3}) | ||
| 28 | + target_compile_definitions(${test_name}_codegen_v2 PRIVATE ${case_definitions}) | ||
| 29 | + target_compile_definitions(${test_name}_e2e_v2 PRIVATE ${case_definitions}) | ||
| 30 | +endfunction() | ||
| 31 | + | ||
| 32 | +add_indirect_load_e2e_case(indirect_load_rank2_axis1_simt 2 1 0 1 5 8 1 1 4 6 1 1) | ||
| 33 | +add_indirect_load_e2e_case(indirect_load_rank2_axis1_simd 2 1 1 0 5 8 1 1 4 6 1 1) | ||
| 34 | +add_indirect_load_e2e_case(indirect_load_rank2_axis0_simt 2 0 0 1 4 9 1 1 6 8 1 1) | ||
| 35 | +add_indirect_load_e2e_case(indirect_load_rank2_axis0_simd 2 0 1 0 4 9 1 1 6 8 1 1) | ||
| 36 | +add_indirect_load_e2e_case(indirect_load_rank2_axisneg2_simt 2 -2 0 1 4 9 1 1 4 8 1 1) | ||
| 37 | +add_indirect_load_e2e_case(indirect_load_rank2_axisneg2_simd 2 -2 1 0 4 9 1 1 4 8 1 1) | ||
| 38 | +add_indirect_load_e2e_case(indirect_load_rank3_axis1_simt 3 1 0 1 3 5 8 1 2 7 6 1) | ||
| 39 | +add_indirect_load_e2e_case(indirect_load_rank3_axis1_simd 3 1 1 0 3 5 8 1 2 7 6 1) | ||
| 40 | +add_indirect_load_e2e_case(indirect_load_rank4_axis2_simt 4 2 0 1 3 3 5 8 2 2 5 6) | ||
| 41 | +add_indirect_load_e2e_case(indirect_load_rank4_axis2_simd 4 2 1 0 3 3 5 8 2 2 5 6) | ||
| @@ -0,0 +1,205 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +namespace { | ||
| 30 | +constexpr size_t kRank = IL_RANK; | ||
| 31 | +constexpr int64_t kAxis = IL_AXIS; | ||
| 32 | +constexpr bool kHasInputPre = IL_HAS_INPUT_PRE; | ||
| 33 | +constexpr bool kUseExp2 = IL_USE_EXP2; | ||
| 34 | + | ||
| 35 | +const af::Axis *FindDerivedAxis(const af::AscGraph &graph, af::Axis::Type type, af::AxisId from) { | ||
| 36 | + for (const auto &axis : graph.GetAllAxis()) { | ||
| 37 | + if (axis != nullptr && axis->type == type && axis->from == std::vector<af::AxisId>{from}) { | ||
| 38 | + return axis.get(); | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | + return nullptr; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +void CollectOriginAxes(af::AscGraph &graph, af::AxisId axis_id, std::vector<af::AxisId> &origins) { | ||
| 45 | + const auto *axis = graph.FindAxis(axis_id); | ||
| 46 | + ASSERT_NE(axis, nullptr); | ||
| 47 | + if (axis->from.empty()) { | ||
| 48 | + origins.emplace_back(axis_id); | ||
| 49 | + return; | ||
| 50 | + } | ||
| 51 | + for (af::AxisId from : axis->from) { | ||
| 52 | + CollectOriginAxes(graph, from, origins); | ||
| 53 | + } | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +void ExpectAxisOrigins(af::AscGraph &graph, af::AxisId axis_id, const std::vector<af::AxisId> &expected) { | ||
| 57 | + std::vector<af::AxisId> origins; | ||
| 58 | + CollectOriginAxes(graph, axis_id, origins); | ||
| 59 | + EXPECT_EQ(origins, expected); | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +void ExpectLoopFramework(af::AscGraph &graph, const af::AscNodePtr &indirect_load) { | ||
| 63 | + ascgen_utils::indirect_load::TemplateAxes axes; | ||
| 64 | + ascgen_utils::indirect_load::TemplateLogicalView logical_view; | ||
| 65 | + ASSERT_EQ(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, axes), af::SUCCESS); | ||
| 66 | + ASSERT_EQ(ascgen_utils::indirect_load::GetTemplateLogicalView(indirect_load, logical_view), af::SUCCESS); | ||
| 67 | + | ||
| 68 | + const auto *outer = graph.FindAxis(axes.outer_axis); | ||
| 69 | + ASSERT_NE(outer, nullptr); | ||
| 70 | + const auto *tile_outer = FindDerivedAxis(graph, af::Axis::Type::kAxisTypeTileOuter, outer->id); | ||
| 71 | + const auto *tile_inner = FindDerivedAxis(graph, af::Axis::Type::kAxisTypeTileInner, outer->id); | ||
| 72 | + ASSERT_NE(tile_outer, nullptr); | ||
| 73 | + ASSERT_NE(tile_inner, nullptr); | ||
| 74 | + EXPECT_EQ(tile_outer->split_pair_other_id, tile_inner->id); | ||
| 75 | + EXPECT_EQ(tile_inner->split_pair_other_id, tile_outer->id); | ||
| 76 | + | ||
| 77 | + const auto *block_outer = FindDerivedAxis(graph, af::Axis::Type::kAxisTypeBlockOuter, tile_outer->id); | ||
| 78 | + const auto *block_inner = FindDerivedAxis(graph, af::Axis::Type::kAxisTypeBlockInner, tile_outer->id); | ||
| 79 | + ASSERT_NE(block_outer, nullptr); | ||
| 80 | + ASSERT_NE(block_inner, nullptr); | ||
| 81 | + EXPECT_EQ(block_outer->split_pair_other_id, block_inner->id); | ||
| 82 | + EXPECT_EQ(block_inner->split_pair_other_id, block_outer->id); | ||
| 83 | + | ||
| 84 | + const int64_t normalized_axis = kAxis < 0L ? kAxis + static_cast<int64_t>(kRank) : kAxis; | ||
| 85 | + const auto template_id = ascir::GetTemplateIdOrDefault(*indirect_load); | ||
| 86 | + if (template_id == ascir::TemplateId::kIndirectLoadSimt) { | ||
| 87 | + ExpectAxisOrigins(graph, axes.outer_axis, logical_view.output.axis_ids); | ||
| 88 | + EXPECT_EQ(axes.inner_axis, af::kIdNone); | ||
| 89 | + return; | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + ASSERT_EQ(template_id, ascir::TemplateId::kIndirectLoadSimd); | ||
| 93 | + const size_t split = static_cast<size_t>(normalized_axis); | ||
| 94 | + if (split == 0UL) { | ||
| 95 | + EXPECT_TRUE(outer->from.empty()); | ||
| 96 | + EXPECT_EQ(af::SymbolicUtils::StaticCheckEq(outer->size, af::ops::One), af::TriBool::kTrue); | ||
| 97 | + } else { | ||
| 98 | + ExpectAxisOrigins( | ||
| 99 | + graph, axes.outer_axis, | ||
| 100 | + std::vector<af::AxisId>(logical_view.output.axis_ids.begin(), logical_view.output.axis_ids.begin() + split)); | ||
| 101 | + } | ||
| 102 | + ExpectAxisOrigins( | ||
| 103 | + graph, axes.inner_axis, | ||
| 104 | + std::vector<af::AxisId>(logical_view.output.axis_ids.begin() + split, logical_view.output.axis_ids.end())); | ||
| 105 | + if (kHasInputPre) { | ||
| 106 | + ExpectAxisOrigins( | ||
| 107 | + graph, axes.input_inner_axis, | ||
| 108 | + std::vector<af::AxisId>(logical_view.data.axis_ids.begin() + split, logical_view.data.axis_ids.end())); | ||
| 109 | + } | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +void CheckScheduledLoopFramework(ascir::FusedScheduledResult &result) { | ||
| 113 | + size_t simd_count = 0UL; | ||
| 114 | + size_t simt_count = 0UL; | ||
| 115 | + for (auto &candidates : result.node_idx_to_scheduled_results) { | ||
| 116 | + for (auto &candidate : candidates) { | ||
| 117 | + for (auto &group : candidate.schedule_groups) { | ||
| 118 | + for (auto &graph : group.impl_graphs) { | ||
| 119 | + const af::AscNodePtr indirect_load = ascgen_utils::indirect_load::FindIndirectLoadNode(graph); | ||
| 120 | + if (indirect_load == nullptr) { | ||
| 121 | + continue; | ||
| 122 | + } | ||
| 123 | + ExpectLoopFramework(graph, indirect_load); | ||
| 124 | + if (ascir::GetTemplateIdOrDefault(*indirect_load) == ascir::TemplateId::kIndirectLoadSimd) { | ||
| 125 | + ++simd_count; | ||
| 126 | + } else { | ||
| 127 | + ++simt_count; | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + EXPECT_GT(simd_count, 0UL); | ||
| 134 | + EXPECT_GT(simt_count, 0UL); | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +void CheckGeneratedKernel(const std::string &kernel) { | ||
| 138 | + EXPECT_NE(kernel.find("// IndirectLoad SIMD"), std::string::npos); | ||
| 139 | + EXPECT_NE(kernel.find("// IndirectLoad SIMT"), std::string::npos); | ||
| 140 | +} | ||
| 141 | +} // namespace | ||
| 142 | + | ||
| 143 | +class TestBackendIndirectLoadStoreE2e : public testing::Test { | ||
| 144 | + protected: | ||
| 145 | + void SetUp() override { | ||
| 146 | + dlog_setlevel(ASCGEN_MODULE_NAME, DLOG_ERROR, 0); | ||
| 147 | + ge::PlatformContext::GetInstance().Reset(); | ||
| 148 | + ge::RuntimeStub::SetInstance(std::make_shared<af::RuntimeStubV2>()); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + void TearDown() override { | ||
| 152 | + dlog_setlevel(ASCGEN_MODULE_NAME, DLOG_ERROR, 0); | ||
| 153 | + ge::RuntimeStub::Reset(); | ||
| 154 | + } | ||
| 155 | +}; | ||
| 156 | + | ||
| 157 | +TEST_F(TestBackendIndirectLoadStoreE2e, IndirectLoadStoreCodegen) { | ||
| 158 | + const std::string tiling_stub = R"( | ||
| 159 | + | ||
| 160 | + | ||
| 161 | +)"; | ||
| 162 | + auto graph = ascir::ShareGraph::IndirectLoadStoreFusedGraph(kRank, kAxis, af::DT_FLOAT16, kHasInputPre, kUseExp2); | ||
| 163 | + ASSERT_NE(graph, nullptr); | ||
| 164 | + std::map<std::string, std::string> shape_info; | ||
| 165 | + for (size_t i = 0UL; i < 2UL * kRank; ++i) { | ||
| 166 | + shape_info.emplace("s" + std::to_string(i), "stub_s" + std::to_string(i)); | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + const std::vector<std::string> parts = splitString(KERNEL_SRC_LIST, ':'); | ||
| 170 | + ASSERT_EQ(parts.size(), 3U); | ||
| 171 | + try { | ||
| 172 | + optimize::Optimizer optimizer(optimize::OptimizerOptions{.graph_type = optimize::GraphType::kFusedAscBackend}); | ||
| 173 | + codegen::Codegen codegen(codegen::CodegenOptions{}); | ||
| 174 | + ascir::FusedScheduledResult fused_schedule_result; | ||
| 175 | + codegen::CodegenResult result; | ||
| 176 | + testing::internal::CaptureStdout(); | ||
| 177 | + const auto optimize_status = optimizer.Optimize(graph, fused_schedule_result); | ||
| 178 | + if (optimize_status == 0) { | ||
| 179 | + CheckScheduledLoopFramework(fused_schedule_result); | ||
| 180 | + } | ||
| 181 | + const auto codegen_status = optimize_status == 0 ? codegen.Generate(shape_info, fused_schedule_result, result) : -1; | ||
| 182 | + const std::string logs = testing::internal::GetCapturedStdout(); | ||
| 183 | + EXPECT_EQ(logs.find("[ERROR]"), std::string::npos) << logs; | ||
| 184 | + ASSERT_EQ(optimize_status, 0) << logs; | ||
| 185 | + ASSERT_EQ(codegen_status, 0) << logs; | ||
| 186 | + CheckGeneratedKernel(result.kernel); | ||
| 187 | + | ||
| 188 | + std::fstream kernel_file(parts[0], std::ios::out); | ||
| 189 | + std::fstream tiling_file(parts[1], std::ios::out); | ||
| 190 | + std::fstream tiling_data_file(parts[2], std::ios::out); | ||
| 191 | + ASSERT_TRUE(kernel_file.is_open()); | ||
| 192 | + ASSERT_TRUE(tiling_file.is_open()); | ||
| 193 | + ASSERT_TRUE(tiling_data_file.is_open()); | ||
| 194 | + kernel_file << tiling_stub << RemoveSubDirInclude(result.kernel); | ||
| 195 | + tiling_file << result.tiling; | ||
| 196 | + tiling_data_file << result.tiling_data; | ||
| 197 | + EXPECT_TRUE(kernel_file.good()); | ||
| 198 | + EXPECT_TRUE(tiling_file.good()); | ||
| 199 | + EXPECT_TRUE(tiling_data_file.good()); | ||
| 200 | + } catch (const std::exception &e) { | ||
| 201 | + FAIL() << e.what(); | ||
| 202 | + } catch (...) { | ||
| 203 | + FAIL() << "Unknown exception"; | ||
| 204 | + } | ||
| 205 | +} | ||
| @@ -0,0 +1,132 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +extern "C" __global__ __aicore__ void indirect_load_store_test(GM_ADDR x, GM_ADDR index, GM_ADDR y, GM_ADDR workspace, | ||
| 24 | + GM_ADDR tiling); | ||
| 25 | + | ||
| 26 | +extern "C" int64_t AutofuseTiling(uint32_t s0, uint32_t s1, uint32_t s2, uint32_t s3, AutofuseTilingData *, uint32_t *, | ||
| 27 | + uint32_t *, uint32_t, uint32_t); | ||
| 28 | +constexpr std::array<int32_t, 2> kInputShape = {IL_X_S0, IL_X_S1}; | ||
| 29 | +constexpr std::array<int32_t, 2> kIndexShape = {IL_INDEX_S0, IL_INDEX_S1}; | ||
| 30 | + | ||
| 31 | +extern "C" int64_t AutofuseTiling(uint32_t s0, uint32_t s1, uint32_t s2, uint32_t s3, uint32_t s4, uint32_t s5, | ||
| 32 | + AutofuseTilingData *, uint32_t *, uint32_t *, uint32_t, uint32_t); | ||
| 33 | +constexpr std::array<int32_t, 3> kInputShape = {IL_X_S0, IL_X_S1, IL_X_S2}; | ||
| 34 | +constexpr std::array<int32_t, 3> kIndexShape = {IL_INDEX_S0, IL_INDEX_S1, IL_INDEX_S2}; | ||
| 35 | + | ||
| 36 | +extern "C" int64_t AutofuseTiling(uint32_t s0, uint32_t s1, uint32_t s2, uint32_t s3, uint32_t s4, uint32_t s5, | ||
| 37 | + uint32_t s6, uint32_t s7, AutofuseTilingData *, uint32_t *, uint32_t *, uint32_t, | ||
| 38 | + uint32_t); | ||
| 39 | +constexpr std::array<int32_t, 4> kInputShape = {IL_X_S0, IL_X_S1, IL_X_S2, IL_X_S3}; | ||
| 40 | +constexpr std::array<int32_t, 4> kIndexShape = {IL_INDEX_S0, IL_INDEX_S1, IL_INDEX_S2, IL_INDEX_S3}; | ||
| 41 | + | ||
| 42 | + | ||
| 43 | +namespace { | ||
| 44 | +constexpr int32_t kAxis = IL_AXIS < 0 ? IL_AXIS + IL_RANK : IL_AXIS; | ||
| 45 | + | ||
| 46 | +void RunTiling(AutofuseTilingData &tiling, uint32_t &workspace_size, uint32_t &block_dim) { | ||
| 47 | + | ||
| 48 | + AutofuseTiling(kInputShape[0], kInputShape[1], kIndexShape[0], kIndexShape[1], &tiling, &workspace_size, &block_dim, | ||
| 49 | + 48, 192 * 1024); | ||
| 50 | + | ||
| 51 | + AutofuseTiling(kInputShape[0], kInputShape[1], kInputShape[2], kIndexShape[0], kIndexShape[1], kIndexShape[2], | ||
| 52 | + &tiling, &workspace_size, &block_dim, 48, 192 * 1024); | ||
| 53 | + | ||
| 54 | + AutofuseTiling(kInputShape[0], kInputShape[1], kInputShape[2], kInputShape[3], kIndexShape[0], kIndexShape[1], | ||
| 55 | + kIndexShape[2], kIndexShape[3], &tiling, &workspace_size, &block_dim, 48, 192 * 1024); | ||
| 56 | + | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +template <size_t N> | ||
| 60 | +int32_t ElementCount(const std::array<int32_t, N> &shape) { | ||
| 61 | + int32_t count = 1; | ||
| 62 | + for (int32_t dim : shape) { | ||
| 63 | + count *= dim; | ||
| 64 | + } | ||
| 65 | + return count; | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +void InitializeData(half *x, int32_t *index, half *expected, int32_t input_count, int32_t output_count) { | ||
| 69 | + std::array<int32_t, IL_RANK> input_strides{}; | ||
| 70 | + input_strides.back() = 1; | ||
| 71 | + for (size_t i = input_strides.size() - 1UL; i > 0UL; --i) { | ||
| 72 | + input_strides[i - 1UL] = kInputShape[i] * input_strides[i]; | ||
| 73 | + } | ||
| 74 | + for (int32_t i = 0; i < input_count; ++i) { | ||
| 75 | + x[i] = static_cast<half>(static_cast<float>((i % 29) - 14) * 0.25F); | ||
| 76 | + } | ||
| 77 | + for (int32_t i = 0; i < output_count; ++i) { | ||
| 78 | + const int32_t index_value = (i * 3 + 1) % kInputShape[kAxis]; | ||
| 79 | + index[i] = i % 2 == 0 ? index_value : -index_value; | ||
| 80 | + int32_t remaining = i; | ||
| 81 | + int32_t input_offset = 0; | ||
| 82 | + for (size_t dim = kIndexShape.size(); dim-- > 0UL;) { | ||
| 83 | + const int32_t coord = remaining % kIndexShape[dim]; | ||
| 84 | + remaining /= kIndexShape[dim]; | ||
| 85 | + input_offset += (static_cast<int32_t>(dim) == kAxis ? index_value : coord) * input_strides[dim]; | ||
| 86 | + } | ||
| 87 | + float value = static_cast<float>(x[input_offset]); | ||
| 88 | + | ||
| 89 | + value = std::max(value, 0.0F); | ||
| 90 | + | ||
| 91 | + | ||
| 92 | + expected[i] = static_cast<half>(-std::exp2(value)); | ||
| 93 | + | ||
| 94 | + expected[i] = static_cast<half>(-std::exp(value)); | ||
| 95 | + | ||
| 96 | + } | ||
| 97 | +} | ||
| 98 | +} // namespace | ||
| 99 | + | ||
| 100 | +TEST(E2EIndirectLoadStore, GeneratedKernelMatchesReference) { | ||
| 101 | + const int32_t input_count = ElementCount(kInputShape); | ||
| 102 | + const int32_t output_count = ElementCount(kIndexShape); | ||
| 103 | + const auto gm_free = [](void *ptr) { AscendC::GmFree(ptr); }; | ||
| 104 | + std::unique_ptr<half, decltype(gm_free)> x(reinterpret_cast<half *>(AscendC::GmAlloc(input_count * sizeof(half))), | ||
| 105 | + gm_free); | ||
| 106 | + std::unique_ptr<int32_t, decltype(gm_free)> index( | ||
| 107 | + reinterpret_cast<int32_t *>(AscendC::GmAlloc(output_count * sizeof(int32_t))), gm_free); | ||
| 108 | + std::unique_ptr<half, decltype(gm_free)> output( | ||
| 109 | + reinterpret_cast<half *>(AscendC::GmAlloc(output_count * sizeof(half))), gm_free); | ||
| 110 | + ASSERT_NE(x, nullptr); | ||
| 111 | + ASSERT_NE(index, nullptr); | ||
| 112 | + ASSERT_NE(output, nullptr); | ||
| 113 | + std::vector<half> expected(static_cast<size_t>(output_count)); | ||
| 114 | + InitializeData(x.get(), index.get(), expected.data(), input_count, output_count); | ||
| 115 | + std::fill_n(output.get(), output_count, static_cast<half>(0.0F)); | ||
| 116 | + | ||
| 117 | + AutofuseTilingData tiling_data{}; | ||
| 118 | + uint32_t workspace_size = 0U; | ||
| 119 | + uint32_t block_dim = 48U; | ||
| 120 | + RunTiling(tiling_data, workspace_size, block_dim); | ||
| 121 | + ASSERT_EQ(tiling_data.graph0_tiling_key, IL_TILING_KEY); | ||
| 122 | + ASSERT_GT(tiling_data.block_dim, 0U); | ||
| 123 | + | ||
| 124 | + AscendC::SetKernelMode(KernelMode::AIV_MODE); | ||
| 125 | + ICPU_RUN_KF(indirect_load_store_test, tiling_data.block_dim, reinterpret_cast<uint8_t *>(x.get()), | ||
| 126 | + reinterpret_cast<uint8_t *>(index.get()), reinterpret_cast<uint8_t *>(output.get()), nullptr, | ||
| 127 | + reinterpret_cast<uint8_t *>(&tiling_data)); | ||
| 128 | + for (int32_t i = 0; i < output_count; ++i) { | ||
| 129 | + EXPECT_NEAR(static_cast<float>(output.get()[i]), static_cast<float>(expected[static_cast<size_t>(i)]), 0.0625F) | ||
| 130 | + << "offset=" << i; | ||
| 131 | + } | ||
| 132 | +} | ||
| @@ -929,6 +929,21 @@ REG_ASC_IR(Gather).Impl(v2_soc_versions, | |||
| 929 | {{"T1", TensorType{DT_INT16, DT_UINT16, DT_INT32, DT_UINT32, DT_FLOAT16, DT_BF16, DT_FLOAT}}, | 929 | {{"T1", TensorType{DT_INT16, DT_UINT16, DT_INT32, DT_UINT32, DT_FLOAT16, DT_BF16, DT_FLOAT}}, |
| 930 | {"T2", TensorType{DT_INT32, DT_INT64}}}}); | 930 | {"T2", TensorType{DT_INT32, DT_INT64}}}}); |
| 931 | 931 | ||
| 932 | +REG_ASC_IR(IndirectLoad) | ||
| 933 | + .Input("x1", "T1") | ||
| 934 | + .Input("x2", "T2") | ||
| 935 | + .Output("y", "T1") | ||
| 936 | + .Attr<int64_t>("axis") | ||
| 937 | + .Attr<bool>("negative_index_support") | ||
| 938 | + .Attr<bool>("need_check_bound") | ||
| 939 | + .Attr<int64_t>("max") | ||
| 940 | + .ComputeType(ComputeType::kComputeLoad) | ||
| 941 | + .Impl(v2_soc_versions, | ||
| 942 | + {af::ascir::AscIrImplCreator<af::ascir::IndirectLoadAscIrAttImplV2>(), | ||
| 943 | + af::ascir::AscIrImplCreator<af::ascir::IndirectLoadAscIrCodegenImplV2>(), | ||
| 944 | + {{"T1", TensorType{DT_INT16, DT_UINT16, DT_INT32, DT_UINT32, DT_FLOAT16, DT_BF16, DT_FLOAT}}, | ||
| 945 | + {"T2", TensorType{DT_INT32, DT_INT64}}}}); | ||
Z | |||
| 946 | + | ||
| 932 | REG_ASC_IR(Transpose).Impl(v2_soc_versions, {af::ascir::AscIrImplCreator<af::ascir::TransposeAscIrAttImplV2>(), | 947 | REG_ASC_IR(Transpose).Impl(v2_soc_versions, {af::ascir::AscIrImplCreator<af::ascir::TransposeAscIrAttImplV2>(), |
| 933 | af::ascir::AscIrImplCreator<af::ascir::TransposeAscIrCodegenImplV2>(), | 948 | af::ascir::AscIrImplCreator<af::ascir::TransposeAscIrCodegenImplV2>(), |
| 934 | {{"T", TensorType{DT_INT16, DT_UINT16, DT_INT32, DT_UINT32, DT_FLOAT16, | 949 | {{"T", TensorType{DT_INT16, DT_UINT16, DT_INT32, DT_UINT32, DT_FLOAT16, |
| @@ -35,6 +35,7 @@ namespace ascir { | |||
| 35 | } | 35 | } |
| 36 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Add); | 36 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Add); |
| 37 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Gather); | 37 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Gather); |
| 38 | +REG_ASC_IR_ATT_V2_CLASS_DEFINE(IndirectLoad); | ||
| 38 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Abs); | 39 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Abs); |
| 39 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Broadcast); | 40 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Broadcast); |
| 40 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Cast); | 41 | REG_ASC_IR_ATT_V2_CLASS_DEFINE(Cast); |
| @@ -18,6 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | + | ||
| 21 | 22 | ||
| 22 | 23 | ||
| 23 | namespace af { | 24 | namespace af { |
| @@ -281,6 +282,20 @@ class CastAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 281 | } | 282 | } |
| 282 | return false; | 283 | return false; |
| 283 | } | 284 | } |
| 285 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 286 | + return IsVectorFunctionSupported(node); | ||
| 287 | + } | ||
| 288 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr(const AscNode &node, const std::vector<std::string> &inputs, | ||
| 289 | + std::string &expr) const override { | ||
| 290 | + auto outputs = const_cast<AscNode &>(node).outputs(); | ||
| 291 | + GE_ASSERT_TRUE(outputs.size() == 1UL, "Cast node %s[%s] must have one output", node.GetTypePtr(), | ||
| 292 | + node.GetNamePtr()); | ||
| 293 | + const char *output_dtype = GetSimtDtypeName(outputs[0]->attr.dtype); | ||
| 294 | + GE_ASSERT_NOTNULL(output_dtype, "Cast node %s[%s] has unsupported SIMT output dtype %d", node.GetTypePtr(), | ||
| 295 | + node.GetNamePtr(), static_cast<int32_t>(outputs[0]->attr.dtype)); | ||
| 296 | + expr = std::string("static_cast<") + output_dtype + ">(" + inputs[0] + ")"; | ||
| 297 | + return ge::GRAPH_SUCCESS; | ||
| 298 | + } | ||
| 284 | [[nodiscard]] std::vector<std::string> IncludeApiHeaderFiles() const override { | 299 | [[nodiscard]] std::vector<std::string> IncludeApiHeaderFiles() const override { |
| 285 | return { | 300 | return { |
| 286 | "basic_api/reg_compute/kernel_reg_compute_intf.h", | 301 | "basic_api/reg_compute/kernel_reg_compute_intf.h", |
| @@ -291,6 +306,20 @@ class CastAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 291 | node.GetTypePtr(), node.GetNamePtr()); | 306 | node.GetTypePtr(), node.GetNamePtr()); |
| 292 | return true; | 307 | return true; |
| 293 | } | 308 | } |
| 309 | + | ||
| 310 | + private: | ||
| 311 | + static const char *GetSimtDtypeName(ge::DataType dtype) { | ||
| 312 | + static constexpr std::pair<ge::DataType, const char *> kDtypeNames[] = { | ||
| 313 | + {DT_FLOAT, "float"}, {DT_FLOAT16, "half"}, {DT_BF16, "bfloat16_t"}, {DT_INT8, "int8_t"}, | ||
| 314 | + {DT_UINT8, "uint8_t"}, {DT_INT16, "int16_t"}, {DT_INT32, "int32_t"}, {DT_INT64, "int64_t"}, | ||
| 315 | + }; | ||
| 316 | + for (const auto &entry : kDtypeNames) { | ||
| 317 | + if (entry.first == dtype) { | ||
| 318 | + return entry.second; | ||
| 319 | + } | ||
| 320 | + } | ||
| 321 | + return nullptr; | ||
| 322 | + } | ||
| 294 | }; | 323 | }; |
| 295 | 324 | ||
| 296 | class AbsAscIrCodegenImplV2 : public AscIrCodegenV2 { | 325 | class AbsAscIrCodegenImplV2 : public AscIrCodegenV2 { |
| @@ -315,6 +344,18 @@ class AbsAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 315 | return true; | 344 | return true; |
| 316 | } | 345 | } |
| 317 | 346 | ||
| 347 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 348 | + (void)node; | ||
| 349 | + return true; | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr([[maybe_unused]] const AscNode &node, | ||
| 353 | + const std::vector<std::string> &inputs, | ||
| 354 | + std::string &expr) const override { | ||
| 355 | + expr = "AscendC::Simt::Abs(" + inputs[0] + ")"; | ||
| 356 | + return ge::GRAPH_SUCCESS; | ||
| 357 | + } | ||
| 358 | + | ||
| 318 | [[nodiscard]] bool IsInplaceSupported(const AscNode &abs_node) const override { | 359 | [[nodiscard]] bool IsInplaceSupported(const AscNode &abs_node) const override { |
| 319 | (void)abs_node; | 360 | (void)abs_node; |
| 320 | return true; | 361 | return true; |
| @@ -384,6 +425,23 @@ class ExpAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 384 | node.GetTypePtr(), node.GetNamePtr()); | 425 | node.GetTypePtr(), node.GetNamePtr()); |
| 385 | return true; | 426 | return true; |
| 386 | } | 427 | } |
| 428 | + | ||
| 429 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 430 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 431 | + auto dt = static_cast<ge::DataType>(outputs[0].attr.dtype); | ||
| 432 | + return dt == DT_FLOAT16 || dt == DT_FLOAT || dt == DT_BF16; | ||
| 433 | + } | ||
| 434 | + | ||
| 435 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr(const AscNode &node, const std::vector<std::string> &inputs, | ||
| 436 | + std::string &expr) const override { | ||
| 437 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 438 | + if (outputs[0].attr.dtype == DT_FLOAT) { | ||
| 439 | + expr = "AscendC::Simt::Exp(" + inputs[0] + ")"; | ||
| 440 | + } else { | ||
| 441 | + expr = "AscendC::Simt::Exp(static_cast<float>(" + inputs[0] + "))"; | ||
| 442 | + } | ||
| 443 | + return ge::GRAPH_SUCCESS; | ||
| 444 | + } | ||
| 387 | }; | 445 | }; |
| 388 | 446 | ||
| 389 | class Exp2AscIrCodegenImplV2 : public AscIrCodegenV2 { | 447 | class Exp2AscIrCodegenImplV2 : public AscIrCodegenV2 { |
| @@ -412,6 +470,23 @@ class Exp2AscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 412 | node.GetTypePtr(), node.GetNamePtr()); | 470 | node.GetTypePtr(), node.GetNamePtr()); |
| 413 | return true; | 471 | return true; |
| 414 | } | 472 | } |
| 473 | + | ||
| 474 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 475 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 476 | + auto dt = static_cast<ge::DataType>(outputs[0].attr.dtype); | ||
| 477 | + return dt == DT_FLOAT16 || dt == DT_FLOAT || dt == DT_BF16; | ||
| 478 | + } | ||
| 479 | + | ||
| 480 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr(const AscNode &node, const std::vector<std::string> &inputs, | ||
| 481 | + std::string &expr) const override { | ||
| 482 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 483 | + if (outputs[0].attr.dtype == DT_FLOAT) { | ||
| 484 | + expr = "AscendC::Simt::Exp2(" + inputs[0] + ")"; | ||
| 485 | + } else { | ||
| 486 | + expr = "AscendC::Simt::Exp2(static_cast<float>(" + inputs[0] + "))"; | ||
| 487 | + } | ||
| 488 | + return ge::GRAPH_SUCCESS; | ||
| 489 | + } | ||
| 415 | }; | 490 | }; |
| 416 | 491 | ||
| 417 | class FloorAscIrCodegenImplV2 : public AscIrCodegenV2 { | 492 | class FloorAscIrCodegenImplV2 : public AscIrCodegenV2 { |
| @@ -1223,6 +1298,23 @@ class LnAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 1223 | node.GetTypePtr(), node.GetNamePtr()); | 1298 | node.GetTypePtr(), node.GetNamePtr()); |
| 1224 | return true; | 1299 | return true; |
| 1225 | } | 1300 | } |
| 1301 | + | ||
| 1302 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 1303 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 1304 | + auto dt = static_cast<ge::DataType>(outputs[0].attr.dtype); | ||
| 1305 | + return dt == DT_FLOAT16 || dt == DT_FLOAT || dt == DT_BF16; | ||
| 1306 | + } | ||
| 1307 | + | ||
| 1308 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr(const AscNode &node, const std::vector<std::string> &inputs, | ||
| 1309 | + std::string &expr) const override { | ||
| 1310 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 1311 | + if (outputs[0].attr.dtype == DT_FLOAT) { | ||
| 1312 | + expr = "AscendC::Simt::Log(" + inputs[0] + ")"; | ||
| 1313 | + } else { | ||
| 1314 | + expr = "AscendC::Simt::Log(static_cast<float>(" + inputs[0] + "))"; | ||
| 1315 | + } | ||
| 1316 | + return ge::GRAPH_SUCCESS; | ||
| 1317 | + } | ||
| 1226 | }; | 1318 | }; |
| 1227 | 1319 | ||
| 1228 | class ExpmAscIrCodegenImplV2 : public AscIrCodegenV2 { | 1320 | class ExpmAscIrCodegenImplV2 : public AscIrCodegenV2 { |
| @@ -1390,6 +1482,23 @@ class SqrtAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 1390 | node.GetTypePtr(), node.GetNamePtr()); | 1482 | node.GetTypePtr(), node.GetNamePtr()); |
| 1391 | return true; | 1483 | return true; |
| 1392 | } | 1484 | } |
| 1485 | + | ||
| 1486 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 1487 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 1488 | + auto dt = static_cast<ge::DataType>(outputs[0].attr.dtype); | ||
| 1489 | + return dt == DT_FLOAT16 || dt == DT_FLOAT || dt == DT_BF16; | ||
| 1490 | + } | ||
| 1491 | + | ||
| 1492 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr(const AscNode &node, const std::vector<std::string> &inputs, | ||
| 1493 | + std::string &expr) const override { | ||
| 1494 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 1495 | + if (outputs[0].attr.dtype == DT_FLOAT) { | ||
| 1496 | + expr = "AscendC::Simt::Sqrt(" + inputs[0] + ")"; | ||
| 1497 | + } else { | ||
| 1498 | + expr = "AscendC::Simt::Sqrt(static_cast<float>(" + inputs[0] + "))"; | ||
| 1499 | + } | ||
| 1500 | + return ge::GRAPH_SUCCESS; | ||
| 1501 | + } | ||
| 1393 | }; | 1502 | }; |
| 1394 | 1503 | ||
| 1395 | class RsqrtAscIrCodegenImplV2 : public AscIrCodegenV2 { | 1504 | class RsqrtAscIrCodegenImplV2 : public AscIrCodegenV2 { |
| @@ -1452,6 +1561,22 @@ class NegAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 1452 | return true; | 1561 | return true; |
| 1453 | } | 1562 | } |
| 1454 | 1563 | ||
| 1564 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 1565 | + (void)node; | ||
| 1566 | + return true; | ||
| 1567 | + } | ||
| 1568 | + | ||
| 1569 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr(const AscNode &node, const std::vector<std::string> &inputs, | ||
| 1570 | + std::string &expr) const override { | ||
| 1571 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 1572 | + if (outputs[0].attr.dtype == DT_FLOAT16 || outputs[0].attr.dtype == DT_BF16) { | ||
| 1573 | + expr = "-static_cast<float>(" + inputs[0] + ")"; | ||
| 1574 | + } else { | ||
| 1575 | + expr = "-(" + inputs[0] + ")"; | ||
| 1576 | + } | ||
| 1577 | + return ge::GRAPH_SUCCESS; | ||
| 1578 | + } | ||
| 1579 | + | ||
| 1455 | [[nodiscard]] bool IsInplaceSupported(const AscNode &neg_node) const override { | 1580 | [[nodiscard]] bool IsInplaceSupported(const AscNode &neg_node) const override { |
| 1456 | (void)neg_node; | 1581 | (void)neg_node; |
| 1457 | return true; | 1582 | return true; |
| @@ -1500,6 +1625,18 @@ class ReluAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 1500 | return true; | 1625 | return true; |
| 1501 | } | 1626 | } |
| 1502 | 1627 | ||
| 1628 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 1629 | + (void)node; | ||
| 1630 | + return true; | ||
| 1631 | + } | ||
| 1632 | + | ||
| 1633 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr([[maybe_unused]] const AscNode &node, | ||
| 1634 | + const std::vector<std::string> &inputs, | ||
| 1635 | + std::string &expr) const override { | ||
| 1636 | + expr = "AscendC::Simt::Max(" + inputs[0] + ", static_cast<decltype(" + inputs[0] + ")>(0))"; | ||
| 1637 | + return ge::GRAPH_SUCCESS; | ||
| 1638 | + } | ||
| 1639 | + | ||
| 1503 | [[nodiscard]] bool IsInplaceSupported(const AscNode &relu_node) const override { | 1640 | [[nodiscard]] bool IsInplaceSupported(const AscNode &relu_node) const override { |
| 1504 | (void)relu_node; | 1641 | (void)relu_node; |
| 1505 | return true; | 1642 | return true; |
| @@ -2571,7 +2708,25 @@ class LeakyReluAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 2571 | node.GetTypePtr(), node.GetNamePtr()); | 2708 | node.GetTypePtr(), node.GetNamePtr()); |
| 2572 | return true; | 2709 | return true; |
| 2573 | } | 2710 | } |
| 2711 | + | ||
| 2712 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 2713 | + (void)node; | ||
| 2714 | + return true; | ||
| 2715 | + } | ||
| 2716 | + | ||
| 2717 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr(const AscNode &node, const std::vector<std::string> &inputs, | ||
| 2718 | + std::string &expr) const override { | ||
| 2719 | + float negative_slope = 0.0f; | ||
| 2720 | + GE_ASSERT_NOTNULL(node.attr.ir_attr, "LeakyRelu node %s has no ir attr", node.GetNamePtr()); | ||
| 2721 | + GE_ASSERT_GRAPH_SUCCESS( | ||
| 2722 | + const_cast<AscIrAttrDefBase *>(node.attr.ir_attr.get())->GetAttrValue("negative_slope", negative_slope)); | ||
| 2723 | + const std::string &input = inputs[0]; | ||
| 2724 | + expr = "(" + input + " > static_cast<decltype(" + input + ")>(0)) ? " + input + " : static_cast<decltype(" + input + | ||
| 2725 | + ")>(" + std::to_string(negative_slope) + ") * " + input; | ||
| 2726 | + return ge::GRAPH_SUCCESS; | ||
| 2727 | + } | ||
| 2574 | }; | 2728 | }; |
| 2729 | + | ||
| 2575 | /*********************************************************************************/ | 2730 | /*********************************************************************************/ |
| 2576 | class ClipByValueAscIrCodegenImplV2 : public AscIrCodegenV2 { | 2731 | class ClipByValueAscIrCodegenImplV2 : public AscIrCodegenV2 { |
| 2577 | public: | 2732 | public: |
| @@ -2711,6 +2866,61 @@ class GatherAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 2711 | } | 2866 | } |
| 2712 | }; | 2867 | }; |
| 2713 | /*********************************************************************************/ | 2868 | /*********************************************************************************/ |
| 2869 | +class IndirectLoadAscIrCodegenImplV2 : public AscIrCodegenV2 { | ||
| 2870 | + public: | ||
| 2871 | + [[nodiscard]] std::vector<std::unique_ptr<TmpBufDesc>> CalcTmpBufSize(const AscNode &node) override { | ||
| 2872 | + if (::ascir::GetTemplateIdOrDefault(node) != ::ascir::TemplateId::kIndirectLoadSimd) { | ||
| 2873 | + return {}; | ||
| 2874 | + } | ||
| 2875 | + auto node_inputs = node.inputs; | ||
| 2876 | + auto node_outputs = node.outputs; | ||
| 2877 | + const auto &x = node_inputs[0].attr; | ||
| 2878 | + const auto &index = node_inputs[1].attr; | ||
| 2879 | + const auto &y = node_outputs[0].attr; | ||
| 2880 | + Expression x_size = Symbol(GetSizeByDataType(x.dtype)); | ||
| 2881 | + for (const auto &repeat : x.repeats) { | ||
| 2882 | + x_size = x_size * repeat; | ||
| 2883 | + } | ||
| 2884 | + Expression index_size = Symbol(GetSizeByDataType(index.dtype)); | ||
| 2885 | + for (const auto &repeat : y.repeats) { | ||
| 2886 | + index_size = index_size * repeat; | ||
| 2887 | + } | ||
| 2888 | + Expression offset_size = Symbol(sizeof(uint32_t)); | ||
| 2889 | + for (const auto &repeat : y.repeats) { | ||
| 2890 | + offset_size = offset_size * repeat; | ||
| 2891 | + } | ||
| 2892 | + const size_t index_dtype_size = static_cast<size_t>(GetSizeByDataType(index.dtype)); | ||
| 2893 | + const Expression aligned_x_size = af::sym::Align(x_size, 32); | ||
| 2894 | + const Expression offset_or_index_size = index_dtype_size > sizeof(uint32_t) ? index_size : offset_size; | ||
| 2895 | + TmpBufDesc desc = {Symbol(2) * aligned_x_size + af::sym::Align(offset_or_index_size, 32), -1}; | ||
Z 当前分配: 2 * aligned_x_size + aligned_offset_size 但实际 SIMD buffer 布局是: 原始输入窗口 + 预处理后输入窗口 + offset buffer 例如: GM float32 -> Cast float16 -> IndirectLoad 元素数 N 临时空间计算看到的是 Cast 后的 IndirectLoad 输入: 当前分配:2 * 2N = 4N bytes 实际需要:4N + 2N = 6N bytes 不含 offset buffer 就已经少了 2N,后续 offset_buf 会落到已分配空间之外,可能覆盖相邻 UB。 当前测试只覆盖了同 dtype 的 Relu,以及 float16→float32 的放大 Cast;没有覆盖 float32→float16、int32→int16 等降精度场景。 建议分别计算: aligned(source_dtype_size * input_elements)
![]() ![]() | |||
| 2896 | + std::vector<std::unique_ptr<TmpBufDesc>> tmp_buf_descs; | ||
| 2897 | + tmp_buf_descs.emplace_back(std::make_unique<TmpBufDesc>(desc)); | ||
| 2898 | + return tmp_buf_descs; | ||
| 2899 | + } | ||
| 2900 | + | ||
| 2901 | + [[nodiscard]] std::string GetApiCallName() const override { | ||
| 2902 | + return "IndirectLoadRegApiCall"; | ||
| 2903 | + } | ||
| 2904 | + [[nodiscard]] std::string GetApiName() const override { | ||
| 2905 | + return "IndirectLoad"; | ||
| 2906 | + } | ||
| 2907 | + [[nodiscard]] bool IsVectorFunctionSupported(const AscNode &node) const override { | ||
| 2908 | + (void)node; | ||
| 2909 | + return false; | ||
| 2910 | + } | ||
| 2911 | + [[nodiscard]] std::vector<std::string> LoadApiHeaderFiles([[maybe_unused]] bool is_dynamic) const override { | ||
| 2912 | + return {"datacopy_reg_base.h"}; | ||
| 2913 | + } | ||
| 2914 | + [[nodiscard]] std::vector<std::string> IncludeApiHeaderFiles() const override { | ||
| 2915 | + return {"basic_api/kernel_operator_vec_gather_intf.h", "simt_api/cpp/kernel_simt_intf.h"}; | ||
| 2916 | + } | ||
| 2917 | + [[nodiscard]] bool IsNodeValid(const AscNode &node) const override { | ||
| 2918 | + GE_ASSERT_TRUE(!IsNodeHasScalarInput(node), "Node %s[%s] not support scalar input", node.GetTypePtr(), | ||
| 2919 | + node.GetNamePtr()); | ||
| 2920 | + return true; | ||
| 2921 | + } | ||
| 2922 | +}; | ||
| 2923 | +/*********************************************************************************/ | ||
| 2714 | class TransposeAscIrCodegenImplV2 : public AscIrCodegenV2 { | 2924 | class TransposeAscIrCodegenImplV2 : public AscIrCodegenV2 { |
| 2715 | public: | 2925 | public: |
| 2716 | [[nodiscard]] std::vector<std::unique_ptr<TmpBufDesc>> CalcTmpBufSize(const AscNode &node) override { | 2926 | [[nodiscard]] std::vector<std::unique_ptr<TmpBufDesc>> CalcTmpBufSize(const AscNode &node) override { |
| @@ -3447,6 +3657,20 @@ class BitwiseNotAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 3447 | node.GetTypePtr(), node.GetNamePtr()); | 3657 | node.GetTypePtr(), node.GetNamePtr()); |
| 3448 | return true; | 3658 | return true; |
| 3449 | } | 3659 | } |
| 3660 | + | ||
| 3661 | + [[nodiscard]] bool IsSimtScalarSupported(const AscNode &node) const override { | ||
| 3662 | + auto &outputs = const_cast<AscNode &>(node).outputs; | ||
| 3663 | + auto dt = static_cast<ge::DataType>(outputs[0].attr.dtype); | ||
| 3664 | + return dt == DT_INT8 || dt == DT_INT16 || dt == DT_INT32 || dt == DT_INT64 || dt == DT_UINT8 || dt == DT_UINT16 || | ||
| 3665 | + dt == DT_UINT32 || dt == DT_UINT64; | ||
| 3666 | + } | ||
| 3667 | + | ||
| 3668 | + [[nodiscard]] ge::graphStatus GenerateSimtScalarExpr([[maybe_unused]] const AscNode &node, | ||
| 3669 | + const std::vector<std::string> &inputs, | ||
| 3670 | + std::string &expr) const override { | ||
| 3671 | + expr = "~(" + inputs[0] + ")"; | ||
| 3672 | + return ge::GRAPH_SUCCESS; | ||
| 3673 | + } | ||
| 3450 | }; | 3674 | }; |
| 3451 | 3675 | ||
| 3452 | class BitwiseOrAscIrCodegenImplV2 : public AscIrCodegenV2 { | 3676 | class BitwiseOrAscIrCodegenImplV2 : public AscIrCodegenV2 { |
| @@ -503,6 +503,9 @@ ApiPerfRegister<ApiPerf> add_api_perf_v2(ApiPerfRegisterV2(kAdd, GetPerfFunc(kAd | |||
| 503 | &perf_param_table_v2, &tiling_schedule_config_table_v2)); | 503 | &perf_param_table_v2, &tiling_schedule_config_table_v2)); |
| 504 | ApiPerfRegister<ApiPerf> gather_api_perf_v2(ApiPerfRegisterV2(kGather, GetPerfFunc(kGather), nullptr, | 504 | ApiPerfRegister<ApiPerf> gather_api_perf_v2(ApiPerfRegisterV2(kGather, GetPerfFunc(kGather), nullptr, |
| 505 | &perf_param_table_v2, &tiling_schedule_config_table_v2)); | 505 | &perf_param_table_v2, &tiling_schedule_config_table_v2)); |
| 506 | +ApiPerfRegister<ApiPerf> indirect_load_api_perf_v2(ApiPerfRegisterV2(kIndirectLoad, GetPerfFunc(kUnitVector), nullptr, | ||
| 507 | + &perf_param_table_v2, | ||
| 508 | + &tiling_schedule_config_table_v2)); | ||
| 506 | ApiPerfRegister<ApiPerf> abs_api_perf_v2(ApiPerfRegisterV2(kAbs, GetPerfFunc(kAbs + "V2"), nullptr, | 509 | ApiPerfRegister<ApiPerf> abs_api_perf_v2(ApiPerfRegisterV2(kAbs, GetPerfFunc(kAbs + "V2"), nullptr, |
| 507 | &perf_param_table_v2, &tiling_schedule_config_table_v2)); | 510 | &perf_param_table_v2, &tiling_schedule_config_table_v2)); |
| 508 | ApiPerfRegister<ApiPerf> broadcast_api_perf_v2(ApiPerfRegisterV2(kBroadcast, GetPerfFunc(kBroadcast), nullptr, | 511 | ApiPerfRegister<ApiPerf> broadcast_api_perf_v2(ApiPerfRegisterV2(kBroadcast, GetPerfFunc(kBroadcast), nullptr, |


注册了: negative_index_support need_check_bound max 但: