已合并
fix: 修正动态shape静态子图HCCL零拷贝判断逻辑 #4321
tangqunzhang创建于 8月10日
fix: 修正动态shape静态子图HCCL零拷贝判断逻辑 #4321
已合并
共 5 个文件变更+225-57
| @@ -1283,13 +1283,19 @@ void BlockMemAssigner::UpdateOpTensorMemType(const std::list<NodeIndexIO> &node_ | |||
| 1283 | } | 1283 | } |
| 1284 | } | 1284 | } |
| 1285 | } | 1285 | } |
| 1286 | + | ||
| 1286 | bool BlockMemAssigner::IsZeroCopyBlock(const NodePtr &node, uint32_t output_index, bool continuous, | 1287 | bool BlockMemAssigner::IsZeroCopyBlock(const NodePtr &node, uint32_t output_index, bool continuous, |
| 1287 | size_t output_size) const { | 1288 | size_t output_size) const { |
| 1288 | std::string op_type(node->GetTypePtr()); | 1289 | std::string op_type(node->GetTypePtr()); |
| 1290 | + | ||
| 1291 | + // 现状: | ||
| 1292 | + // 动态shape静态子图的输入,按零拷贝处理(ge执行流程不支持输入做拷贝处理,搞成非零拷贝会有精度问题,输入数据错误) | ||
| 1293 | + // 动态shape静态子图的输出,按非零拷贝处理(搞成零拷贝需要hccl算子支持地址刷新,会导致性能劣化) | ||
| 1289 | if (NodeUtils::IsDynamicShape(node)) { | 1294 | if (NodeUtils::IsDynamicShape(node)) { |
| 1290 | if (compute_graph_.get() != node->GetOwnerComputeGraphBarePtr()) { | 1295 | if (compute_graph_.get() != node->GetOwnerComputeGraphBarePtr()) { |
| 1291 | return false; | 1296 | return false; |
| 1292 | } | 1297 | } |
| 1298 | + | ||
| 1293 | if (OpTypeUtils::IsDataNode(op_type)) { | 1299 | if (OpTypeUtils::IsDataNode(op_type)) { |
| 1294 | return (!continuous); | 1300 | return (!continuous); |
| 1295 | } | 1301 | } |
| @@ -1331,14 +1337,14 @@ bool BlockMemAssigner::IsZeroCopyBlock(const NodePtr &node, uint32_t output_inde | |||
| 1331 | return false; | 1337 | return false; |
| 1332 | } | 1338 | } |
| 1333 | // Data flow to unsupported zero copy task type eg. memcpy, can never zero copied | 1339 | // Data flow to unsupported zero copy task type eg. memcpy, can never zero copied |
| 1334 | - return IsNodeAndPeerNodeTaskSupportZeroCopy(node, output_index, is_feature_map_refreshable_); | 1340 | + return IsNodeAndPeerNodeTaskSupportZeroCopy(node, output_index); |
| 1335 | } | 1341 | } |
| 1336 | 1342 | ||
| 1337 | // Only node output that flow to sure one output maybe zero copied | 1343 | // Only node output that flow to sure one output maybe zero copied |
| 1338 | if (GetOutputFlowToNetoutputNum(node, output_index, compute_graph_, symbol_to_anchors_, anchor_to_symbol_) == | 1344 | if (GetOutputFlowToNetoutputNum(node, output_index, compute_graph_, symbol_to_anchors_, anchor_to_symbol_) == |
| 1339 | 1U) { // 1U means output to only one netoutput | 1345 | 1U) { // 1U means output to only one netoutput |
| 1340 | // Output from unsupported task type eg. memcpy, can never zero copied | 1346 | // Output from unsupported task type eg. memcpy, can never zero copied |
| 1341 | - return IsNodeAndPeerNodeTaskSupportZeroCopy(node, output_index, is_feature_map_refreshable_); | 1347 | + return IsNodeAndPeerNodeTaskSupportZeroCopy(node, output_index); |
| 1342 | } | 1348 | } |
| 1343 | 1349 | ||
| 1344 | return false; | 1350 | return false; |
| @@ -1698,7 +1704,7 @@ Status BlockMemAssigner::ApplyContinuousMemWithMng(const NodePtr &n, int32_t idx | |||
| 1698 | if (iter != symbol_mem_reuse_info_.cend()) { | 1704 | if (iter != symbol_mem_reuse_info_.cend()) { |
| 1699 | block->is_fixed_addr_prior_ = (block->is_fixed_addr_prior_ || iter->second.is_fixed_addr_prior_); | 1705 | block->is_fixed_addr_prior_ = (block->is_fixed_addr_prior_ || iter->second.is_fixed_addr_prior_); |
| 1700 | } | 1706 | } |
| 1701 | - MarkReuseZeroCopyBlockFlag(n, block, idx, is_feature_map_refreshable_); | 1707 | + MarkReuseZeroCopyBlockFlag(n, block, idx); |
| 1702 | MarkZeroCopyBlockAttr(bool_attr_, op_desc, block->is_zero_copy_, OpMemoryType::kOutput, idx); | 1708 | MarkZeroCopyBlockAttr(bool_attr_, op_desc, block->is_zero_copy_, OpMemoryType::kOutput, idx); |
| 1703 | GELOGI( | 1709 | GELOGI( |
| 1704 | "[ContinuousMem]Node name: %s index:%u size:%zu ref count: %d, zero copy:%d, fixed addr prior: %d, " | 1710 | "[ContinuousMem]Node name: %s index:%u size:%zu ref count: %d, zero copy:%d, fixed addr prior: %d, " |
| @@ -2030,7 +2036,7 @@ MemoryBlock *BlockMemAssigner::ApplyOutMemory(const NodePtr &n, uint32_t index, | |||
| 2030 | block_size, block->ref_count_, out_count, block->is_zero_copy_, block->is_fixed_addr_prior_); | 2036 | block_size, block->ref_count_, out_count, block->is_zero_copy_, block->is_fixed_addr_prior_); |
| 2031 | } | 2037 | } |
| 2032 | 2038 | ||
| 2033 | - MarkReuseZeroCopyBlockFlag(n, block, index, is_feature_map_refreshable_); | 2039 | + MarkReuseZeroCopyBlockFlag(n, block, index); |
| 2034 | MarkZeroCopyBlockAttr(bool_attr_, node_op_desc, block->is_zero_copy_, OpMemoryType::kOutput, index); | 2040 | MarkZeroCopyBlockAttr(bool_attr_, node_op_desc, block->is_zero_copy_, OpMemoryType::kOutput, index); |
| 2035 | GELOGI("Node name: %s index:%u size:%zu ref count: %d, out count: %d zero copy:%d, out node need continuous input %d", | 2041 | GELOGI("Node name: %s index:%u size:%zu ref count: %d, out count: %d zero copy:%d, out node need continuous input %d", |
| 2036 | n->GetNamePtr(), index, block_size, block->ref_count_, out_count, block->is_zero_copy_, | 2042 | n->GetNamePtr(), index, block_size, block->ref_count_, out_count, block->is_zero_copy_, |
| @@ -33,17 +33,20 @@ namespace { | |||
| 33 | const std::string kOffline = "offline"; | 33 | const std::string kOffline = "offline"; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | +// 地址不可刷新时不能做零拷贝,也不能和零拷贝节点内存进行复用 | ||
| 37 | +// 和Data、Netoutput连接时用于判断是否可零拷贝,和其他节点连接是用于判断是否可以和零拷贝节点进行复用 | ||
| 36 | bool IsNodeSupportZeroCopy(const ge::NodePtr &node) { | 38 | bool IsNodeSupportZeroCopy(const ge::NodePtr &node) { |
| 37 | bool is_support_zero_copy = ge::MemLayoutConflictUtil::IsAddressRefreshable(node); | 39 | bool is_support_zero_copy = ge::MemLayoutConflictUtil::IsAddressRefreshable(node); |
| 38 | if (!is_support_zero_copy) { | 40 | if (!is_support_zero_copy) { |
| 39 | GELOGI("Op[%s] not support zero copy", node->GetName().c_str()); | 41 | GELOGI("Op[%s] not support zero copy", node->GetName().c_str()); |
| 40 | } else { | 42 | } else { |
| 41 | - // 1.IsAddressRefreshable在动态shape静态子图场景认为hccl算子是可刷新的,实际上因为处理阶段有差异, | 43 | + // IsAddressRefreshable在动态shape静态子图场景认为hccl算子是可刷新的,实际上因为处理阶段有差异, |
| 42 | - // 比如hccl判断时还未拆图,可能导致结果不一致 | 44 | + // 比如hccl判断时还未拆图,可能导致结果不一致,这里还是统一使用IsHcomNodeNotSupportAddrRefresh的结果 |
| 43 | - // 2.静态shape根图图场景,把hccl搞成可刷新会导致性能劣化 | 45 | + // 另外静态子图里hccl支持刷新会有性能劣化,hccl内部会做数据拷贝等额外处理 |
| 44 | - // 因此IsGraphFeatureMapRefreshable为true时,这里还是统一使用IsHcomNodeNotSupportAddrRefresh的结果,和原有处理保持一致 | 46 | + const auto root_graph = GraphUtils::FindRootGraph(node->GetOwnerComputeGraph()); |
| 45 | - if (ge::MemLayoutConflictUtil::IsGraphFeatureMapRefreshable(node->GetOwnerComputeGraph()) && | 47 | + const bool is_dynamic_shape_sub_graph = (root_graph != nullptr) && root_graph->GetGraphUnknownFlag() && |
| 46 | - ge::OpUtils::IsHcomNodeNotSupportAddrRefresh(node->GetOpDesc())) { | 48 | + (!node->GetOwnerComputeGraph()->GetGraphUnknownFlag()); |
| 49 | + if (is_dynamic_shape_sub_graph && ge::OpUtils::IsHcomNodeNotSupportAddrRefresh(node->GetOpDesc())) { | ||
| 47 | GELOGI("hccl engine op[%s] not support zero copy", node->GetName().c_str()); | 50 | GELOGI("hccl engine op[%s] not support zero copy", node->GetName().c_str()); |
| 48 | is_support_zero_copy = false; | 51 | is_support_zero_copy = false; |
| 49 | } | 52 | } |
| @@ -211,8 +214,7 @@ void SetReleaseBlockLifeEnd(MemoryBlock *to_release, int64_t stream_id) { | |||
| 211 | to_release->SetLifeTimeEnd(max_end_life_time, to_release->stream_id_); | 214 | to_release->SetLifeTimeEnd(max_end_life_time, to_release->stream_id_); |
| 212 | } | 215 | } |
| 213 | 216 | ||
| 214 | -void MarkReuseZeroCopyBlockFlag(const NodePtr &n, MemoryBlock *const block, const uint32_t index, | 217 | +void MarkReuseZeroCopyBlockFlag(const NodePtr &n, MemoryBlock *const block, const uint32_t index) { |
| 215 | - bool is_feature_map_refreshable) { | ||
| 216 | auto node_op_desc = n->GetOpDescBarePtr(); | 218 | auto node_op_desc = n->GetOpDescBarePtr(); |
| 217 | 219 | ||
| 218 | // 输出连续内存不能做零拷贝,同时NoPadding且Reuse的也不能做零拷贝 | 220 | // 输出连续内存不能做零拷贝,同时NoPadding且Reuse的也不能做零拷贝 |
| @@ -235,35 +237,12 @@ void MarkReuseZeroCopyBlockFlag(const NodePtr &n, MemoryBlock *const block, cons | |||
| 235 | block->is_reuse_zero_copy_ = false; | 237 | block->is_reuse_zero_copy_ = false; |
| 236 | } | 238 | } |
| 237 | 239 | ||
| 238 | - // data直连特殊的rts算子且不可刷新状态下,不能做零拷贝 | 240 | + GELOGD("Node name: %s index: %d, can_reuse_zero_copy: %s.", n->GetNamePtr(), index, |
| 239 | - const static std::set<std::string> kTaskUnsupportZeroCopyOp{ge::STREAMSWITCH, ge::LABELSWITCHBYINDEX}; | 241 | + can_reuse_zero_copy ? "true" : "false"); |
| 240 | - auto out_anchor = n->GetOutDataAnchor(static_cast<int32_t>(index)); | ||
| 241 | - bool data_connect_unsupport_zero_copy = false; | ||
| 242 | - if (OpTypeUtils::IsDataNode(node_op_desc->GetType()) && (!is_feature_map_refreshable) && (out_anchor != nullptr)) { | ||
| 243 | - auto peer_anchors = out_anchor->GetPeerInDataAnchorsPtr(); | ||
| 244 | - data_connect_unsupport_zero_copy = | ||
| 245 | - std::any_of(peer_anchors.begin(), peer_anchors.end(), [](const ge::InDataAnchor *anchor) { | ||
| 246 | - return ((anchor != nullptr) && (anchor->GetOwnerNodeBarePtr() != nullptr) && | ||
| 247 | - (kTaskUnsupportZeroCopyOp.count(anchor->GetOwnerNodeBarePtr()->GetTypePtr()) > 0U)); | ||
| 248 | - }); | ||
| 249 | - } | ||
| 250 | - | ||
| 251 | - if (data_connect_unsupport_zero_copy) { | ||
| 252 | - block->is_reuse_zero_copy_ = false; | ||
| 253 | - block->is_zero_copy_ = false; | ||
| 254 | - } | ||
| 255 | - | ||
| 256 | - GELOGD("Node name: %s index: %d, can_reuse_zero_copy: %s, data_connect_unsupport_zero_copy: %s ", n->GetNamePtr(), | ||
| 257 | - index, can_reuse_zero_copy ? "true" : "false", data_connect_unsupport_zero_copy ? "true" : "false"); | ||
| 258 | } | 242 | } |
| 259 | 243 | ||
| 260 | -bool IsNodeAndPeerNodeTaskSupportZeroCopy(const ge::NodePtr &node, uint32_t output_index, | 244 | +bool IsNodeAndPeerNodeTaskSupportZeroCopy(const ge::NodePtr &node, uint32_t output_index) { |
| 261 | - bool is_feature_map_refreshable) { | ||
| 262 | GELOGD("Check node %s and peer node of output %u task zero copy supported", node->GetNamePtr(), output_index); | 245 | GELOGD("Check node %s and peer node of output %u task zero copy supported", node->GetNamePtr(), output_index); |
| 263 | - if (is_feature_map_refreshable) { | ||
| 264 | - return true; | ||
| 265 | - } | ||
| 266 | - | ||
| 267 | if (!IsNodeSupportZeroCopy(node)) { | 246 | if (!IsNodeSupportZeroCopy(node)) { |
| 268 | return false; | 247 | return false; |
| 269 | } | 248 | } |
| @@ -30,10 +30,8 @@ bool IsKnownSubgraphData(const ge::Node *node); | |||
| 30 | void SetReleaseBlockLifeEnd(ge::MemoryBlock *to_release, int64_t stream_id); | 30 | void SetReleaseBlockLifeEnd(ge::MemoryBlock *to_release, int64_t stream_id); |
| 31 | void MarkZeroCopyBlockAttr(std::vector<TAttr<bool>> &bool_attr, const ge::OpDesc *const op_desc, bool is_zero_copy, | 31 | void MarkZeroCopyBlockAttr(std::vector<TAttr<bool>> &bool_attr, const ge::OpDesc *const op_desc, bool is_zero_copy, |
| 32 | OpMemoryType mem_type, uint32_t out_index); | 32 | OpMemoryType mem_type, uint32_t out_index); |
| 33 | -void MarkReuseZeroCopyBlockFlag(const NodePtr &n, MemoryBlock *const block, const uint32_t index, | 33 | +void MarkReuseZeroCopyBlockFlag(const NodePtr &n, MemoryBlock *const block, const uint32_t index); |
| 34 | - bool is_feature_map_refreshable); | 34 | +bool IsNodeAndPeerNodeTaskSupportZeroCopy(const ge::NodePtr &node, uint32_t output_index); |
| 35 | -bool IsNodeAndPeerNodeTaskSupportZeroCopy(const ge::NodePtr &node, uint32_t output_index, | ||
| 36 | - bool is_feature_map_refreshable); | ||
| 37 | 35 | ||
| 38 | } // namespace ge | 36 | } // namespace ge |
| 39 | 37 | ||
| @@ -177,7 +177,6 @@ class FakeOpsKernelInfoStore : public OpsKernelInfoStore { | |||
| 177 | } | 177 | } |
| 178 | }; | 178 | }; |
| 179 | FakeOpsKernelInfoStore g_fake_ops_kernel_info_store; | 179 | FakeOpsKernelInfoStore g_fake_ops_kernel_info_store; |
| 180 | - | ||
| 181 | Status GenerateTaskForHcomAllReduce(const Node &node, RunContext &run_context, std::vector<domi::TaskDef> &tasks) { | 180 | Status GenerateTaskForHcomAllReduce(const Node &node, RunContext &run_context, std::vector<domi::TaskDef> &tasks) { |
| 182 | std::cout << "======node.GetType():" << node.GetType() << std::endl; | 181 | std::cout << "======node.GetType():" << node.GetType() << std::endl; |
| 183 | if (node.GetType() != "HcomAllReduce") { | 182 | if (node.GetType() != "HcomAllReduce") { |
| @@ -8790,4 +8789,186 @@ TEST_F(FmMemoryRefreshTest, annotated_args_in_dynamic_root_known_subgraph_refres | |||
| 8790 | runtime_stub.Clear(); | 8789 | runtime_stub.Clear(); |
| 8791 | } | 8790 | } |
| 8792 | 8791 | ||
| 8792 | +/* | ||
| 8793 | + * 动态shape图有两个静态子图,静态子图里有hccl算子 | ||
| 8794 | + * 子图1: data -> hcom_1 -> netoutput (hcom输出作为子图输出) | ||
| 8795 | + * 子图2: data(子图输入) -> hcom_2 (hcom输入是子图输入) | ||
| 8796 | + * root: data_1 -> known_op1 -> root_netoutput | ||
| 8797 | + * data_2 -> known_op2 -> root_netoutput | ||
| 8798 | + */ | ||
| 8799 | +namespace { | ||
| 8800 | +void VerifyZeroCopyInSubgraphs(const ComputeGraphPtr &root_graph) { | ||
| 8801 | + for (const auto &sub_graph : root_graph->GetAllSubgraphs()) { | ||
| 8802 | + if (sub_graph->GetGraphUnknownFlag()) { | ||
| 8803 | + continue; | ||
| 8804 | + } | ||
| 8805 | + for (const auto &node : sub_graph->GetAllNodes()) { | ||
| 8806 | + const auto &op_desc = node->GetOpDesc(); | ||
| 8807 | + if (op_desc == nullptr) { | ||
| 8808 | + continue; | ||
| 8809 | + } | ||
| 8810 | + bool is_zero_copy = false; | ||
| 8811 | + (void)AttrUtils::GetBool(op_desc->GetOutputDescPtr(0), ATTR_IS_ZERO_COPY_BLOCK, is_zero_copy); | ||
| 8812 | + if (OpTypeUtils::IsDataNode(op_desc->GetType())) { | ||
| 8813 | + EXPECT_TRUE(is_zero_copy) << "data node " << op_desc->GetName() << " should be zero copy"; | ||
| 8814 | + } else if (op_desc->GetType() == HCOMALLREDUCE) { | ||
| 8815 | + EXPECT_FALSE(is_zero_copy) << "hccl node " << op_desc->GetName() << " should not be zero copy"; | ||
| 8816 | + } | ||
| 8817 | + } | ||
| 8818 | + } | ||
| 8819 | +} | ||
| 8820 | + | ||
| 8821 | +std::vector<ge::Tensor> MakeTensor(const std::vector<int64_t> &data) { | ||
| 8822 | + TensorDesc desc(Shape({2, 2})); | ||
| 8823 | + desc.SetPlacement(Placement::kPlacementDevice); | ||
| 8824 | + ge::Tensor tensor{desc}; | ||
| 8825 | + tensor.SetData(reinterpret_cast<uint8_t *>(const_cast<int64_t *>(data.data())), data.size() * sizeof(int64_t)); | ||
| 8826 | + return {tensor}; | ||
| 8827 | +} | ||
| 8828 | + | ||
| 8829 | +OpDescPtr BuildHcclNode(const std::string &hcom_name, int64_t hcom_input_offset, int64_t data_output_offset) { | ||
| 8830 | + std::vector<int64_t> shape{2, 2}; | ||
| 8831 | + std::vector<int64_t> memtype_list = {RT_MEMORY_HBM}; | ||
| 8832 | + auto hcom = OP_CFG(HCOMALLREDUCE) | ||
| 8833 | + .TensorDesc(FORMAT_NCHW, DT_FLOAT, shape) | ||
| 8834 | + .InCnt(1) | ||
| 8835 | + .OutCnt(1) | ||
| 8836 | + .Attr(ATTR_NAME_MODIFY_INPUT, true) | ||
| 8837 | + .Attr(ATTR_NAME_INPUT_MEM_TYPE_LIST, memtype_list) | ||
| 8838 | + .Attr(ATTR_NAME_OUTPUT_MEM_TYPE_LIST, memtype_list) | ||
| 8839 | + .Build(hcom_name); | ||
| 8840 | + hcom->SetOpKernelLibName("ops_kernel_info_hccl"); | ||
| 8841 | + hcom->SetWorkspace({0}); | ||
| 8842 | + hcom->SetWorkspaceBytes({512}); | ||
| 8843 | + hcom->SetInputOffset({hcom_input_offset}); | ||
| 8844 | + hcom->SetOutputOffset({data_output_offset}); | ||
| 8845 | + ge::AttrUtils::SetBool(hcom, ATTR_NAME_IS_FIXED_ADDR_PRIOR, true); | ||
| 8846 | + return hcom; | ||
| 8847 | +} | ||
| 8848 | + | ||
| 8849 | +OpDescPtr BuildSubgraphDataNode(const std::string &name, int64_t output_offset) { | ||
| 8850 | + std::vector<int64_t> shape{2, 2}; | ||
| 8851 | + auto data = OP_CFG("Data") | ||
| 8852 | + .TensorDesc(FORMAT_NCHW, DT_FLOAT, shape) | ||
| 8853 | + .InCnt(1) | ||
| 8854 | + .OutCnt(1) | ||
| 8855 | + .Attr(ATTR_NAME_PARENT_NODE_INDEX, 0) | ||
| 8856 | + .Attr(ATTR_NAME_INDEX, 0) | ||
| 8857 | + .Build(name); | ||
| 8858 | + data->SetOutputOffset({output_offset}); | ||
| 8859 | + return data; | ||
| 8860 | +} | ||
| 8861 | + | ||
| 8862 | +OpDescPtr BuildSubgraphNetOutputNode(const std::string &name, int64_t input_offset) { | ||
| 8863 | + std::vector<int64_t> shape{2, 2}; | ||
| 8864 | + auto netoutput = OP_CFG(ge::NETOUTPUT) | ||
| 8865 | + .TensorDesc(FORMAT_NCHW, DT_FLOAT, shape) | ||
| 8866 | + .InCnt(1) | ||
| 8867 | + .OutCnt(1) | ||
| 8868 | + .InputAttr(0, ATTR_NAME_PARENT_NODE_INDEX, 0) | ||
| 8869 | + .Attr(TVM_ATTR_NAME_MAGIC, "RT_DEV_BINARY_MAGIC_ELF") | ||
| 8870 | + .Build(name); | ||
| 8871 | + netoutput->SetInputOffset({input_offset}); | ||
| 8872 | + return netoutput; | ||
| 8873 | +} | ||
| 8874 | + | ||
| 8875 | +ComputeGraphPtr SetupSubgraph(const Graph &sub_graph, const std::string &netoutput_name, const std::string &hcom_name) { | ||
| 8876 | + auto compute_graph = ge::GraphUtilsEx::GetComputeGraph(sub_graph); | ||
| 8877 | + compute_graph->SetGraphUnknownFlag(false); | ||
| 8878 | + compute_graph->FindNode(netoutput_name)->GetOpDesc()->SetSrcName({hcom_name}); | ||
| 8879 | + compute_graph->FindNode(netoutput_name)->GetOpDesc()->SetSrcIndex({0}); | ||
| 8880 | + return compute_graph; | ||
| 8881 | +} | ||
| 8882 | +void RunAndVerify(Session &session, uint32_t graph_id, const ComputeGraphPtr &root_graph, | ||
| 8883 | + GertRuntimeStub &runtime_stub) { | ||
| 8884 | + std::vector<int64_t> input_data(2 * 2, 0); | ||
| 8885 | + for (size_t i = 0; i < input_data.size(); ++i) { | ||
| 8886 | + input_data[i] = static_cast<int64_t>(i + 1) * 0x1000; | ||
| 8887 | + } | ||
| 8888 | + auto inputs = MakeTensor(input_data); | ||
| 8889 | + auto outputs = MakeTensor(std::vector<int64_t>(2 * 2, 0)); | ||
| 8890 | + const auto summary = session.GetCompiledGraphSummary(graph_id); | ||
| 8891 | + EXPECT_NE(summary, nullptr); | ||
| 8892 | + size_t fix_feature_size = 0; | ||
| 8893 | + EXPECT_EQ(SUCCESS, summary->GetFixedFeatureMemorySize(fix_feature_size)); | ||
| 8894 | + std::vector<uint8_t> fixed_feature_mem(fix_feature_size, 0); | ||
| 8895 | + EXPECT_EQ(SUCCESS, session.SetGraphFixedFeatureMemoryBase(graph_id, fixed_feature_mem.data(), fix_feature_size)); | ||
| 8896 | + VerifyZeroCopyInSubgraphs(root_graph); | ||
| 8897 | + runtime_stub.Clear(); | ||
| 8898 | + EXPECT_EQ(SUCCESS, session.RunGraphWithStreamAsync(graph_id, nullptr, inputs, outputs)); | ||
| 8899 | + OpsKernelBuilderRegistry::GetInstance().Unregister("ops_kernel_info_hccl"); | ||
| 8900 | + runtime_stub.Clear(); | ||
| 8901 | +} | ||
| 8902 | +OpDescPtr BuildRootDataNode(const std::vector<int64_t> &shape) { | ||
| 8903 | + return OP_CFG("Data") | ||
| 8904 | + .TensorDesc(FORMAT_NCHW, DT_FLOAT, shape) | ||
| 8905 | + .InCnt(1) | ||
| 8906 | + .OutCnt(1) | ||
| 8907 | + .Attr(ATTR_NAME_INDEX, 0) | ||
| 8908 | + .Build("data_1"); | ||
| 8909 | +} | ||
| 8910 | + | ||
| 8911 | +OpDescPtr BuildPartitionedCallNode(const std::string &name, const std::vector<int64_t> &shape) { | ||
| 8912 | + return OP_CFG(ge::PARTITIONEDCALL).TensorDesc(FORMAT_NCHW, DT_FLOAT, shape).InCnt(1).OutCnt(1).Build(name); | ||
| 8913 | +} | ||
| 8914 | + | ||
| 8915 | +OpDescPtr BuildRootNetOutputNode(const std::vector<int64_t> &shape) { | ||
| 8916 | + auto netoutput = | ||
| 8917 | + OP_CFG(ge::NETOUTPUT).TensorDesc(FORMAT_NCHW, DT_FLOAT, shape).InCnt(1).OutCnt(1).Build("root_netoutput"); | ||
| 8918 | + netoutput->SetSrcName({"known_op2"}); | ||
| 8919 | + netoutput->SetSrcIndex({0}); | ||
| 8920 | + return netoutput; | ||
| 8921 | +} | ||
| 8922 | +} // namespace | ||
| 8923 | + | ||
| 8924 | +TEST_F(FmMemoryRefreshTest, hccl_zero_copy_dynamic_shape_two_static_subgraph) { | ||
| 8925 | + GertRuntimeStub runtime_stub; | ||
| 8926 | + MockForGenerateTask("ops_kernel_info_hccl", GenerateTaskForHcomAllReduce); | ||
| 8927 | + std::vector<int64_t> shape{2, 2}; | ||
| 8928 | + | ||
| 8929 | + auto data_sub1 = BuildSubgraphDataNode("data_sub1", 32); | ||
| 8930 | + auto hcom_1 = BuildHcclNode("hcom_1", 0, 32); | ||
| 8931 | + auto sub_1_netoutput = BuildSubgraphNetOutputNode("sub_1_netoutput", 0); | ||
| 8932 | + auto data_sub2 = BuildSubgraphDataNode("data_sub2", 64); | ||
| 8933 | + auto hcom_2 = BuildHcclNode("hcom_2", 16, 64); | ||
| 8934 | + auto sub_2_netoutput = BuildSubgraphNetOutputNode("sub_2_netoutput", 16); | ||
| 8935 | + auto known_op1 = BuildPartitionedCallNode("known_op1", shape); | ||
| 8936 | + auto known_op2 = BuildPartitionedCallNode("known_op2", shape); | ||
| 8937 | + auto data_1 = BuildRootDataNode(shape); | ||
| 8938 | + auto root_netoutput = BuildRootNetOutputNode(shape); | ||
| 8939 | + | ||
| 8940 | + DEF_GRAPH(sub_1) { | ||
| 8941 | + CHAIN(NODE(data_sub1)->NODE(hcom_1)->NODE(sub_1_netoutput)); | ||
| 8942 | + }; | ||
| 8943 | + DEF_GRAPH(sub_2) { | ||
| 8944 | + CHAIN(NODE(data_sub2)->NODE(hcom_2)->NODE(sub_2_netoutput)); | ||
| 8945 | + }; | ||
| 8946 | + DEF_GRAPH(root) { | ||
| 8947 | + CHAIN(NODE(data_1)->EDGE(0, 0)->NODE(known_op1)->EDGE(0, 0)->NODE(known_op2)->EDGE(0, 0)->NODE(root_netoutput)); | ||
| 8948 | + }; | ||
| 8949 | + | ||
| 8950 | + auto graph = ToGeGraph(root); | ||
| 8951 | + auto root_graph = ge::GraphUtilsEx::GetComputeGraph(graph); | ||
| 8952 | + root_graph->SetGraphUnknownFlag(true); | ||
| 8953 | + (void)AttrUtils::SetBool(root_graph, ATTR_NAME_DYNAMIC_SHAPE_PARTITIONED, true); | ||
| 8954 | + auto sub_graph1 = ToGeGraph(sub_1); | ||
| 8955 | + auto compute_graph1 = SetupSubgraph(sub_graph1, "sub_1_netoutput", "hcom_1"); | ||
| 8956 | + auto sub_graph2 = ToGeGraph(sub_2); | ||
| 8957 | + auto compute_graph2 = SetupSubgraph(sub_graph2, "sub_2_netoutput", "hcom_2"); | ||
| 8958 | + | ||
| 8959 | + auto known_node1 = root_graph->FindNode("known_op1"); | ||
| 8960 | + auto known_node2 = root_graph->FindNode("known_op2"); | ||
| 8961 | + (void)AttrUtils::SetBool(known_node1->GetOpDesc(), ATTR_NAME_IS_UNKNOWN_SHAPE, true); | ||
| 8962 | + SetSubGraph(root_graph, known_node1, compute_graph1); | ||
| 8963 | + SetSubGraph(root_graph, known_node2, compute_graph2); | ||
| 8964 | + AddCompileResult(known_node1, false); | ||
| 8965 | + AddCompileResult(known_node2, false); | ||
| 8966 | + | ||
| 8967 | + std::map<AscendString, AscendString> options; | ||
| 8968 | + Session session(options); | ||
| 8969 | + session.AddGraph(1U, graph); | ||
| 8970 | + EXPECT_EQ(session.CompileGraph(1U), SUCCESS); | ||
| 8971 | + RunAndVerify(session, 1U, root_graph, runtime_stub); | ||
| 8972 | +} | ||
| 8973 | + | ||
| 8793 | } // namespace ge | 8974 | } // namespace ge |
| @@ -307,6 +307,7 @@ TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_hccl) { | |||
| 307 | } | 307 | } |
| 308 | 308 | ||
| 309 | TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_rts) { | 309 | TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_rts) { |
| 310 | + ge::GetThreadLocalContext().SetGraphOption({{"ge.featureBaseRefreshable", "1"}}); | ||
| 310 | auto root_builder = block_mem_ut::GraphBuilder("root_graph"); | 311 | auto root_builder = block_mem_ut::GraphBuilder("root_graph"); |
| 311 | const auto &data = root_builder.AddNode("data", DATA, 0, 1); | 312 | const auto &data = root_builder.AddNode("data", DATA, 0, 1); |
| 312 | const auto &rts = root_builder.AddNode("STREAMSWITCH", STREAMSWITCH, 1, 1); | 313 | const auto &rts = root_builder.AddNode("STREAMSWITCH", STREAMSWITCH, 1, 1); |
| @@ -325,15 +326,17 @@ TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_rts) { | |||
| 325 | ge::GetThreadLocalContext().SetGraphOption({{}}); | 326 | ge::GetThreadLocalContext().SetGraphOption({{}}); |
| 326 | } | 327 | } |
| 327 | 328 | ||
| 328 | -TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_refreshable_unsupport_hccl) { | 329 | +TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_static_graph_support_hccl) { |
| 329 | auto root_builder = block_mem_ut::GraphBuilder("root_graph"); | 330 | auto root_builder = block_mem_ut::GraphBuilder("root_graph"); |
| 330 | ge::GetThreadLocalContext().SetGraphOption({{"ge.featureBaseRefreshable", "1"}}); | 331 | ge::GetThreadLocalContext().SetGraphOption({{"ge.featureBaseRefreshable", "1"}}); |
| 331 | - const auto &add = root_builder.AddNode("add", ADD, 0, 1); | 332 | + const auto &data = root_builder.AddNode("data", DATA, 0, 1); |
| 333 | + const auto &hcom = root_builder.AddNode("hcom", HCOMALLREDUCE, 1, 1); | ||
| 332 | const auto &netout = root_builder.AddNode("NETOUTPUT", NETOUTPUT, 1, 1); | 334 | const auto &netout = root_builder.AddNode("NETOUTPUT", NETOUTPUT, 1, 1); |
| 333 | - root_builder.AddDataEdge(add, 0, netout, 0); | 335 | + root_builder.AddDataEdge(data, 0, hcom, 0); |
| 336 | + root_builder.AddDataEdge(hcom, 0, netout, 0); | ||
| 334 | const auto &root_graph = root_builder.GetGraph(); | 337 | const auto &root_graph = root_builder.GetGraph(); |
| 335 | 338 | ||
| 336 | - auto op_desc = root_graph->FindNode("add")->GetOpDesc(); | 339 | + auto op_desc = root_graph->FindNode("hcom")->GetOpDesc(); |
| 337 | op_desc->SetOpKernelLibName(ge::kEngineNameHccl.c_str()); | 340 | op_desc->SetOpKernelLibName(ge::kEngineNameHccl.c_str()); |
| 338 | 341 | ||
| 339 | MemAssistInfo mem_assist_info; | 342 | MemAssistInfo mem_assist_info; |
| @@ -341,7 +344,8 @@ TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_refreshable_unsupport_hccl) { | |||
| 341 | auto ret = GraphUtils::GetRefMapping(root_graph, mem_assist_info.symbol_to_anchors, mem_assist_info.anchor_to_symbol); | 344 | auto ret = GraphUtils::GetRefMapping(root_graph, mem_assist_info.symbol_to_anchors, mem_assist_info.anchor_to_symbol); |
| 342 | EXPECT_EQ(ret, SUCCESS); | 345 | EXPECT_EQ(ret, SUCCESS); |
| 343 | auto p1 = std::make_shared<FakBlockMemAssigner>(mem_assist_info); | 346 | auto p1 = std::make_shared<FakBlockMemAssigner>(mem_assist_info); |
| 344 | - EXPECT_EQ(p1->IsZeroCopyBlock(add, 0, false), false); | 347 | + EXPECT_EQ(p1->IsZeroCopyBlock(hcom, 0, false), true); |
| 348 | + EXPECT_EQ(p1->IsZeroCopyBlock(data, 0, false), true); | ||
| 345 | ge::GetThreadLocalContext().SetGraphOption({{}}); | 349 | ge::GetThreadLocalContext().SetGraphOption({{}}); |
| 346 | } | 350 | } |
| 347 | 351 | ||
| @@ -354,10 +358,10 @@ TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_hccl_dynamic_static_subg | |||
| 354 | 358 | ||
| 355 | auto sub_builder = std::make_shared<block_mem_ut::GraphBuilder>("sub_graph"); | 359 | auto sub_builder = std::make_shared<block_mem_ut::GraphBuilder>("sub_graph"); |
| 356 | const auto &data = sub_builder->AddNode("data", DATA, 0, 1); | 360 | const auto &data = sub_builder->AddNode("data", DATA, 0, 1); |
| 357 | - const auto &add = sub_builder->AddNode("add", ADD, 1, 1); | 361 | + const auto &hcom = sub_builder->AddNode("hcom", HCOMALLREDUCE, 1, 1); |
| 358 | const auto &netout = sub_builder->AddNode("NETOUTPUT", NETOUTPUT, 1, 1); | 362 | const auto &netout = sub_builder->AddNode("NETOUTPUT", NETOUTPUT, 1, 1); |
| 359 | - sub_builder->AddDataEdge(data, 0, add, 0); | 363 | + sub_builder->AddDataEdge(data, 0, hcom, 0); |
| 360 | - sub_builder->AddDataEdge(add, 0, netout, 0); | 364 | + sub_builder->AddDataEdge(hcom, 0, netout, 0); |
| 361 | const auto &sub_graph = sub_builder->GetGraph(); | 365 | const auto &sub_graph = sub_builder->GetGraph(); |
| 362 | 366 | ||
| 363 | partitioned_call->GetOpDesc()->AddSubgraphName(sub_graph->GetName()); | 367 | partitioned_call->GetOpDesc()->AddSubgraphName(sub_graph->GetName()); |
| @@ -366,7 +370,7 @@ TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_hccl_dynamic_static_subg | |||
| 366 | sub_graph->SetParentNode(partitioned_call); | 370 | sub_graph->SetParentNode(partitioned_call); |
| 367 | root_graph->AddSubgraph(sub_graph); | 371 | root_graph->AddSubgraph(sub_graph); |
| 368 | 372 | ||
| 369 | - auto op_desc = sub_graph->FindNode("add")->GetOpDesc(); | 373 | + auto op_desc = sub_graph->FindNode("hcom")->GetOpDesc(); |
| 370 | op_desc->SetOpKernelLibName(ge::kEngineNameHccl.c_str()); | 374 | op_desc->SetOpKernelLibName(ge::kEngineNameHccl.c_str()); |
| 371 | 375 | ||
| 372 | MemAssistInfo mem_assist_info; | 376 | MemAssistInfo mem_assist_info; |
| @@ -374,8 +378,10 @@ TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_hccl_dynamic_static_subg | |||
| 374 | auto ret = GraphUtils::GetRefMapping(sub_graph, mem_assist_info.symbol_to_anchors, mem_assist_info.anchor_to_symbol); | 378 | auto ret = GraphUtils::GetRefMapping(sub_graph, mem_assist_info.symbol_to_anchors, mem_assist_info.anchor_to_symbol); |
| 375 | EXPECT_EQ(ret, SUCCESS); | 379 | EXPECT_EQ(ret, SUCCESS); |
| 376 | auto p1 = std::make_shared<FakBlockMemAssigner>(mem_assist_info); | 380 | auto p1 = std::make_shared<FakBlockMemAssigner>(mem_assist_info); |
| 377 | - EXPECT_EQ(p1->IsZeroCopyBlock(add, 0, false), false); | 381 | + // data support zero copy |
| 382 | + // net out not support zero copy | ||
| 378 | EXPECT_EQ(p1->IsZeroCopyBlock(data, 0, false), true); | 383 | EXPECT_EQ(p1->IsZeroCopyBlock(data, 0, false), true); |
| 384 | + EXPECT_EQ(p1->IsZeroCopyBlock(hcom, 0, false), false); | ||
| 379 | } | 385 | } |
| 380 | 386 | ||
| 381 | TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_hccl_with_dynamic) { | 387 | TEST_F(UtestBlockMemAssigner, IsZeroCopyBlock_unsupport_hccl_with_dynamic) { |
| @@ -1153,13 +1159,11 @@ TEST_F(UtestBlockMemAssigner, ContinuousOutputCanNotZeroCpy) { | |||
| 1153 | MemoryBlock child(reuse_strategy_, block_size, stream_id, is_reuse_mem, memory_type); | 1159 | MemoryBlock child(reuse_strategy_, block_size, stream_id, is_reuse_mem, memory_type); |
| 1154 | child.is_reuse_zero_copy_ = true; | 1160 | child.is_reuse_zero_copy_ = true; |
| 1155 | child.is_zero_copy_ = true; | 1161 | child.is_zero_copy_ = true; |
| 1156 | - MarkReuseZeroCopyBlockFlag(node_a, &child, 0, false); | 1162 | + MarkReuseZeroCopyBlockFlag(node_a, &child, 0); |
| 1157 | EXPECT_EQ(child.is_reuse_zero_copy_, false); | 1163 | EXPECT_EQ(child.is_reuse_zero_copy_, false); |
| 1158 | - EXPECT_EQ(child.is_zero_copy_, true); | ||
| 1159 | 1164 | ||
| 1160 | - MarkReuseZeroCopyBlockFlag(node, &child, 0, false); | 1165 | + MarkReuseZeroCopyBlockFlag(node, &child, 0); |
| 1161 | EXPECT_EQ(child.is_reuse_zero_copy_, false); | 1166 | EXPECT_EQ(child.is_reuse_zero_copy_, false); |
| 1162 | - EXPECT_EQ(child.is_zero_copy_, false); | ||
| 1163 | } | 1167 | } |
| 1164 | 1168 | ||
| 1165 | TEST_F(UtestBlockMemAssigner, DataOutDiffStream) { | 1169 | TEST_F(UtestBlockMemAssigner, DataOutDiffStream) { |