已合并
fix: ensure deterministic codegen and reliable PGO fallback #1662
zhang_shengjie创建于 8月5日
fix: ensure deterministic codegen and reliable PGO fallback #1662
已合并
共 15 个文件变更+318-54
| @@ -28,7 +28,7 @@ void TilingLib::GenSharedPgoRuntimeProfiling(const ascir::FusedScheduledResult & | |||
| 28 | std::string TilingLib::GenerateForPgo(const ascir::FusedScheduledResult &fused_schedule_result, | 28 | std::string TilingLib::GenerateForPgo(const ascir::FusedScheduledResult &fused_schedule_result, |
| 29 | const std::string &pgo_dir) const { | 29 | const std::string &pgo_dir) const { |
| 30 | if (ShouldFallbackPgo(fused_schedule_result)) { | 30 | if (ShouldFallbackPgo(fused_schedule_result)) { |
| 31 | - return "int main() { return 0; }\n"; | 31 | + return "int main() { return 1; }\n"; |
| 32 | } | 32 | } |
| 33 | std::stringstream ss; | 33 | std::stringstream ss; |
| 34 | GenPgoHeaders(ss, false); | 34 | GenPgoHeaders(ss, false); |
| @@ -607,8 +607,11 @@ def extract_time(line): | |||
| 607 | 607 | ||
| 608 | 608 | ||
| 609 | def pgo_get_top_result(search_path, top_n=5): | 609 | def pgo_get_top_result(search_path, top_n=5): |
| 610 | - with open(search_path, "r") as file: | 610 | + try: |
| 611 | - lines = [line.strip() for line in file if line.strip()] | 611 | + with open(search_path, "r") as file: |
| 612 | + lines = [line.strip() for line in file if line.strip()] | ||
| 613 | + except OSError: | ||
| 614 | + return None, None, None | ||
| 612 | 615 | ||
| 613 | if not lines: | 616 | if not lines: |
| 614 | return None, None, None | 617 | return None, None, None |
| @@ -571,6 +571,7 @@ Status BufQueAllocator::InitTensorInfo(af::AscGraph &graph, TensorInfoMap &tenso | |||
| 571 | } | 571 | } |
| 572 | 572 | ||
| 573 | Status BufQueAllocator::InitNodeTmpBuffInfo(af::AscGraph &graph, TmpBuffInfoMap &node_attr_to_tensor_info) { | 573 | Status BufQueAllocator::InitNodeTmpBuffInfo(af::AscGraph &graph, TmpBuffInfoMap &node_attr_to_tensor_info) { |
| 574 | + int64_t allocation_order = 0; | ||
| 574 | for (const auto &node : graph.GetAllNodes()) { | 575 | for (const auto &node : graph.GetAllNodes()) { |
| 575 | GE_ASSERT_NOTNULL(node); | 576 | GE_ASSERT_NOTNULL(node); |
| 576 | if (ScheduleUtils::IsBuffer(node)) { | 577 | if (ScheduleUtils::IsBuffer(node)) { |
| @@ -578,6 +579,7 @@ Status BufQueAllocator::InitNodeTmpBuffInfo(af::AscGraph &graph, TmpBuffInfoMap | |||
| 578 | } | 579 | } |
| 579 | for (auto &tmp_buff : node->attr.tmp_buffers) { | 580 | for (auto &tmp_buff : node->attr.tmp_buffers) { |
| 580 | auto &tmp_buff_info = node_attr_to_tensor_info[&tmp_buff]; | 581 | auto &tmp_buff_info = node_attr_to_tensor_info[&tmp_buff]; |
| 582 | + tmp_buff_info.allocation_order = allocation_order++; | ||
| 581 | tmp_buff_info.mem_position = af::Position::kPositionVecCalc; | 583 | tmp_buff_info.mem_position = af::Position::kPositionVecCalc; |
| 582 | tmp_buff_info.life_start = 0L; | 584 | tmp_buff_info.life_start = 0L; |
| 583 | tmp_buff_info.life_end = std::numeric_limits<int64_t>::max(); | 585 | tmp_buff_info.life_end = std::numeric_limits<int64_t>::max(); |
| @@ -13,6 +13,13 @@ | |||
| 13 | namespace optimize { | 13 | namespace optimize { |
| 14 | size_t kDbReuseThreshold = 2UL; | 14 | size_t kDbReuseThreshold = 2UL; |
| 15 | 15 | ||
| 16 | +static bool TensorGroupLifeLess(const TensorGroup &lhs, const TensorGroup &rhs) { | ||
| 17 | + if (lhs.merged_life_start != rhs.merged_life_start) { | ||
| 18 | + return lhs.merged_life_start < rhs.merged_life_start; | ||
| 19 | + } | ||
| 20 | + return std::make_pair(lhs.allocation_order, lhs.group_id) < std::make_pair(rhs.allocation_order, rhs.group_id); | ||
| 21 | +} | ||
| 22 | + | ||
| 16 | bool MemReuseManager::IsLifetimeOverlap(int64_t start1, int64_t end1, int64_t start2, int64_t end2) { | 23 | bool MemReuseManager::IsLifetimeOverlap(int64_t start1, int64_t end1, int64_t start2, int64_t end2) { |
| 17 | if (end1 == std::numeric_limits<int64_t>::max() || end2 == std::numeric_limits<int64_t>::max()) { | 24 | if (end1 == std::numeric_limits<int64_t>::max() || end2 == std::numeric_limits<int64_t>::max()) { |
| 18 | return true; | 25 | return true; |
| @@ -23,16 +30,16 @@ bool MemReuseManager::IsLifetimeOverlap(int64_t start1, int64_t end1, int64_t st | |||
| 23 | void MemReuseManager::MergeTensorByGroupId(std::vector<TensorGroup> ©_in_groups, | 30 | void MemReuseManager::MergeTensorByGroupId(std::vector<TensorGroup> ©_in_groups, |
| 24 | std::vector<TensorGroup> ©_out_groups, | 31 | std::vector<TensorGroup> ©_out_groups, |
| 25 | std::vector<TensorGroup> &calc_groups) const { | 32 | std::vector<TensorGroup> &calc_groups) const { |
| 26 | - using GroupKey = int64_t; | 33 | + std::unordered_map<int64_t, TensorGroup> temp_groups; |
| 27 | - std::unordered_map<GroupKey, TensorGroup> temp_groups; | ||
| 28 | for (auto &info : tensor_attr_to_tensor_info_) { | 34 | for (auto &info : tensor_attr_to_tensor_info_) { |
| 29 | auto cur_tensor = &info.second; | 35 | auto cur_tensor = &info.second; |
| 30 | 36 | ||
| 31 | - GroupKey key = cur_tensor->group_id; | 37 | + const auto key = cur_tensor->group_id; |
| 32 | auto [it, is_new] = temp_groups.try_emplace(key); | 38 | auto [it, is_new] = temp_groups.try_emplace(key); |
| 33 | TensorGroup &group = it->second; | 39 | TensorGroup &group = it->second; |
| 34 | if (is_new) { | 40 | if (is_new) { |
| 35 | group.group_id = cur_tensor->group_id; | 41 | group.group_id = cur_tensor->group_id; |
| 42 | + group.allocation_order = cur_tensor->allocation_order; | ||
| 36 | group.grouped_tensors = {cur_tensor}; | 43 | group.grouped_tensors = {cur_tensor}; |
| 37 | group.merged_life_start = cur_tensor->life_start; | 44 | group.merged_life_start = cur_tensor->life_start; |
| 38 | group.merged_life_end = cur_tensor->life_end; | 45 | group.merged_life_end = cur_tensor->life_end; |
| @@ -211,8 +218,7 @@ MemoryBlock *MemReuseManager::SelectBestMemoryBlock(const TensorGroup &tensor_gr | |||
| 211 | } | 218 | } |
| 212 | 219 | ||
| 213 | void MemReuseManager::AllocForTQue(MemoryType mem_type, std::vector<TensorGroup> &que_groups) { | 220 | void MemReuseManager::AllocForTQue(MemoryType mem_type, std::vector<TensorGroup> &que_groups) { |
| 214 | - std::sort(que_groups.begin(), que_groups.end(), | 221 | + std::sort(que_groups.begin(), que_groups.end(), TensorGroupLifeLess); |
| 215 | - [](const TensorGroup &a, const TensorGroup &b) { return a.merged_life_start < b.merged_life_start; }); | ||
| 216 | int64_t last_block_id = -1; | 222 | int64_t last_block_id = -1; |
| 217 | for (const auto &tensor : que_groups) { | 223 | for (const auto &tensor : que_groups) { |
| 218 | // 被标记为不能复用,需要直接创建 | 224 | // 被标记为不能复用,需要直接创建 |
| @@ -237,8 +243,7 @@ void MemReuseManager::AllocForTQue(MemoryType mem_type, std::vector<TensorGroup> | |||
| 237 | } | 243 | } |
| 238 | 244 | ||
| 239 | void MemReuseManager::AllocForCalc(std::vector<TensorGroup> &calc_groups) { | 245 | void MemReuseManager::AllocForCalc(std::vector<TensorGroup> &calc_groups) { |
| 240 | - std::sort(calc_groups.begin(), calc_groups.end(), | 246 | + std::sort(calc_groups.begin(), calc_groups.end(), TensorGroupLifeLess); |
| 241 | - [](const TensorGroup &a, const TensorGroup &b) { return a.merged_life_start < b.merged_life_start; }); | ||
| 242 | for (const auto &tensor_group : calc_groups) { | 247 | for (const auto &tensor_group : calc_groups) { |
| 243 | // 不能复用别人的tensor,直接创建新块 | 248 | // 不能复用别人的tensor,直接创建新块 |
| 244 | if (!tensor_group.group_is_can_reuse_others) { | 249 | if (!tensor_group.group_is_can_reuse_others) { |
| @@ -311,6 +316,7 @@ void MemReuseManager::AllocTmpBuff(std::map<af::TmpBuffer *, std::vector<TensorG | |||
| 311 | auto cur_tensor = &info.second; | 316 | auto cur_tensor = &info.second; |
| 312 | TensorGroup group; | 317 | TensorGroup group; |
| 313 | group.group_id = cur_tensor->group_id; | 318 | group.group_id = cur_tensor->group_id; |
| 319 | + group.allocation_order = cur_tensor->allocation_order; | ||
| 314 | group.grouped_tensors = {cur_tensor}; | 320 | group.grouped_tensors = {cur_tensor}; |
| 315 | group.merged_life_start = cur_tensor->life_start; | 321 | group.merged_life_start = cur_tensor->life_start; |
| 316 | group.merged_life_end = cur_tensor->life_end; | 322 | group.merged_life_end = cur_tensor->life_end; |
| @@ -320,36 +326,36 @@ void MemReuseManager::AllocTmpBuff(std::map<af::TmpBuffer *, std::vector<TensorG | |||
| 320 | group.group_is_reusable = cur_tensor->is_reusable; | 326 | group.group_is_reusable = cur_tensor->is_reusable; |
| 321 | tmp_buff_to_groups[info.first].push_back(std::move(group)); | 327 | tmp_buff_to_groups[info.first].push_back(std::move(group)); |
| 322 | } | 328 | } |
| 329 | + std::vector<std::pair<af::TmpBuffer *, const TensorGroup *>> ordered_groups; | ||
| 323 | for (const auto &info : tmp_buff_to_groups) { | 330 | for (const auto &info : tmp_buff_to_groups) { |
| 324 | - // 不能复用别人的tensor,直接创建新块 | ||
| 325 | - auto tmp_buff = info.first; | ||
| 326 | - tmp_buff->mem.alloc_type = af::AllocType::kAllocTypeBuffer; | ||
| 327 | for (const auto &group : info.second) { | 331 | for (const auto &group : info.second) { |
| 328 | - if (group.group_id != -1) { | 332 | + ordered_groups.emplace_back(info.first, &group); |
| 329 | - tmp_buff->id = buf_id_; | ||
| 330 | - CreateMemBlockByType(&group, MemoryType::kLoopTmpBuff); | ||
| 331 | - continue; | ||
| 332 | - } | ||
| 333 | - | ||
| 334 | - if (type_blocks_[MemoryType::kTmpBuff].empty()) { | ||
| 335 | - tmp_buff->id = buf_id_; | ||
| 336 | - CreateMemBlockByType(&group, MemoryType::kTmpBuff); | ||
| 337 | - continue; | ||
| 338 | - } | ||
| 339 | - // 更新内存块信息 | ||
| 340 | - std::vector<MemoryBlock *> candidate_blocks; | ||
| 341 | - FindCandidateTmpBuffBlockWithSizeCheck(group, candidate_blocks); | ||
| 342 | - if (candidate_blocks.empty()) { | ||
| 343 | - tmp_buff->id = buf_id_; | ||
| 344 | - CreateMemBlockByType(&group, MemoryType::kTmpBuff); | ||
| 345 | - continue; | ||
| 346 | - } | ||
| 347 | - | ||
| 348 | - tmp_buff->id = candidate_blocks[0]->id; | ||
| 349 | - candidate_blocks[0]->tensor_groups.push_back(&group); | ||
| 350 | - GELOGD("[MemReuse] reuse mem block with type[%d] id[%d] for group[%ld].", | ||
| 351 | - static_cast<int>(candidate_blocks[0]->mem_type), candidate_blocks[0]->id, group.group_id); | ||
| 352 | } | 333 | } |
| 353 | } | 334 | } |
| 335 | + std::sort(ordered_groups.begin(), ordered_groups.end(), | ||
| 336 | + [](const auto &lhs, const auto &rhs) { return TensorGroupLifeLess(*lhs.second, *rhs.second); }); | ||
| 337 | + for (const auto &[tmp_buff, group] : ordered_groups) { | ||
| 338 | + AllocTmpBuffGroup(tmp_buff, *group); | ||
| 339 | + } | ||
| 340 | +} | ||
| 341 | + | ||
| 342 | +void MemReuseManager::AllocTmpBuffGroup(af::TmpBuffer *tmp_buff, const TensorGroup &group) { | ||
| 343 | + tmp_buff->mem.alloc_type = af::AllocType::kAllocTypeBuffer; | ||
| 344 | + if (group.group_id != -1) { | ||
| 345 | + tmp_buff->id = buf_id_; | ||
| 346 | + CreateMemBlockByType(&group, MemoryType::kLoopTmpBuff); | ||
| 347 | + return; | ||
| 348 | + } | ||
| 349 | + std::vector<MemoryBlock *> candidate_blocks; | ||
| 350 | + FindCandidateTmpBuffBlockWithSizeCheck(group, candidate_blocks); | ||
| 351 | + if (candidate_blocks.empty()) { | ||
| 352 | + tmp_buff->id = buf_id_; | ||
| 353 | + CreateMemBlockByType(&group, MemoryType::kTmpBuff); | ||
| 354 | + return; | ||
| 355 | + } | ||
| 356 | + tmp_buff->id = candidate_blocks[0]->id; | ||
| 357 | + candidate_blocks[0]->tensor_groups.push_back(&group); | ||
| 358 | + GELOGD("[MemReuse] reuse mem block with type[%d] id[%d] for group[%ld].", | ||
| 359 | + static_cast<int>(candidate_blocks[0]->mem_type), candidate_blocks[0]->id, group.group_id); | ||
| 354 | } | 360 | } |
| 355 | } // namespace optimize | 361 | } // namespace optimize |
| @@ -36,6 +36,7 @@ class MemReuseManager { | |||
| 36 | std::vector<MemoryBlock *> &candidate_blocks); | 36 | std::vector<MemoryBlock *> &candidate_blocks); |
| 37 | static void SetQueBufIdToTensorAttr(const MemoryBlock &block); | 37 | static void SetQueBufIdToTensorAttr(const MemoryBlock &block); |
| 38 | void AllocTmpBuff(std::map<af::TmpBuffer *, std::vector<TensorGroup>> &tmp_buff_to_groups); | 38 | void AllocTmpBuff(std::map<af::TmpBuffer *, std::vector<TensorGroup>> &tmp_buff_to_groups); |
| 39 | + void AllocTmpBuffGroup(af::TmpBuffer *tmp_buff, const TensorGroup &group); | ||
| 39 | void FindCandidateTmpBuffBlockWithSizeCheck(const TensorGroup &tensor_group, | 40 | void FindCandidateTmpBuffBlockWithSizeCheck(const TensorGroup &tensor_group, |
| 40 | std::vector<MemoryBlock *> &candidate_blocks); | 41 | std::vector<MemoryBlock *> &candidate_blocks); |
| 41 | // 按类型分组的内存块 | 42 | // 按类型分组的内存块 |
| @@ -24,6 +24,7 @@ enum class MemorySizeLevel : int32_t { kScalar = 0, kMedium, kLargest }; | |||
| 24 | // tensor块大小信息 | 24 | // tensor块大小信息 |
| 25 | struct TensorInfo { | 25 | struct TensorInfo { |
| 26 | int64_t group_id{-1}; | 26 | int64_t group_id{-1}; |
| 27 | + int64_t allocation_order{-1}; | ||
| 27 | af::AscTensorAttr *output_tensor_attr{nullptr}; | 28 | af::AscTensorAttr *output_tensor_attr{nullptr}; |
| 28 | int64_t life_start{-1}; | 29 | int64_t life_start{-1}; |
| 29 | int64_t life_end{-1}; | 30 | int64_t life_end{-1}; |
| @@ -61,6 +62,7 @@ struct TensorInfo { | |||
| 61 | // 需要绑定进行内存分配的tensor链 | 62 | // 需要绑定进行内存分配的tensor链 |
| 62 | struct TensorGroup { | 63 | struct TensorGroup { |
| 63 | int64_t group_id{-1}; | 64 | int64_t group_id{-1}; |
| 65 | + int64_t allocation_order{-1}; | ||
| 64 | std::vector<TensorInfo *> grouped_tensors; // group中的tensor,会绑定生命周期 | 66 | std::vector<TensorInfo *> grouped_tensors; // group中的tensor,会绑定生命周期 |
| 65 | std::set<int64_t> merged_loop_axes; | 67 | std::set<int64_t> merged_loop_axes; |
| 66 | int64_t merged_life_start; | 68 | int64_t merged_life_start; |
| @@ -256,8 +256,27 @@ const std::unordered_map<std::string, std::string> kTypeToGroup = { | |||
| 256 | {af::ascir_op::Scalar::Type, af::ascir_op::Scalar::Type}, | 256 | {af::ascir_op::Scalar::Type, af::ascir_op::Scalar::Type}, |
| 257 | {af::ascir_op::Store::Type, af::ascir_op::Store::Type}}; | 257 | {af::ascir_op::Store::Type, af::ascir_op::Store::Type}}; |
| 258 | 258 | ||
| 259 | -bool ShouldDeleteCastNode(DataType peer_output_dtype, DataType output_dtype) { | 259 | +bool IsSharedRoundingBoundary(const std::vector<NodePtr> &peer_in_nodes, DataType peer_output_dtype, |
| 260 | - return IsFloatDataType(output_dtype) && IsFloatDataType(peer_output_dtype); | 260 | + DataType output_dtype) { |
| 261 | + if (!IsHighPrecisionDataType(peer_output_dtype) || !IsLowPrecisionDataType(output_dtype)) { | ||
| 262 | + return false; | ||
| 263 | + } | ||
| 264 | + bool has_store = false; | ||
| 265 | + bool has_compute = false; | ||
| 266 | + for (const auto &peer_in_node : peer_in_nodes) { | ||
| 267 | + if (peer_in_node->GetType() == af::ascir_op::Store::Type) { | ||
| 268 | + has_store = true; | ||
| 269 | + } else { | ||
| 270 | + has_compute = true; | ||
| 271 | + } | ||
| 272 | + } | ||
| 273 | + return has_store && has_compute; | ||
| 274 | +} | ||
| 275 | + | ||
| 276 | +bool ShouldDeleteCastNode(const std::vector<NodePtr> &peer_in_nodes, DataType peer_output_dtype, | ||
| 277 | + DataType output_dtype) { | ||
| 278 | + return IsFloatDataType(output_dtype) && IsFloatDataType(peer_output_dtype) && | ||
| 279 | + !IsSharedRoundingBoundary(peer_in_nodes, peer_output_dtype, output_dtype); | ||
| 261 | } | 280 | } |
| 262 | 281 | ||
| 263 | bool ShouldChangeDataType(const NodePtr &node, const std::vector<NodePtr> &peer_in_nodes, DataType peer_output_dtype, | 282 | bool ShouldChangeDataType(const NodePtr &node, const std::vector<NodePtr> &peer_in_nodes, DataType peer_output_dtype, |
| @@ -376,7 +395,7 @@ Status CastNodeProc(AscGraph &asc_graph, const NodePtr &node) { | |||
| 376 | GE_ASSERT_SUCCESS(GetOutputTensorDesc(node, output_tensor_desc)); | 395 | GE_ASSERT_SUCCESS(GetOutputTensorDesc(node, output_tensor_desc)); |
| 377 | const auto peer_output_dtype = peer_output_tensor_desc->GetDataType(); | 396 | const auto peer_output_dtype = peer_output_tensor_desc->GetDataType(); |
| 378 | const auto output_dtype = output_tensor_desc->GetDataType(); | 397 | const auto output_dtype = output_tensor_desc->GetDataType(); |
| 379 | - if (ShouldDeleteCastNode(peer_output_dtype, output_dtype)) { | 398 | + if (ShouldDeleteCastNode(peer_in_nodes, peer_output_dtype, output_dtype)) { |
| 380 | GE_ASSERT_SUCCESS(DelNode(asc_graph, node)); | 399 | GE_ASSERT_SUCCESS(DelNode(asc_graph, node)); |
| 381 | return af::SUCCESS; | 400 | return af::SUCCESS; |
| 382 | } | 401 | } |
Mautofuse/tests/st/backend_e2e/pgo_add_abs_inductor_test/pgo_add_abs_inductor_backend_generate.cpp+1-1
| @@ -113,7 +113,7 @@ TEST_F(TestBackendPgoAddAbsInductorE2e, PgoTilingKeyOverflowFallsBackWithoutRunn | |||
| 113 | EXPECT_EQ(result.tiling.find("AUTOFUSE_SPLIT_FILE_BEGIN: PgoRunner"), std::string::npos); | 113 | EXPECT_EQ(result.tiling.find("AUTOFUSE_SPLIT_FILE_BEGIN: PgoRunner"), std::string::npos); |
| 114 | const auto tiling_files = codegen.GenerateTiling(fused_schedule_result, {}, "/tmp", "10"); | 114 | const auto tiling_files = codegen.GenerateTiling(fused_schedule_result, {}, "/tmp", "10"); |
| 115 | EXPECT_EQ(tiling_files.at(codegen::kTilingDefAndConstIdentify).find("autofuse_tiling_func_pgo.h"), std::string::npos); | 115 | EXPECT_EQ(tiling_files.at(codegen::kTilingDefAndConstIdentify).find("autofuse_tiling_func_pgo.h"), std::string::npos); |
| 116 | - EXPECT_EQ(codegen.GeneratorPgo(fused_schedule_result, "/tmp"), "int main() { return 0; }\n"); | 116 | + EXPECT_EQ(codegen.GeneratorPgo(fused_schedule_result, "/tmp"), "int main() { return 1; }\n"); |
| 117 | 117 | ||
| 118 | schedule_groups.back().impl_graphs.pop_back(); | 118 | schedule_groups.back().impl_graphs.pop_back(); |
| 119 | fused_schedule_result.node_idx_to_scheduled_results[0].emplace_back(); | 119 | fused_schedule_result.node_idx_to_scheduled_results[0].emplace_back(); |
| @@ -174,6 +174,27 @@ TEST_F(TestImprovePrecisionST, Fp32ToFp16CastBeforeStore_RemovedAndAbsPromoted) | |||
| 174 | EXPECT_TRUE(CheckNodeOutputDtype(graph, "abs0", ge::DT_FLOAT)); | 174 | EXPECT_TRUE(CheckNodeOutputDtype(graph, "abs0", ge::DT_FLOAT)); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | +TEST_F(TestImprovePrecisionST, Fp32ToBf16CastSharedByStoreAndCompute_Preserved) { | ||
| 178 | + auto graph = AscGraphBuilder("st_fp32_to_bf16_shared_output") | ||
| 179 | + .Loops({Sym("s0")}) | ||
| 180 | + .Data("data0", 0, ge::DT_FLOAT) | ||
| 181 | + .Load("load0", "data0") | ||
| 182 | + .Add("add0", "load0", "load0") | ||
| 183 | + .Cast("cast_to_bf16", "add0", ge::DT_BF16) | ||
| 184 | + .Store("store_bf16", "cast_to_bf16") | ||
| 185 | + .Output("output_bf16", "store_bf16", 0, ge::DT_BF16) | ||
| 186 | + .Cast("cast_to_fp32", "cast_to_bf16", ge::DT_FLOAT) | ||
| 187 | + .Mul("mul0", "cast_to_fp32", "cast_to_fp32") | ||
| 188 | + .Store("store_fp32", "mul0") | ||
| 189 | + .Output("output_fp32", "store_fp32", 1, ge::DT_FLOAT) | ||
| 190 | + .Build(); | ||
| 191 | + | ||
| 192 | + ASSERT_EQ(ImprovePrecisionForAscGraph(graph), af::SUCCESS); | ||
| 193 | + | ||
| 194 | + EXPECT_TRUE(CheckNodeOutputDtype(graph, "cast_to_bf16", ge::DT_BF16)); | ||
| 195 | + EXPECT_TRUE(CheckNodeOutputDtype(graph, "mul0", ge::DT_FLOAT)); | ||
| 196 | +} | ||
| 197 | + | ||
| 177 | TEST_F(TestImprovePrecisionST, ScalarFp16Promoted_DownstreamAllFp32) { | 198 | TEST_F(TestImprovePrecisionST, ScalarFp16Promoted_DownstreamAllFp32) { |
| 178 | auto graph = AscGraphBuilder("st_scalar_fp16_downstream") | 199 | auto graph = AscGraphBuilder("st_scalar_fp16_downstream") |
| 179 | .Loops({Sym("s0")}) | 200 | .Loops({Sym("s0")}) |
| @@ -1935,7 +1935,7 @@ TEST_F(TestCodegenTiling, PgoTilingKeyCountOverflowShouldFallbackTfAndPgoRunner) | |||
| 1935 | EXPECT_EQ(entry.find("#include \"autofuse_tiling_func_pgo.h\""), std::string::npos); | 1935 | EXPECT_EQ(entry.find("#include \"autofuse_tiling_func_pgo.h\""), std::string::npos); |
| 1936 | EXPECT_NE(entry.find("extern \"C\" int64_t FindBestTilingKey"), std::string::npos); | 1936 | EXPECT_NE(entry.find("extern \"C\" int64_t FindBestTilingKey"), std::string::npos); |
| 1937 | const auto pgo_source = GenerateForPgo(fused_schedule_result, "/tmp"); | 1937 | const auto pgo_source = GenerateForPgo(fused_schedule_result, "/tmp"); |
| 1938 | - EXPECT_NE(pgo_source.find("int main()"), std::string::npos); | 1938 | + EXPECT_EQ(pgo_source, "int main() { return 1; }\n"); |
| 1939 | EXPECT_EQ(pgo_source.find("PGOGetProfiling"), std::string::npos); | 1939 | EXPECT_EQ(pgo_source.find("PGOGetProfiling"), std::string::npos); |
| 1940 | EXPECT_TRUE(CompileCode(pgo_source, false)); | 1940 | EXPECT_TRUE(CompileCode(pgo_source, false)); |
| 1941 | } | 1941 | } |
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | + | ||
| 14 | 15 | ||
| 15 | 16 | ||
| 16 | 17 | ||
| @@ -23,6 +24,7 @@ | |||
| 23 | 24 | ||
| 24 | 25 | ||
| 25 | 26 | ||
| 27 | + | ||
| 26 | 28 | ||
| 27 | 29 | ||
| 28 | 30 | ||
| @@ -1930,6 +1932,22 @@ TEST_F(BufQueAllocatorUT, TestTensorInfoToStr) { | |||
| 1930 | ASSERT_FALSE(res.empty()); | 1932 | ASSERT_FALSE(res.empty()); |
| 1931 | } | 1933 | } |
| 1932 | 1934 | ||
| 1935 | +TEST_F(BufQueAllocatorUT, tmp_buffer_allocation_follows_lifetime_instead_of_pointer_order) { | ||
| 1936 | + std::array<af::TmpBuffer, 2> tmp_buffers; | ||
| 1937 | + TmpBuffInfoMap tmp_buffer_infos; | ||
| 1938 | + tmp_buffer_infos[&tmp_buffers[0]].life_start = 2; | ||
| 1939 | + tmp_buffer_infos[&tmp_buffers[0]].life_end = std::numeric_limits<int64_t>::max(); | ||
| 1940 | + tmp_buffer_infos[&tmp_buffers[1]].life_start = 1; | ||
| 1941 | + tmp_buffer_infos[&tmp_buffers[1]].life_end = std::numeric_limits<int64_t>::max(); | ||
| 1942 | + TensorInfoMap tensor_infos; | ||
| 1943 | + | ||
| 1944 | + MemReuseManager manager(tensor_infos, tmp_buffer_infos); | ||
| 1945 | + manager.AllocMemBlocks(); | ||
| 1946 | + | ||
| 1947 | + EXPECT_EQ(tmp_buffers[1].id, 0); | ||
| 1948 | + EXPECT_EQ(tmp_buffers[0].id, 1); | ||
| 1949 | +} | ||
| 1950 | + | ||
| 1933 | TEST_F(BufQueAllocatorUT, test_tmp_buff_reuse) { | 1951 | TEST_F(BufQueAllocatorUT, test_tmp_buff_reuse) { |
| 1934 | af::AscGraph graph("LoadAbsStore"); | 1952 | af::AscGraph graph("LoadAbsStore"); |
| 1935 | auto ONE = af::Symbol(1); | 1953 | auto ONE = af::Symbol(1); |
| @@ -123,6 +123,18 @@ def test_device_compile_does_not_add_host_default_abi(compile_adapter_module): | |||
| 123 | temp_dir_ctx.cleanup() | 123 | temp_dir_ctx.cleanup() |
| 124 | 124 | ||
| 125 | 125 | ||
| 126 | +def test_pgo_get_top_result_missing_file_returns_failure( | ||
| 127 | + compile_adapter_module, tmpdir | ||
| 128 | +): | ||
| 129 | + missing_search = str(tmpdir.join("missing_search.txt")) | ||
| 130 | + | ||
| 131 | + assert compile_adapter_module.pgo_get_top_result(missing_search) == ( | ||
| 132 | + None, | ||
| 133 | + None, | ||
| 134 | + None, | ||
| 135 | + ) | ||
| 136 | + | ||
| 137 | + | ||
| 126 | def test_jit_compile_records_atrace_and_reports(compile_adapter_module, tmpdir, capsys): | 138 | def test_jit_compile_records_atrace_and_reports(compile_adapter_module, tmpdir, capsys): |
| 127 | output_file = tmpdir.join("jit.so") | 139 | output_file = tmpdir.join("jit.so") |
| 128 | argv = [f"--output_file={output_file}", f"--output_path={tmpdir}"] | 140 | argv = [f"--output_file={output_file}", f"--output_path={tmpdir}"] |
| @@ -9,6 +9,7 @@ | |||
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | + | ||
| 12 | 13 | ||
| 13 | 14 | ||
| 14 | 15 | ||
| @@ -80,6 +81,87 @@ void SetupGraphAxes(af::AscGraph &graph, const std::vector<af::Symbol> &loops) { | |||
| 80 | } | 81 | } |
| 81 | } | 82 | } |
| 82 | } | 83 | } |
| 84 | + | ||
| 85 | +af::AscGraph BuildParallelVfGraph(bool reverse_branch_order) { | ||
| 86 | + af::testing::AscGraphBuilder builder("parallel_vf"); | ||
| 87 | + builder.Loops({16}).Data("data", 0).Load("load", "data"); | ||
| 88 | + if (reverse_branch_order) { | ||
| 89 | + builder.Abs("branch_z", "load").Abs("branch_a", "load"); | ||
| 90 | + } else { | ||
| 91 | + builder.Abs("branch_a", "load").Abs("branch_z", "load"); | ||
| 92 | + } | ||
| 93 | + builder.Add("sum", "branch_a", "branch_z") | ||
| 94 | + .Store("store_branch_a", "branch_a") | ||
| 95 | + .Output("output_branch_a", "store_branch_a", 0) | ||
| 96 | + .Store("store_branch_z", "branch_z") | ||
| 97 | + .Output("output_branch_z", "store_branch_z", 1) | ||
| 98 | + .Store("store_sum", "sum") | ||
| 99 | + .Output("output_sum", "store_sum", 2); | ||
| 100 | + auto graph = builder.Build(); | ||
| 101 | + optimize::AscGraphInfoComplete::CompleteApiInfo(graph); | ||
| 102 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 103 | + if (ScheduleUtils::IsBuffer(node)) { | ||
| 104 | + continue; | ||
| 105 | + } | ||
| 106 | + for (auto &output : node->outputs()) { | ||
| 107 | + output->attr.vectorized_axis = output->attr.axis; | ||
| 108 | + output->attr.vectorized_strides = output->attr.strides; | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + return graph; | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +std::vector<std::string> GetVfOutputConsumers(af::AscGraph &graph) { | ||
| 115 | + EXPECT_EQ(AlignmentHandler::AlignVectorizedStrides(graph), af::SUCCESS); | ||
| 116 | + VectorFuncPartitioner partitioner(graph); | ||
| 117 | + EXPECT_EQ(partitioner.Partition(), af::SUCCESS); | ||
| 118 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 119 | + if (!af::ops::IsOps<af::ascir_op::VectorFunc>(node)) { | ||
| 120 | + continue; | ||
| 121 | + } | ||
| 122 | + std::vector<std::string> consumers; | ||
| 123 | + for (const auto &output : node->GetAllOutDataAnchors()) { | ||
| 124 | + const auto peers = output->GetPeerInDataAnchors(); | ||
| 125 | + EXPECT_EQ(peers.size(), 1UL); | ||
| 126 | + consumers.push_back(peers.empty() ? "" : (*peers.begin())->GetOwnerNodeBarePtr()->GetName()); | ||
| 127 | + } | ||
| 128 | + return consumers; | ||
| 129 | + } | ||
| 130 | + return {}; | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +std::vector<std::string> GetVfSubgraphTensorOrder(const af::AscGraph &graph) { | ||
| 134 | + std::vector<af::AscGraph> subgraphs; | ||
| 135 | + EXPECT_EQ(graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 136 | + EXPECT_EQ(subgraphs.size(), 1UL); | ||
| 137 | + std::vector<std::string> tensor_order; | ||
| 138 | + if (subgraphs.empty()) { | ||
| 139 | + return tensor_order; | ||
| 140 | + } | ||
| 141 | + for (const auto &node : subgraphs[0].GetAllNodes()) { | ||
| 142 | + for (const auto &output : node->outputs()) { | ||
| 143 | + tensor_order.push_back(node->GetName() + ":" + std::to_string(node->GetOpDescBarePtr()->GetId()) + ":" + | ||
| 144 | + std::to_string(output->attr.mem.tensor_id)); | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + return tensor_order; | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +std::vector<std::string> GetGraphNodeOrder(const af::AscGraph &graph) { | ||
| 151 | + std::vector<std::string> node_order; | ||
| 152 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 153 | + node_order.push_back(node->GetName() + ":" + std::to_string(node->GetOpDescBarePtr()->GetId())); | ||
| 154 | + } | ||
| 155 | + return node_order; | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +std::vector<std::string> GetGraphNodeNames(const af::AscGraph &graph) { | ||
| 159 | + std::vector<std::string> node_names; | ||
| 160 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 161 | + node_names.push_back(node->GetName()); | ||
| 162 | + } | ||
| 163 | + return node_names; | ||
| 164 | +} | ||
| 83 | } // namespace | 165 | } // namespace |
| 84 | 166 | ||
| 85 | class VfPartition : public testing::Test { | 167 | class VfPartition : public testing::Test { |
| @@ -142,7 +224,7 @@ TEST_F(VfPartition, brc_abs) { | |||
| 142 | *abs01.y.strides = {s2, Zero, One}; | 224 | *abs01.y.strides = {s2, Zero, One}; |
| 143 | *abs01.y.vectorized_axis = {z0.id, z1.id, z2.id}; | 225 | *abs01.y.vectorized_axis = {z0.id, z1.id, z2.id}; |
| 144 | 226 | ||
| 145 | - af::ascir_op::Abs abs02("abs01"); | 227 | + af::ascir_op::Abs abs02("abs02"); |
| 146 | abs02.x = abs01.y; | 228 | abs02.x = abs01.y; |
| 147 | abs02.attr.api.compute_type = af::ComputeType::kComputeElewise; | 229 | abs02.attr.api.compute_type = af::ComputeType::kComputeElewise; |
| 148 | abs02.attr.sched.axis = {z0.id, z1.id, z2.id}; | 230 | abs02.attr.sched.axis = {z0.id, z1.id, z2.id}; |
| @@ -1821,4 +1903,60 @@ TEST_F(VfPartition, topological_sort_for_vf_graph_keeps_load_before_consumer) { | |||
| 1821 | ASSERT_NE(truediv_iter, node_names.end()); | 1903 | ASSERT_NE(truediv_iter, node_names.end()); |
| 1822 | EXPECT_LT(std::distance(node_names.begin(), load1_iter), std::distance(node_names.begin(), truediv_iter)); | 1904 | EXPECT_LT(std::distance(node_names.begin(), load1_iter), std::distance(node_names.begin(), truediv_iter)); |
| 1823 | } | 1905 | } |
| 1906 | + | ||
| 1907 | +TEST_F(VfPartition, topological_sort_for_vf_graph_preserves_control_dependency) { | ||
| 1908 | + af::AscGraph graph("vf_sort_control_dependency"); | ||
| 1909 | + af::ascir_op::Data control_src("z_src", graph); | ||
| 1910 | + af::ascir_op::Data control_dst("a_dst", graph); | ||
| 1911 | + auto src_node = graph.FindNode("z_src"); | ||
| 1912 | + auto dst_node = graph.FindNode("a_dst"); | ||
| 1913 | + ASSERT_NE(src_node, nullptr); | ||
| 1914 | + ASSERT_NE(dst_node, nullptr); | ||
| 1915 | + ASSERT_EQ(af::GraphUtils::AddEdge(src_node->GetOutControlAnchor(), dst_node->GetInControlAnchor()), af::SUCCESS); | ||
| 1916 | + | ||
| 1917 | + VectorFuncPartitioner partitioner(graph); | ||
| 1918 | + ASSERT_EQ(partitioner.TopologicalSortingForVfGraph(graph), af::SUCCESS); | ||
| 1919 | + | ||
| 1920 | + const auto node_names = GetGraphNodeNames(graph); | ||
| 1921 | + ASSERT_EQ(node_names.size(), 2UL); | ||
| 1922 | + EXPECT_EQ(node_names[0], "z_src"); | ||
| 1923 | + EXPECT_EQ(node_names[1], "a_dst"); | ||
| 1924 | +} | ||
| 1925 | + | ||
| 1926 | +TEST_F(VfPartition, partition_rejects_duplicate_node_names_before_graph_mutation) { | ||
| 1927 | + af::AscGraph graph("vf_duplicate_node_names"); | ||
| 1928 | + af::ascir_op::Data first("duplicate", graph); | ||
| 1929 | + af::ascir_op::Data second("duplicate", graph); | ||
| 1930 | + ASSERT_EQ(GetGraphNodeNames(graph).size(), 2UL); | ||
| 1931 | + | ||
| 1932 | + VectorFuncPartitioner partitioner(graph); | ||
| 1933 | + EXPECT_NE(partitioner.Partition(), af::SUCCESS); | ||
| 1934 | + EXPECT_EQ(GetGraphNodeNames(graph).size(), 2UL); | ||
| 1935 | + std::vector<af::AscGraph> subgraphs; | ||
| 1936 | + EXPECT_EQ(graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 1937 | + EXPECT_TRUE(subgraphs.empty()); | ||
| 1938 | +} | ||
| 1939 | + | ||
| 1940 | +TEST_F(VfPartition, vf_output_order_is_independent_of_parallel_node_insertion_order) { | ||
| 1941 | + auto graph = BuildParallelVfGraph(false); | ||
| 1942 | + auto reversed_graph = BuildParallelVfGraph(true); | ||
| 1943 | + const std::vector<std::string> expected = {"store_branch_a", "store_branch_z", "store_sum"}; | ||
| 1944 | + const auto consumers = GetVfOutputConsumers(graph); | ||
| 1945 | + const auto reversed_consumers = GetVfOutputConsumers(reversed_graph); | ||
| 1946 | + | ||
| 1947 | + ASSERT_EQ(consumers.size(), expected.size()); | ||
| 1948 | + ASSERT_EQ(reversed_consumers.size(), expected.size()); | ||
| 1949 | + EXPECT_EQ(consumers, expected); | ||
| 1950 | + EXPECT_EQ(reversed_consumers, expected); | ||
| 1951 | +} | ||
| 1952 | + | ||
| 1953 | +TEST_F(VfPartition, vf_tensor_order_is_independent_of_parallel_node_insertion_order) { | ||
| 1954 | + auto graph = BuildParallelVfGraph(false); | ||
| 1955 | + auto reversed_graph = BuildParallelVfGraph(true); | ||
| 1956 | + GetVfOutputConsumers(graph); | ||
| 1957 | + GetVfOutputConsumers(reversed_graph); | ||
| 1958 | + | ||
| 1959 | + EXPECT_EQ(GetGraphNodeOrder(graph), GetGraphNodeOrder(reversed_graph)); | ||
| 1960 | + EXPECT_EQ(GetVfSubgraphTensorOrder(graph), GetVfSubgraphTensorOrder(reversed_graph)); | ||
| 1961 | +} | ||
| 1824 | } // namespace optimize | 1962 | } // namespace optimize |
| @@ -30,25 +30,41 @@ constexpr int32_t kMaxBitWidthGap = 2; | |||
| 30 | constexpr int64_t kOutLoopAxisId = -1L; | 30 | constexpr int64_t kOutLoopAxisId = -1L; |
| 31 | constexpr size_t kMinVfNodesNum = 2UL; | 31 | constexpr size_t kMinVfNodesNum = 2UL; |
| 32 | 32 | ||
| 33 | -std::unordered_map<af::Node *, size_t> BuildDependencyAwareRanks( | 33 | +af::Status ValidateUniqueNodeNames(const af::AscGraph &graph) { |
| 34 | - const af::AscGraph &graph, const std::unordered_set<af::Node *> &outer_loop_sequences) { | 34 | + std::unordered_set<std::string> node_names; |
| 35 | - std::vector<af::NodePtr> nodes; | ||
| 36 | - std::unordered_map<af::Node *, size_t> indegrees; | ||
| 37 | - std::unordered_map<af::Node *, std::vector<af::NodePtr>> out_nodes; | ||
| 38 | for (const auto &node : graph.GetAllNodes()) { | 35 | for (const auto &node : graph.GetAllNodes()) { |
| 39 | - nodes.push_back(node); | 36 | + GE_ASSERT_TRUE(node_names.emplace(node->GetName()).second, "VF input graph [%s] contains duplicate node name [%s].", |
| 40 | - indegrees[node.get()] = 0UL; | 37 | + graph.GetName().c_str(), node->GetName().c_str()); |
| 41 | } | 38 | } |
| 39 | + return af::SUCCESS; | ||
| 40 | +} | ||
| 42 | 41 | ||
| 42 | +using NodeIndegrees = std::unordered_map<af::Node *, size_t>; | ||
| 43 | +using NodeSuccessors = std::unordered_map<af::Node *, std::vector<af::NodePtr>>; | ||
| 44 | + | ||
| 45 | +void AddGraphDependencies(const std::vector<af::NodePtr> &nodes, NodeIndegrees &indegrees, NodeSuccessors &out_nodes) { | ||
| 43 | for (const auto &node : nodes) { | 46 | for (const auto &node : nodes) { |
| 44 | - for (const auto &out_node : node->GetOutDataNodes()) { | 47 | + std::unordered_set<af::Node *> unique_out_nodes; |
| 45 | - if (indegrees.find(out_node.get()) == indegrees.end()) { | 48 | + for (const auto &out_node : node->GetOutAllNodes()) { |
| 49 | + if ((indegrees.find(out_node.get()) == indegrees.end()) || !unique_out_nodes.emplace(out_node.get()).second) { | ||
| 46 | continue; | 50 | continue; |
| 47 | } | 51 | } |
| 48 | out_nodes[node.get()].push_back(out_node); | 52 | out_nodes[node.get()].push_back(out_node); |
| 49 | ++indegrees[out_node.get()]; | 53 | ++indegrees[out_node.get()]; |
| 50 | } | 54 | } |
| 51 | } | 55 | } |
| 56 | +} | ||
| 57 | + | ||
| 58 | +std::unordered_map<af::Node *, size_t> BuildDependencyAwareRanks( | ||
| 59 | + const af::AscGraph &graph, const std::unordered_set<af::Node *> &outer_loop_sequences) { | ||
| 60 | + std::vector<af::NodePtr> nodes; | ||
| 61 | + NodeIndegrees indegrees; | ||
| 62 | + NodeSuccessors out_nodes; | ||
| 63 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 64 | + nodes.push_back(node); | ||
| 65 | + indegrees[node.get()] = 0UL; | ||
| 66 | + } | ||
| 67 | + AddGraphDependencies(nodes, indegrees, out_nodes); | ||
| 52 | 68 | ||
| 53 | const auto has_higher_priority = [&outer_loop_sequences](const af::NodePtr &node1, const af::NodePtr &node2) -> bool { | 69 | const auto has_higher_priority = [&outer_loop_sequences](const af::NodePtr &node1, const af::NodePtr &node2) -> bool { |
| 54 | bool is_node1_in_outer_seq = outer_loop_sequences.find(node1.get()) != outer_loop_sequences.end(); | 70 | bool is_node1_in_outer_seq = outer_loop_sequences.find(node1.get()) != outer_loop_sequences.end(); |
| @@ -56,7 +72,8 @@ std::unordered_map<af::Node *, size_t> BuildDependencyAwareRanks( | |||
| 56 | if (is_node1_in_outer_seq != is_node2_in_outer_seq) { | 72 | if (is_node1_in_outer_seq != is_node2_in_outer_seq) { |
| 57 | return is_node1_in_outer_seq; | 73 | return is_node1_in_outer_seq; |
| 58 | } | 74 | } |
| 59 | - return node1->GetOpDescBarePtr()->GetId() < node2->GetOpDescBarePtr()->GetId(); | 75 | + return std::make_pair(node1->GetName(), node1->GetOpDescBarePtr()->GetId()) < |
| 76 | + std::make_pair(node2->GetName(), node2->GetOpDescBarePtr()->GetId()); | ||
| 60 | }; | 77 | }; |
| 61 | 78 | ||
| 62 | std::vector<af::NodePtr> ready_nodes; | 79 | std::vector<af::NodePtr> ready_nodes; |
| @@ -353,6 +370,26 @@ void AddAnchorToOrderMap( | |||
| 353 | } | 370 | } |
| 354 | } | 371 | } |
| 355 | 372 | ||
| 373 | +using BoundaryAnchorEntry = std::pair<af::OutDataAnchorPtr, std::vector<af::InDataAnchorPtr>>; | ||
| 374 | + | ||
| 375 | +void SortBoundaryAnchors(std::vector<BoundaryAnchorEntry> &anchors) { | ||
| 376 | + const auto out_anchor_less = [](const BoundaryAnchorEntry &lhs, const BoundaryAnchorEntry &rhs) { | ||
| 377 | + const auto *lhs_node = lhs.first->GetOwnerNodeBarePtr(); | ||
| 378 | + const auto *rhs_node = rhs.first->GetOwnerNodeBarePtr(); | ||
| 379 | + return std::make_pair(lhs_node->GetName(), lhs.first->GetIdx()) < | ||
| 380 | + std::make_pair(rhs_node->GetName(), rhs.first->GetIdx()); | ||
| 381 | + }; | ||
| 382 | + const auto in_anchor_less = [](const af::InDataAnchorPtr &lhs, const af::InDataAnchorPtr &rhs) { | ||
| 383 | + const auto *lhs_node = lhs->GetOwnerNodeBarePtr(); | ||
| 384 | + const auto *rhs_node = rhs->GetOwnerNodeBarePtr(); | ||
| 385 | + return std::make_pair(lhs_node->GetName(), lhs->GetIdx()) < std::make_pair(rhs_node->GetName(), rhs->GetIdx()); | ||
| 386 | + }; | ||
| 387 | + std::sort(anchors.begin(), anchors.end(), out_anchor_less); | ||
| 388 | + for (auto &entry : anchors) { | ||
| 389 | + std::sort(entry.second.begin(), entry.second.end(), in_anchor_less); | ||
| 390 | + } | ||
| 391 | +} | ||
| 392 | + | ||
| 356 | bool NeedRemovePad(const af::AscNodePtr &node) { | 393 | bool NeedRemovePad(const af::AscNodePtr &node) { |
| 357 | // 如果是非scalar的Broadcast节点,直接插RemovePad,结束循环 | 394 | // 如果是非scalar的Broadcast节点,直接插RemovePad,结束循环 |
| 358 | if (optimize::ScheduleUtils::IsBroadcast(node) && !optimize::ScheduleUtils::IsScalarBroadcastNode(node)) { | 395 | if (optimize::ScheduleUtils::IsBroadcast(node) && !optimize::ScheduleUtils::IsScalarBroadcastNode(node)) { |
| @@ -423,6 +460,7 @@ const std::string kNamePrefixScalar = "Scalar_"; | |||
| 423 | const std::string kNamePrefixOutput = "Output_"; | 460 | const std::string kNamePrefixOutput = "Output_"; |
| 424 | 461 | ||
| 425 | af::Status VectorFuncPartitioner::Partition() { | 462 | af::Status VectorFuncPartitioner::Partition() { |
| 463 | + GE_ASSERT_SUCCESS(ValidateUniqueNodeNames(impl_graph_)); | ||
| 426 | ascir::utils::DumpGraph(impl_graph_, "BeforePartition"); | 464 | ascir::utils::DumpGraph(impl_graph_, "BeforePartition"); |
| 427 | GE_ASSERT_SUCCESS(ScheduleUtils::TopologicalSorting(impl_graph_), "Failed to do topological sorting for graph[%s].", | 465 | GE_ASSERT_SUCCESS(ScheduleUtils::TopologicalSorting(impl_graph_), "Failed to do topological sorting for graph[%s].", |
| 428 | impl_graph_.GetName().c_str()); | 466 | impl_graph_.GetName().c_str()); |
| @@ -960,6 +998,8 @@ af::Status VectorFuncPartitioner::BuildSubgraph(const ClusterPtr &cluster, af::A | |||
| 960 | GE_ASSERT_SUCCESS(AddInputDataAnchors(node, load_to_peer_in_anchors)); | 998 | GE_ASSERT_SUCCESS(AddInputDataAnchors(node, load_to_peer_in_anchors)); |
| 961 | GE_ASSERT_SUCCESS(AddOutputDataAnchors(node, store_to_peed_in_anchors)); | 999 | GE_ASSERT_SUCCESS(AddOutputDataAnchors(node, store_to_peed_in_anchors)); |
| 962 | } | 1000 | } |
| 1001 | + SortBoundaryAnchors(load_to_peer_in_anchors); | ||
| 1002 | + SortBoundaryAnchors(store_to_peed_in_anchors); | ||
| 963 | 1003 | ||
| 964 | vf_op.InstanceOutputy(store_to_peed_in_anchors.size()); | 1004 | vf_op.InstanceOutputy(store_to_peed_in_anchors.size()); |
| 965 | std::vector<af::AscOpOutput> outputs; | 1005 | std::vector<af::AscOpOutput> outputs; |
| @@ -1249,7 +1289,8 @@ af::Status VectorFuncPartitioner::TopologicalSortingForVfGraph(af::AscGraph &gra | |||
| 1249 | if (rank1 != ranks.end() && rank2 != ranks.end()) { | 1289 | if (rank1 != ranks.end() && rank2 != ranks.end()) { |
| 1250 | return rank1->second < rank2->second; | 1290 | return rank1->second < rank2->second; |
| 1251 | } | 1291 | } |
| 1252 | - return node1->GetOpDescBarePtr()->GetId() < node2->GetOpDescBarePtr()->GetId(); | 1292 | + return std::make_pair(node1->GetName(), node1->GetOpDescBarePtr()->GetId()) < |
| 1293 | + std::make_pair(node2->GetName(), node2->GetOpDescBarePtr()->GetId()); | ||
| 1253 | }; | 1294 | }; |
| 1254 | 1295 | ||
| 1255 | auto compute_graph = af::AscGraphUtils::GetComputeGraph(graph); | 1296 | auto compute_graph = af::AscGraphUtils::GetComputeGraph(graph); |
| @@ -22,6 +22,7 @@ namespace optimize { | |||
| 22 | class VectorFuncPartitioner { | 22 | class VectorFuncPartitioner { |
| 23 | public: | 23 | public: |
| 24 | explicit VectorFuncPartitioner(af::AscGraph &impl_graph) : impl_graph_(impl_graph) {}; | 24 | explicit VectorFuncPartitioner(af::AscGraph &impl_graph) : impl_graph_(impl_graph) {}; |
| 25 | + // VF input graph node names must be unique to keep node and boundary-anchor ordering deterministic. | ||
| 25 | af::Status Partition(); | 26 | af::Status Partition(); |
| 26 | 27 | ||
| 27 | private: | 28 | private: |