已合并
feat: Inductor支持PGO(复用TF流程) #1541
zhang_shengjie创建于 7月28日
feat: Inductor支持PGO(复用TF流程) #1541
已合并
共 39 个文件变更+5288-516
| @@ -47,6 +47,14 @@ void PgoEnvConfigInit(TilingCodeGenConfig &generator_config) { | |||
| 47 | } | 47 | } |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | +void OverridePgoConfigByOptions(TilingCodeGenConfig &generator_config, | ||
| 51 | + const std::map<std::string, std::string> &options) { | ||
| 52 | + const auto iter = options.find(kInternalEnableAutofusePgo); | ||
| 53 | + if (iter != options.cend()) { | ||
| 54 | + generator_config.enable_autofuse_pgo = (iter->second == "true"); | ||
| 55 | + } | ||
| 56 | +} | ||
| 57 | + | ||
| 50 | af::Status InitializeConfigByEnvOrIni(TilingCodeGenConfig &generator_config) { | 58 | af::Status InitializeConfigByEnvOrIni(TilingCodeGenConfig &generator_config) { |
| 51 | // ATT的配置初始化,当前前端不支持传入配置文件的目录,所以这里直接使用默认的配置文件路径 | 59 | // ATT的配置初始化,当前前端不支持传入配置文件的目录,所以这里直接使用默认的配置文件路径 |
| 52 | const auto res = AutoFuseConfig::MutableAttStrategyConfig().Init(); | 60 | const auto res = AutoFuseConfig::MutableAttStrategyConfig().Init(); |
| @@ -188,6 +196,7 @@ bool GenTilingImplAutoFuseV3(const std::string &op_name, const ascir::FusedSched | |||
| 188 | generator_config.is_inductor_scene = is_inductor_scene; | 196 | generator_config.is_inductor_scene = is_inductor_scene; |
| 189 | generator_config.is_cube = ascgen_utils::IsCubeFusedScheduled(fused_schedule_result); | 197 | generator_config.is_cube = ascgen_utils::IsCubeFusedScheduled(fused_schedule_result); |
| 190 | InitializeConfigByEnvOrIni(generator_config); | 198 | InitializeConfigByEnvOrIni(generator_config); |
| 199 | + OverridePgoConfigByOptions(generator_config, options); | ||
| 191 | TilingCodeGenerator generator; | 200 | TilingCodeGenerator generator; |
| 192 | FusedParsedScheduleResult fused_parsed_schedule_result; | 201 | FusedParsedScheduleResult fused_parsed_schedule_result; |
| 193 | GE_ASSERT_SUCCESS(GetModelInfoMap(fused_schedule_result, options, fused_parsed_schedule_result)); | 202 | GE_ASSERT_SUCCESS(GetModelInfoMap(fused_schedule_result, options, fused_parsed_schedule_result)); |
| @@ -16,6 +16,8 @@ | |||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | namespace att { | 18 | namespace att { |
| 19 | +inline constexpr char kInternalEnableAutofusePgo[] = "_internal_enable_autofuse_pgo"; | ||
| 20 | + | ||
| 19 | extern "C" { | 21 | extern "C" { |
| 20 | /** | 22 | /** |
| 21 | * @brief 生成Tiling函数 | 23 | * @brief 生成Tiling函数 |
| @@ -864,7 +864,7 @@ std::string AxesReorderSolverGen::GenPGOSolverClassImpl() { | |||
| 864 | codes += " bool CalUsedCoreNum(double &used_core_num) override;\n"; | 864 | codes += " bool CalUsedCoreNum(double &used_core_num) override;\n"; |
| 865 | codes += " bool CalRealUsedCoreNum(int64_t &used_corenum) override;\n"; | 865 | codes += " bool CalRealUsedCoreNum(int64_t &used_corenum) override;\n"; |
| 866 | codes += " bool SatisfyThresholdUBSize() override {return false;};\n"; | 866 | codes += " bool SatisfyThresholdUBSize() override {return false;};\n"; |
| 867 | - codes += " bool SatisfyUBSizeCacheLine(uint32_t idx) override {return false;};\n"; | 867 | + codes += " bool SatisfyUBSizeCacheLine(uint32_t) override {return false;};\n"; |
| 868 | codes += " double GetPerf() override {return 0;};\n"; | 868 | codes += " double GetPerf() override {return 0;};\n"; |
| 869 | codes += "};\n"; | 869 | codes += "};\n"; |
| 870 | codes += "\n"; | 870 | codes += "\n"; |
| @@ -463,8 +463,10 @@ std::string WrapAtomicHeaderBody(autofuse::GeneratedHeaderId header_id, const st | |||
| 463 | " &tiling_data);\nnamespace optiling {\nstruct PgoTensorArgs;\nstruct SearchConfig;\n" + body + | 463 | " &tiling_data);\nnamespace optiling {\nstruct PgoTensorArgs;\nstruct SearchConfig;\n" + body + |
| 464 | "} // namespace optiling\n"; | 464 | "} // namespace optiling\n"; |
| 465 | } | 465 | } |
| 466 | - return "struct AutofuseTilingDataPerf;\nnamespace optiling {\nstruct " + tiling_data_type_name + | 466 | + const std::string global_tiling_decl = is_autofuse ? "struct AutofuseTilingData;\n" : ""; |
| 467 | - ";\nstruct PgoTensorArgs;\nstruct SearchConfig;\n" + body + "} // namespace optiling\n"; | 467 | + return global_tiling_decl + "struct AutofuseTilingDataPerf;\nnamespace optiling {\nstruct " + |
| 468 | + tiling_data_type_name + ";\nstruct PgoTensorArgs;\nstruct SearchConfig;\n" + body + | ||
| 469 | + "} // namespace optiling\n"; | ||
| 468 | } | 470 | } |
| 469 | std::string prefix; | 471 | std::string prefix; |
| 470 | if (header_id == autofuse::GeneratedHeaderId::kSolver) { | 472 | if (header_id == autofuse::GeneratedHeaderId::kSolver) { |
| @@ -1031,6 +1033,8 @@ void TilingCodeGenImpl::GenPgoConfigDefs(ge::CodePrinter &pgo_header) { | |||
| 1031 | pgo_header.AddLine(" ProfilingCallback single_callback;"); | 1033 | pgo_header.AddLine(" ProfilingCallback single_callback;"); |
| 1032 | pgo_header.AddLine(" ProfilingBatchCallback batch_callback;"); | 1034 | pgo_header.AddLine(" ProfilingBatchCallback batch_callback;"); |
| 1033 | pgo_header.AddLine(" PgoTensorArgs *tensor_args = nullptr;"); | 1035 | pgo_header.AddLine(" PgoTensorArgs *tensor_args = nullptr;"); |
| 1036 | + pgo_header.AddLine(" std::vector<AutofuseTilingDataPerf> *measured_candidates = nullptr;"); | ||
| 1037 | + pgo_header.AddLine(" void *stream = nullptr;"); | ||
| 1034 | pgo_header.AddLine(" int32_t pgo_algorithm = 1; // 0 for pruning, 1 for core num"); | 1038 | pgo_header.AddLine(" int32_t pgo_algorithm = 1; // 0 for pruning, 1 for core num"); |
| 1035 | pgo_header.AddLine(" bool need_change_solver_run = false;"); | 1039 | pgo_header.AddLine(" bool need_change_solver_run = false;"); |
| 1036 | pgo_header.AddLine(" size_t pgo_threshold_index = 0;"); | 1040 | pgo_header.AddLine(" size_t pgo_threshold_index = 0;"); |
| @@ -2930,8 +2934,10 @@ void TilingCodeGenImpl::GenPGOSearchTilingKeyUniqGroupBatch() { | |||
| 2930 | tiling_func_.AddLine(" workspaceSize += 16 * 1024 * 1024;"); | 2934 | tiling_func_.AddLine(" workspaceSize += 16 * 1024 * 1024;"); |
| 2931 | tiling_func_.AddLine(" if (PgoConfig::Instance().batch_callback != nullptr) {"); | 2935 | tiling_func_.AddLine(" if (PgoConfig::Instance().batch_callback != nullptr) {"); |
| 2932 | tiling_func_.AddLine( | 2936 | tiling_func_.AddLine( |
| 2933 | - " PgoConfig::Instance().batch_callback(PgoConfig::Instance().tensor_args, stream, " | 2937 | + " if (PgoConfig::Instance().batch_callback(PgoConfig::Instance().tensor_args, stream, " |
| 2934 | - "workspaceSize, &tiling_data_list);"); | 2938 | + "workspaceSize, &tiling_data_list) != 0) {"); |
| 2939 | + tiling_func_.AddLine(" return false;"); | ||
| 2940 | + tiling_func_.AddLine(" }"); | ||
| 2935 | tiling_func_.AddLine(" }"); | 2941 | tiling_func_.AddLine(" }"); |
| 2936 | tiling_func_.AddLine(" for (const auto &tiling_data_perf : tiling_data_list) {"); | 2942 | tiling_func_.AddLine(" for (const auto &tiling_data_perf : tiling_data_list) {"); |
| 2937 | tiling_func_.AddLine(" if (best_perf > tiling_data_perf.best_perf) {"); | 2943 | tiling_func_.AddLine(" if (best_perf > tiling_data_perf.best_perf) {"); |
| @@ -3967,8 +3973,10 @@ af::Status TilingCodeGenImpl::GenPGOGetScheduleResultPerGroup( | |||
| 3967 | tiling_func_.AddLine(" workspaceSize += 16 * 1024 * 1024;"); | 3973 | tiling_func_.AddLine(" workspaceSize += 16 * 1024 * 1024;"); |
| 3968 | tiling_func_.AddLine(" if (PgoConfig::Instance().batch_callback) {"); | 3974 | tiling_func_.AddLine(" if (PgoConfig::Instance().batch_callback) {"); |
| 3969 | tiling_func_.AddLine( | 3975 | tiling_func_.AddLine( |
| 3970 | - " PgoConfig::Instance().batch_callback(PgoConfig::Instance().tensor_args, stream, " | 3976 | + " if (PgoConfig::Instance().batch_callback(PgoConfig::Instance().tensor_args, stream, " |
| 3971 | - "workspaceSize, &tiling_data_list_tmp);"); | 3977 | + "workspaceSize, &tiling_data_list_tmp) != 0) {"); |
| 3978 | + tiling_func_.AddLine(" return false;"); | ||
| 3979 | + tiling_func_.AddLine(" }"); | ||
| 3972 | tiling_func_.AddLine(" }"); | 3980 | tiling_func_.AddLine(" }"); |
| 3973 | tiling_func_.AddLine(" for (size_t candidate_index = " + candidate_begin_name + | 3981 | tiling_func_.AddLine(" for (size_t candidate_index = " + candidate_begin_name + |
| 3974 | "; candidate_index < tiling_data_list_tmp.size(); ++candidate_index) {"); | 3982 | "; candidate_index < tiling_data_list_tmp.size(); ++candidate_index) {"); |
| @@ -5074,7 +5082,7 @@ af::Status TilingCodeGenImpl::GenPGOReuseGroupTilingWrapper() { | |||
| 5074 | // Gen PGOProfileReuseGroup: reuse group does not search, only profiles by copying from primary group | 5082 | // Gen PGOProfileReuseGroup: reuse group does not search, only profiles by copying from primary group |
| 5075 | std::string pgo_profile_sig = | 5083 | std::string pgo_profile_sig = |
| 5076 | std::string("bool PGOProfileReuseGroup(std::vector<AutofuseTilingDataPerf>& tiling_data_list, ") + | 5084 | std::string("bool PGOProfileReuseGroup(std::vector<AutofuseTilingDataPerf>& tiling_data_list, ") + |
| 5077 | - config_.tiling_data_type_name + "* output_tiling_data, void* stream, uint32_t workspaceSize, double& best_perf)"; | 5085 | + "AutofuseTilingData* output_tiling_data, void* stream, uint32_t workspaceSize, double& best_perf)"; |
| 5078 | AddAtomicHeaderLine(autofuse::GeneratedHeaderId::kApi, pgo_profile_sig + ";"); | 5086 | AddAtomicHeaderLine(autofuse::GeneratedHeaderId::kApi, pgo_profile_sig + ";"); |
| 5079 | tiling_func_.AddLine(pgo_profile_sig + " {"); | 5087 | tiling_func_.AddLine(pgo_profile_sig + " {"); |
| 5080 | tiling_func_.AddLine(" double cur_perf = DBL_MAX;"); | 5088 | tiling_func_.AddLine(" double cur_perf = DBL_MAX;"); |
| @@ -390,10 +390,32 @@ Status Codegen::Generate(const ascir::FusedScheduledResult &fused_schedule_resul | |||
| 390 | // inductor路径仍返回单个host_tiling字符串,通过注释marker保留拆分边界 | 390 | // inductor路径仍返回单个host_tiling字符串,通过注释marker保留拆分边界 |
| 391 | Status Codegen::GenerateForInductor(const ascir::FusedScheduledResult &fused_schedule_result, | 391 | Status Codegen::GenerateForInductor(const ascir::FusedScheduledResult &fused_schedule_result, |
| 392 | CodegenResult &result) const { | 392 | CodegenResult &result) const { |
| 393 | + const auto generate_tiling_without_pgo = [&]() { | ||
| 394 | + if (!tiling_lib_.IsInductorPgoEnabled() || ascgen_utils::IsCubeFusedScheduled(fused_schedule_result) || | ||
| 395 | + !ascgen_utils::IsStaticSchedResult(fused_schedule_result)) { | ||
| 396 | + return af::FAILED; | ||
| 397 | + } | ||
| 398 | + GELOGW("Inductor PGO codegen failed, fallback to non-PGO codegen for static non-CV kernel"); | ||
| 399 | + Codegen fallback(*this); | ||
| 400 | + fallback.tiling_lib_.DisableInductorPgo(); | ||
| 401 | + return fallback.GenerateForInductor(fused_schedule_result, result); | ||
| 402 | + }; | ||
| 393 | GE_CHK_STATUS_RET(GenerateKernel(fused_schedule_result, result.kernel, true), "Codegen generate kernel failed"); | 403 | GE_CHK_STATUS_RET(GenerateKernel(fused_schedule_result, result.kernel, true), "Codegen generate kernel failed"); |
| 394 | result.tiling_data = GenerateTilingData(fused_schedule_result, true); | 404 | result.tiling_data = GenerateTilingData(fused_schedule_result, true); |
| 395 | std::map<std::string, std::string> tiling_file_name_to_content; | 405 | std::map<std::string, std::string> tiling_file_name_to_content; |
| 396 | - GE_CHK_STATUS_RET(GenerateTilingForInductor(fused_schedule_result, tiling_file_name_to_content)); | 406 | + const auto tiling_ret = GenerateTilingForInductor(fused_schedule_result, tiling_file_name_to_content); |
| 407 | + if (tiling_ret != af::SUCCESS) { | ||
| 408 | + if (generate_tiling_without_pgo() == af::SUCCESS) { | ||
| 409 | + return af::SUCCESS; | ||
| 410 | + } | ||
| 411 | + GE_CHK_STATUS_RET(tiling_ret, "Codegen generate tiling failed"); | ||
| 412 | + } | ||
| 413 | + const auto pgo_runner = tiling_file_name_to_content.find(kPgoRunnerIdentify); | ||
| 414 | + if (pgo_runner != tiling_file_name_to_content.end()) { | ||
| 415 | + GE_CHK_BOOL_RET_STATUS(!pgo_runner->second.empty() && !result.kernel.empty(), af::FAILED, | ||
| 416 | + "Inductor PGO runner or device source is empty"); | ||
| 417 | + tiling_file_name_to_content[kPgoDeviceSourceIdentify] = result.kernel; | ||
| 418 | + } | ||
| 397 | GE_CHK_STATUS_RET(CombineTilingsWithSplitMarkers(tiling_file_name_to_content, result.tiling)); | 419 | GE_CHK_STATUS_RET(CombineTilingsWithSplitMarkers(tiling_file_name_to_content, result.tiling)); |
| 398 | return af::SUCCESS; | 420 | return af::SUCCESS; |
| 399 | } | 421 | } |
| @@ -16,11 +16,9 @@ | |||
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | - | ||
| 20 | 19 | ||
| 20 | + | ||
| 21 | 21 | ||
| 22 | - | ||
| 23 | - | ||
| 24 | 22 | ||
| 25 | 23 | ||
| 26 | 24 | ||
| @@ -33,8 +31,6 @@ | |||
| 33 | 31 | ||
| 34 | 32 | ||
| 35 | 33 | ||
| 36 | - | ||
| 37 | - | ||
| 38 | 34 | ||
| 39 | 35 | ||
| 40 | 36 | ||
| @@ -49,7 +45,6 @@ using namespace codegen; | |||
| 49 | using namespace af::ops; | 45 | using namespace af::ops; |
| 50 | using namespace ascgen_utils; | 46 | using namespace ascgen_utils; |
| 51 | namespace { | 47 | namespace { |
| 52 | - | ||
| 53 | bool CheckTilingHeadersValid(const std::map<std::string, std::string> &tiling_file_name_to_content) { | 48 | bool CheckTilingHeadersValid(const std::map<std::string, std::string> &tiling_file_name_to_content) { |
| 54 | for (const auto &pair : tiling_file_name_to_content) { | 49 | for (const auto &pair : tiling_file_name_to_content) { |
| 55 | if (pair.second == INVALID_TILING) { | 50 | if (pair.second == INVALID_TILING) { |
| @@ -141,7 +136,7 @@ std::string RenderEntryTranslationUnit(const std::string &body, const EntryTrans | |||
| 141 | autofuse::RequireGeneratedHeader(code.dependencies, autofuse::GeneratedHeaderId::kPgo); | 136 | autofuse::RequireGeneratedHeader(code.dependencies, autofuse::GeneratedHeaderId::kPgo); |
| 142 | } | 137 | } |
| 143 | if (options.enable_pgo_runtime) { | 138 | if (options.enable_pgo_runtime) { |
| 144 | - RequireSystemHeaders(code.dependencies, {"fstream", "securec.h", "unordered_set"}); | 139 | + RequireSystemHeaders(code.dependencies, {"fstream", "securec.h", "unordered_set", "utility"}); |
| 145 | } | 140 | } |
| 146 | if (options.include_solver) { | 141 | if (options.include_solver) { |
| 147 | autofuse::RequireGeneratedHeader(code.dependencies, autofuse::GeneratedHeaderId::kSolver); | 142 | autofuse::RequireGeneratedHeader(code.dependencies, autofuse::GeneratedHeaderId::kSolver); |
| @@ -460,6 +455,10 @@ std::map<std::string, std::string> TilingLib::GenerateForInductor( | |||
| 460 | const ascir::FusedScheduledResult &fused_schedule_result) const { | 455 | const ascir::FusedScheduledResult &fused_schedule_result) const { |
| 461 | ascir::FusedScheduledResult elemwise_schedule_result = fused_schedule_result; | 456 | ascir::FusedScheduledResult elemwise_schedule_result = fused_schedule_result; |
| 462 | const bool is_cube_fused_scheduled = ascgen_utils::IsCubeFusedScheduled(fused_schedule_result); | 457 | const bool is_cube_fused_scheduled = ascgen_utils::IsCubeFusedScheduled(fused_schedule_result); |
| 458 | + if (enable_autofuse_pgo_ && !IsSupportedInductorPgoScene(fused_schedule_result)) { | ||
| 459 | + GELOGE(af::FAILED, "Inductor MSPTI PGO only supports static, non-CV kernels"); | ||
| 460 | + return {{kTilingDefAndConstIdentify, ascgen_utils::INVALID_TILING}}; | ||
| 461 | + } | ||
| 463 | if (is_cube_fused_scheduled) { | 462 | if (is_cube_fused_scheduled) { |
| 464 | GE_ASSERT_SUCCESS(ascgen_utils::ProcessCubeFusionResultDynamic(elemwise_schedule_result)); | 463 | GE_ASSERT_SUCCESS(ascgen_utils::ProcessCubeFusionResultDynamic(elemwise_schedule_result)); |
| 465 | } | 464 | } |
| @@ -473,23 +472,7 @@ std::map<std::string, std::string> TilingLib::GenerateForInductor( | |||
| 473 | ss << "#pragma GCC diagnostic pop\n"; | 472 | ss << "#pragma GCC diagnostic pop\n"; |
| 474 | ss << TilingFuncDefForInductor(fused_schedule_result, elemwise_schedule_result) << std::endl; | 473 | ss << TilingFuncDefForInductor(fused_schedule_result, elemwise_schedule_result) << std::endl; |
| 475 | if (!is_cube_fused_scheduled) { | 474 | if (!is_cube_fused_scheduled) { |
| 476 | - ss << this->GenCandidateSolutionProtocolForInductor("AutofuseTilingData") << std::endl; | 475 | + GenInductorTopnSources(elemwise_schedule_result, ss, tiling_file_name_to_content); |
| 477 | - ss << this->GenTopnSelectorHelpersForInductor() << std::endl; | ||
| 478 | - ss << this->GenBuiltinTfPgoConfigsForInductor() << std::endl; | ||
| 479 | - ss << this->GenInductorConfigParserForInductor() << std::endl; | ||
| 480 | - ss << GenGetTilingKeyCount(elemwise_schedule_result) << std::endl; | ||
| 481 | - if (!ascgen_utils::IsSingleGroup(elemwise_schedule_result)) { | ||
| 482 | - ss << GenUpdateCurPerfAndBlockByGroupHelper() << std::endl; | ||
| 483 | - } | ||
| 484 | - ss << this->GenEvaluateModeledPerfForInductor("AutofuseTilingData", elemwise_schedule_result) << std::endl; | ||
| 485 | - ss << "extern \"C\" double GetModeledPerfForTesting(const AutofuseTilingData *tiling_data) {\n" | ||
| 486 | - << " if (tiling_data == nullptr) { return 0.0; }\n" | ||
| 487 | - << " double modeled_perf = EvaluateModeledPerf(*tiling_data);\n" | ||
| 488 | - << " return std::isfinite(modeled_perf) ? modeled_perf : DBL_MAX;\n" | ||
| 489 | - << "}\n" | ||
| 490 | - << std::endl; | ||
| 491 | - ss << this->GenGetTopnSolutionsFuncForInductor(elemwise_schedule_result, "AutofuseTilingData") << std::endl; | ||
| 492 | - ss << this->GenGetTilingDataReprFuncForInductor(elemwise_schedule_result, "AutofuseTilingData") << std::endl; | ||
| 493 | } | 476 | } |
| 494 | // 生成GenConstTilingData方法(对所有场景生成,包括CV fusion静态shape) | 477 | // 生成GenConstTilingData方法(对所有场景生成,包括CV fusion静态shape) |
| 495 | ss << TilingData("Autofuse").GenerateConst(fused_schedule_result) << std::endl; | 478 | ss << TilingData("Autofuse").GenerateConst(fused_schedule_result) << std::endl; |
| @@ -506,6 +489,10 @@ std::map<std::string, std::string> TilingLib::GenerateForInductor( | |||
| 506 | return tiling_file_name_to_content; | 489 | return tiling_file_name_to_content; |
| 507 | } | 490 | } |
| 508 | 491 | ||
| 492 | +bool TilingLib::IsSupportedInductorPgoScene(const ascir::FusedScheduledResult &fused_schedule_result) const { | ||
| 493 | + return !ascgen_utils::IsCubeFusedScheduled(fused_schedule_result) && IsStaticSchedResult(fused_schedule_result); | ||
| 494 | +} | ||
| 495 | + | ||
| 509 | void TilingLib::GenPgoMixTilingTable(const ascir::FusedScheduledResult &fused_schedule_result, | 496 | void TilingLib::GenPgoMixTilingTable(const ascir::FusedScheduledResult &fused_schedule_result, |
| 510 | std::stringstream &ss) const { | 497 | std::stringstream &ss) const { |
| 511 | for (size_t graph_id = 0U; graph_id < fused_schedule_result.node_idx_to_scheduled_results.size(); graph_id++) { | 498 | for (size_t graph_id = 0U; graph_id < fused_schedule_result.node_idx_to_scheduled_results.size(); graph_id++) { |
| @@ -772,6 +759,9 @@ std::map<std::string, std::string> TilingLib::GetTilingHeaders(const ascir::Fuse | |||
| 772 | tiling_file_name_to_content[kTilingHeadIdentify] += ss.str(); | 759 | tiling_file_name_to_content[kTilingHeadIdentify] += ss.str(); |
| 773 | options.emplace("tiling_data_type_name", tiling_name); | 760 | options.emplace("tiling_data_type_name", tiling_name); |
| 774 | options.emplace("solver_type", "AxesReorder"); | 761 | options.emplace("solver_type", "AxesReorder"); |
| 762 | + if (is_inductor_scene) { | ||
| 763 | + options.emplace(att::kInternalEnableAutofusePgo, enable_autofuse_pgo_ ? "true" : "false"); | ||
| 764 | + } | ||
| 775 | GE_CHK_BOOL_EXEC( | 765 | GE_CHK_BOOL_EXEC( |
| 776 | this->codegen_func_(fused_schedule_result.fused_graph_name.GetString(), fused_schedule_result, options, | 766 | this->codegen_func_(fused_schedule_result.fused_graph_name.GetString(), fused_schedule_result, options, |
| 777 | tiling_file_name_to_content, is_inductor_scene), | 767 | tiling_file_name_to_content, is_inductor_scene), |
| @@ -42,6 +42,8 @@ const std::string kCubeTilingHeadInclude = "#include \"autofuse_cube_tiling_data | |||
| 42 | const std::string kCubeKernelTilingWrapperHpp = "ACubeKernelTilingWrapperHpp"; | 42 | const std::string kCubeKernelTilingWrapperHpp = "ACubeKernelTilingWrapperHpp"; |
| 43 | const std::string kCubeKernelTilingWrapperCpp = "BCubeKernelTilingWrapperCpp"; | 43 | const std::string kCubeKernelTilingWrapperCpp = "BCubeKernelTilingWrapperCpp"; |
| 44 | const std::string kCubeKernelTilingWrapperInclude = "#include \"cube_kernel_tiling_wrapper.h\""; | 44 | const std::string kCubeKernelTilingWrapperInclude = "#include \"cube_kernel_tiling_wrapper.h\""; |
| 45 | +const std::string kPgoRunnerIdentify = "PgoRunner"; | ||
| 46 | +const std::string kPgoDeviceSourceIdentify = "PgoDeviceSource"; | ||
| 45 | 47 | ||
| 46 | struct MatMulCubeInfo { | 48 | struct MatMulCubeInfo { |
| 47 | bool transpose_x1 = false; | 49 | bool transpose_x1 = false; |
| @@ -97,9 +99,14 @@ struct PgoShapeStringStream { | |||
| 97 | std::stringstream tiling_set_shape_dim; | 99 | std::stringstream tiling_set_shape_dim; |
| 98 | std::stringstream shape_dim_use; | 100 | std::stringstream shape_dim_use; |
| 99 | }; | 101 | }; |
| 102 | + | ||
| 103 | +// TilingLib declarations stay centralized to preserve the class layout and access control. Implementations are split by | ||
| 104 | +// responsibility: common entry/workspace in codegen_tiling.cpp, Cube/CV in codegen_tiling_cube.cpp, PGO search in | ||
| 105 | +// codegen_tiling_pgo_search.cpp, PGO IO/memory in codegen_tiling_pgo_memory.cpp, shared PGO runtime code generation in | ||
| 106 | +// codegen_tiling_pgo_common.cpp, Inductor TopN/runner/proxy in the corresponding codegen_tiling_inductor_*.cpp files. | ||
| 100 | class TilingLib { | 107 | class TilingLib { |
| 101 | public: | 108 | public: |
| 102 | - // Core generation entry points are implemented in codegen_tiling.cpp. | 109 | + // codegen_tiling.cpp: common TF and Inductor entry generation. |
| 103 | TilingLib(const std::string &lib_path, const std::string &codegen_symbol_name); | 110 | TilingLib(const std::string &lib_path, const std::string &codegen_symbol_name); |
| 104 | std::map<std::string, std::string> Generate(const ::ascir::FusedScheduledResult &fused_schedule_result, | 111 | std::map<std::string, std::string> Generate(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 105 | const std::map<std::string, std::string> &shape_info, | 112 | const std::map<std::string, std::string> &shape_info, |
| @@ -107,19 +114,29 @@ class TilingLib { | |||
| 107 | std::map<std::string, std::string> GenerateForInductor( | 114 | std::map<std::string, std::string> GenerateForInductor( |
| 108 | const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 115 | const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 109 | 116 | ||
| 110 | - // The TF PGO generation entry point is implemented in codegen_tiling_pgo_runtime.cpp. | 117 | + // codegen_tiling_pgo_runtime.cpp: TF and Inductor shared PGO runtime orchestration. |
| 111 | std::string GenerateForPgo(const ::ascir::FusedScheduledResult &fused_schedule_result, | 118 | std::string GenerateForPgo(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 112 | const std::string &pgo_dir) const; | 119 | const std::string &pgo_dir) const; |
| 113 | std::string GetTilingIncludeHead(bool is_cv = false) const; | 120 | std::string GetTilingIncludeHead(bool is_cv = false) const; |
| 121 | + bool IsInductorPgoEnabled() const { | ||
| 122 | + return enable_autofuse_pgo_; | ||
| 123 | + } | ||
| 124 | + void DisableInductorPgo() { | ||
| 125 | + enable_autofuse_pgo_ = false; | ||
| 126 | + } | ||
| 114 | 127 | ||
| 115 | protected: | 128 | protected: |
| 116 | - // Common tiling generation is implemented in codegen_tiling.cpp. | 129 | + // codegen_tiling.cpp: ordinary tiling entry and generated translation-unit assembly. |
| 117 | std::string TilingFuncDef(const ::ascir::FusedScheduledResult &fused_schedule_result, | 130 | std::string TilingFuncDef(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 118 | const ::ascir::FusedScheduledResult &elemwise_schedule_result, | 131 | const ::ascir::FusedScheduledResult &elemwise_schedule_result, |
| 119 | const std::map<std::string, std::string> &shape_info, const std::string &pgo_dir, | 132 | const std::map<std::string, std::string> &shape_info, const std::string &pgo_dir, |
| 120 | const std::string &core_num) const; | 133 | const std::string &core_num) const; |
| 121 | std::string TilingFuncDefForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, | 134 | std::string TilingFuncDefForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 122 | const ::ascir::FusedScheduledResult &elemwise_schedule_result) const; | 135 | const ::ascir::FusedScheduledResult &elemwise_schedule_result) const; |
| 136 | + bool IsSupportedInductorPgoScene(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | ||
| 137 | + // codegen_tiling_inductor_topn.cpp: Inductor modeled/measured TopN source generation. | ||
| 138 | + void GenInductorTopnSources(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 139 | + std::map<std::string, std::string> &tiling_file_name_to_content) const; | ||
| 123 | std::map<std::string, std::string> GetTilingHeaders(const ::ascir::FusedScheduledResult &fused_schedule_result, | 140 | std::map<std::string, std::string> GetTilingHeaders(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 124 | bool is_inductor_scene, bool is_cv = false) const; | 141 | bool is_inductor_scene, bool is_cv = false) const; |
| 125 | std::string InferShapeDef(const ::ascir::HintGraph &graph) const; | 142 | std::string InferShapeDef(const ::ascir::HintGraph &graph) const; |
| @@ -128,7 +145,7 @@ class TilingLib { | |||
| 128 | std::string OpInputDef(const ::ascir::NodeView &node) const; | 145 | std::string OpInputDef(const ::ascir::NodeView &node) const; |
| 129 | std::string OpOutputDef(const ::ascir::NodeView &node) const; | 146 | std::string OpOutputDef(const ::ascir::NodeView &node) const; |
| 130 | 147 | ||
| 131 | - // PGO tensor arguments and memory management are implemented in codegen_tiling_pgo_memory.cpp. | 148 | + // codegen_tiling_pgo_memory.cpp: PGO IO declarations, size calculation and allocation lifecycle. |
| 132 | std::string ExternFunctionDeclare(const ::ascir::FusedScheduledResult &fused_schedule_result, | 149 | std::string ExternFunctionDeclare(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 133 | const std::string tiling) const; | 150 | const std::string tiling) const; |
| 134 | std::string PGOTensorArgsDef() const; | 151 | std::string PGOTensorArgsDef() const; |
| @@ -146,24 +163,27 @@ class TilingLib { | |||
| 146 | uint32_t PGOSearchFuncGetInputOutputCount(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 163 | uint32_t PGOSearchFuncGetInputOutputCount(const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 147 | uint32_t PGOSearchFuncGetOutputCount(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 164 | uint32_t PGOSearchFuncGetOutputCount(const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 148 | std::string CalculateTensorMemorySizeStr(const ::ascir::TensorAttr &tensor) const; | 165 | std::string CalculateTensorMemorySizeStr(const ::ascir::TensorAttr &tensor) const; |
| 166 | + std::string CalculateTensorMemorySizeStr(const ::ascir::TensorAttr &tensor, | ||
| 167 | + const ::af::Expression &element_offset) const; | ||
| 168 | + std::vector<std::string> CalculatePgoIoMemorySizeStrs(const ::ascir::FusedScheduledResult &fused_schedule_result, | ||
| 169 | + int64_t io_index, bool is_input, | ||
| 170 | + const ::ascir::TensorAttr &fallback_tensor) const; | ||
| 149 | std::string PGOSearchTensorMallocDef(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 171 | std::string PGOSearchTensorMallocDef(const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 150 | std::string PGOSearchTensorFreeDef(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 172 | std::string PGOSearchTensorFreeDef(const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 151 | - | 173 | + // codegen_tiling.cpp: fallback headers and ordinary tiling helpers. |
| 152 | - // Fallback headers are implemented in codegen_tiling.cpp. | ||
| 153 | std::string StubHeadersWithoutCodegenFunc() const; | 174 | std::string StubHeadersWithoutCodegenFunc() const; |
| 154 | std::string GetStubTilingHeaders(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 175 | std::string GetStubTilingHeaders(const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 155 | std::string GetStubTilingApi(const ::ascir::FusedScheduledResult &fused_schedule_result, bool include_pgo) const; | 176 | std::string GetStubTilingApi(const ::ascir::FusedScheduledResult &fused_schedule_result, bool include_pgo) const; |
| 156 | void PopulateFallbackAtomicHeaders(std::map<std::string, std::string> &tiling_file_name_to_content, | 177 | void PopulateFallbackAtomicHeaders(std::map<std::string, std::string> &tiling_file_name_to_content, |
| 157 | const ::ascir::FusedScheduledResult &fused_schedule_result, bool use_att_codegen, | 178 | const ::ascir::FusedScheduledResult &fused_schedule_result, bool use_att_codegen, |
| 158 | bool include_pgo) const; | 179 | bool include_pgo) const; |
| 159 | - | 180 | + // codegen_tiling_pgo_search.cpp: shared TF/Inductor PGO search source generation. |
| 160 | - // Shared PGO search helpers are implemented in codegen_tiling_pgo_search.cpp. | ||
| 161 | std::string GenGetAutoFuseTilingInput(bool is_inductor_scene) const; | 181 | std::string GenGetAutoFuseTilingInput(bool is_inductor_scene) const; |
| 162 | std::string GenGetResLimitStru(void) const; | 182 | std::string GenGetResLimitStru(void) const; |
| 163 | bool IsMixKernelTaskType(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 183 | bool IsMixKernelTaskType(const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 164 | 184 | ||
| 165 | private: | 185 | private: |
| 166 | - // Common tiling generation is implemented in codegen_tiling.cpp. | 186 | + // codegen_tiling.cpp: common tiling, workspace, shape and cache generation. |
| 167 | // 判断某个 origin_var 是否被特定 schedule_group 使用 | 187 | // 判断某个 origin_var 是否被特定 schedule_group 使用 |
| 168 | bool IsVarUsedInScheduleGroup(const std::string &var_define, const ::ascir::ScheduleGroup &schedule_group) const; | 188 | bool IsVarUsedInScheduleGroup(const std::string &var_define, const ::ascir::ScheduleGroup &schedule_group) const; |
| 169 | std::string GenGetTilingSizeFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, | 189 | std::string GenGetTilingSizeFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| @@ -175,31 +195,39 @@ class TilingLib { | |||
| 175 | std::string GenTilingFuncForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, | 195 | std::string GenTilingFuncForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 176 | const ::ascir::FusedScheduledResult &elemwise_schedule_result, | 196 | const ::ascir::FusedScheduledResult &elemwise_schedule_result, |
| 177 | const std::string func, const std::string tiling) const; | 197 | const std::string func, const std::string tiling) const; |
| 178 | - | 198 | + // codegen_tiling_inductor_topn.cpp: candidate protocol, selection and multi-group performance aggregation. |
| 179 | - // Inductor TopN generation is implemented in codegen_tiling_inductor_topn.cpp. | ||
| 180 | std::string GenGetTopnSolutionsFuncForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, | 199 | std::string GenGetTopnSolutionsFuncForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 181 | - const std::string &tiling) const; | 200 | + const std::string &tiling, bool use_measured_perf = false, |
| 201 | + const std::string &entry_declaration = "") const; | ||
| 202 | + std::string GenModeledFallbackTopnForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, | ||
| 203 | + const std::string &tiling) const; | ||
| 182 | void GenTopnInitSearchTiling(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result, | 204 | void GenTopnInitSearchTiling(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 183 | - const std::string &tiling, int symbol_value_count) const; | 205 | + const std::string &tiling, int symbol_value_count, bool use_measured_perf) const; |
| 184 | void GenTopnGetTilingFunc(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result, | 206 | void GenTopnGetTilingFunc(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 185 | - const std::string &tiling, int symbol_value_count) const; | 207 | + const std::string &tiling, int symbol_value_count, bool use_measured_perf) const; |
| 186 | void GenTopnSearchTilingSetup(std::stringstream &ss, const std::string &tiling, | 208 | void GenTopnSearchTilingSetup(std::stringstream &ss, const std::string &tiling, |
| 187 | const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 209 | const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 188 | void GenTopnCollectCandidates(std::stringstream &ss, const std::string &tiling) const; | 210 | void GenTopnCollectCandidates(std::stringstream &ss, const std::string &tiling) const; |
| 211 | + void GenTopnMeasuredCoreSearch(std::stringstream &ss, const std::string &tiling) const; | ||
| 212 | + void GenTopnMeasuredBatchProfiling(std::stringstream &ss) const; | ||
| 213 | + void GenTopnAppendMeasuredDefault(std::stringstream &ss) const; | ||
| 189 | void GenTopnSearchTilingKeyCall(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result, | 214 | void GenTopnSearchTilingKeyCall(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 190 | const std::string &search_cfg) const; | 215 | const std::string &search_cfg) const; |
| 191 | void GenTopnSetFailureMessage(std::stringstream &ss, const std::string &indent, const std::string &reason) const; | 216 | void GenTopnSetFailureMessage(std::stringstream &ss, const std::string &indent, const std::string &reason) const; |
| 192 | void GenTopnDefaultTiling(std::stringstream &ss, const std::string &tiling) const; | 217 | void GenTopnDefaultTiling(std::stringstream &ss, const std::string &tiling) const; |
| 193 | void GenTopnSearchAndFinalChecks(std::stringstream &ss, const std::string &tiling, | 218 | void GenTopnSearchAndFinalChecks(std::stringstream &ss, const std::string &tiling, |
| 194 | - const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 219 | + const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 220 | + bool use_measured_perf) const; | ||
| 195 | void GenGenerateTopnSolutionsEntry(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result, | 221 | void GenGenerateTopnSolutionsEntry(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 196 | - const std::string &tiling, | 222 | + const std::string &tiling, const codegen::PgoShapeStringStream &pgo_shape_dim, |
| 197 | - const codegen::PgoShapeStringStream &pgo_shape_dim) const; | 223 | + const std::string &entry_declaration) const; |
| 224 | + void GenInductorPgoProxyEntry(std::stringstream &ss, const std::string &tiling) const; | ||
| 198 | std::string GenCandidateSolutionProtocolForInductor(const std::string &tiling) const; | 225 | std::string GenCandidateSolutionProtocolForInductor(const std::string &tiling) const; |
| 226 | + void GenDeduplicateCandidateSolutionsPrefix(std::stringstream &ss) const; | ||
| 199 | void GenDeduplicateCandidateSolutions(std::stringstream &ss) const; | 227 | void GenDeduplicateCandidateSolutions(std::stringstream &ss) const; |
| 228 | + void GenDeduplicateMeasuredCandidateSolutions(std::stringstream &ss) const; | ||
| 200 | std::string GenTopnSelectorHelpersForInductor() const; | 229 | std::string GenTopnSelectorHelpersForInductor() const; |
| 201 | - std::string GenSearchConfigProtocolForInductor() const; | 230 | + std::string GenMeasuredTopnSelectorHelpersForInductor() const; |
| 202 | - std::string GenBuiltinTfPgoConfigsForInductor() const; | ||
| 203 | std::string GenInductorConfigParserForInductor() const; | 231 | std::string GenInductorConfigParserForInductor() const; |
| 204 | std::string GenGetTilingDataReprFuncForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, | 232 | std::string GenGetTilingDataReprFuncForInductor(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 205 | const std::string &tiling) const; | 233 | const std::string &tiling) const; |
| @@ -217,8 +245,7 @@ class TilingLib { | |||
| 217 | const std::string &field_prefix, bool top_level) const; | 245 | const std::string &field_prefix, bool top_level) const; |
| 218 | void GenReprSingleGroup(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 246 | void GenReprSingleGroup(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 219 | void GenReprMultiGroup(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 247 | void GenReprMultiGroup(std::stringstream &ss, const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 220 | - | 248 | + // codegen_tiling_pgo_search.cpp: PGO candidate traversal, tiling key and configuration persistence. |
| 221 | - // PGO candidate search is implemented in codegen_tiling_pgo_search.cpp. | ||
| 222 | std::string GenPgoTilingFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, const std::string &tiling, | 249 | std::string GenPgoTilingFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, const std::string &tiling, |
| 223 | codegen::PgoShapeStringStream &pgo_shape_dim, bool is_inductor_scene, | 250 | codegen::PgoShapeStringStream &pgo_shape_dim, bool is_inductor_scene, |
| 224 | const std::string &core_num = "0") const; | 251 | const std::string &core_num = "0") const; |
| @@ -229,6 +256,7 @@ class TilingLib { | |||
| 229 | std::string GenPgoTilingSearchPGO(const ::ascir::FusedScheduledResult &fused_schedule_result, | 256 | std::string GenPgoTilingSearchPGO(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 230 | codegen::PgoShapeStringStream &pgo_shape_dim, const std::string &tiling, | 257 | codegen::PgoShapeStringStream &pgo_shape_dim, const std::string &tiling, |
| 231 | bool is_inductor_scene, const std::string &core_num) const; | 258 | bool is_inductor_scene, const std::string &core_num) const; |
| 259 | + void GenPgoTilingKeySearch(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | ||
| 232 | 260 | ||
| 233 | std::string GenPgoTilingSearch(const ::ascir::FusedScheduledResult &fused_schedule_result, | 261 | std::string GenPgoTilingSearch(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 234 | codegen::PgoShapeStringStream &pgo_shape_dim, const std::string &tiling) const; | 262 | codegen::PgoShapeStringStream &pgo_shape_dim, const std::string &tiling) const; |
| @@ -242,46 +270,98 @@ class TilingLib { | |||
| 242 | std::string GenPGOGetTilingKey(const std::string tiling) const; | 270 | std::string GenPGOGetTilingKey(const std::string tiling) const; |
| 243 | std::string GenSavePGOSearchTilingDataFunc(const std::string tiling) const; | 271 | std::string GenSavePGOSearchTilingDataFunc(const std::string tiling) const; |
| 244 | std::string GenSavePGOConfigTilingDataFunc() const; | 272 | std::string GenSavePGOConfigTilingDataFunc() const; |
| 245 | - | 273 | + // codegen_tiling_pgo_common.cpp: shared MSPTI callbacks and launch/runtime source generation. |
| 246 | - // PGO runtime source generation is implemented in codegen_tiling_pgo_common.cpp. | ||
| 247 | void GenPgoSaveTilingKey(std::stringstream &ss) const; | 274 | void GenPgoSaveTilingKey(std::stringstream &ss) const; |
| 248 | void GenPgoAppendSearchTilingData(std::stringstream &ss) const; | 275 | void GenPgoAppendSearchTilingData(std::stringstream &ss) const; |
| 249 | - void GenPgoKernelLaunchOpArgs(const ::ascir::FusedScheduledResult &fused_schedule_result, | 276 | + void GenPgoKernelLaunchOpArgs(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, |
| 250 | - std::stringstream &ss) const; | 277 | + bool direct_link = false) const; |
| 251 | void GenDynamicLibraryLoaderCode(std::stringstream &ss) const; | 278 | void GenDynamicLibraryLoaderCode(std::stringstream &ss) const; |
| 252 | - void GenPgoHeaders(std::stringstream &ss) const; | 279 | + void GenPgoHeaders(std::stringstream &ss, bool direct_link = false) const; |
| 253 | void GenPgoMain(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 280 | void GenPgoMain(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 254 | void GenPgoEnvInit(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 281 | void GenPgoEnvInit(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 255 | void GenPgoCardLock(std::stringstream &ss) const; | 282 | void GenPgoCardLock(std::stringstream &ss) const; |
| 256 | void GenPgoMixTilingTable(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 283 | void GenPgoMixTilingTable(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 257 | void GenPgoCheckTilingIsMix(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 284 | void GenPgoCheckTilingIsMix(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 285 | + void GenPgoToolDeclarations(const ::ascir::FusedScheduledResult &fused_schedule_result, const std::string &pgo_dir, | ||
| 286 | + std::stringstream &ss, bool direct_link) const; | ||
| 258 | void GenPgoToolFunction(const ::ascir::FusedScheduledResult &fused_schedule_result, const std::string &pgo_dir, | 287 | void GenPgoToolFunction(const ::ascir::FusedScheduledResult &fused_schedule_result, const std::string &pgo_dir, |
| 259 | - std::stringstream &ss) const; | 288 | + std::stringstream &ss, bool direct_link = false) const; |
| 260 | - void GenPgoLaunchKernelInit(std::stringstream &ss) const; | 289 | + void GenPgoLaunchKernelInit(std::stringstream &ss, bool direct_link = false) const; |
| 261 | - void GenPgoLaunchParamsInit(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 290 | + void GenInductorPgoKernelFunctionInit(std::stringstream &ss) const; |
| 291 | + void GenPgoKernelFunctionsInit(const std::string &bin_handle, std::stringstream &ss) const; | ||
| 292 | + void GenPgoLaunchParamsInit(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 293 | + bool direct_link = false) const; | ||
| 262 | void GenPgoLaunchParamsDeInit(std::stringstream &ss) const; | 294 | void GenPgoLaunchParamsDeInit(std::stringstream &ss) const; |
| 263 | void GenPgoUpdateLaunchParams(std::stringstream &ss) const; | 295 | void GenPgoUpdateLaunchParams(std::stringstream &ss) const; |
| 264 | - void GenPgoLaunchParams(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 296 | + void GenInductorPgoUpdateLaunchParams(std::stringstream &ss) const; |
| 297 | + void GenPgoCopyLaunchArgs(std::stringstream &ss, const std::string &kernel_type, const std::string &assignment) const; | ||
| 298 | + void GenPgoLaunchParams(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 299 | + bool direct_link = false) const; | ||
| 265 | void GenPgoDeinit(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 300 | void GenPgoDeinit(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 266 | void GenPgoWrapperParmCall(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 301 | void GenPgoWrapperParmCall(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 302 | + void GenPgoWrapperInit(std::stringstream &ss, bool direct_link) const; | ||
| 267 | void GenPgoWrapperKernelLaunch(std::stringstream &ss) const; | 303 | void GenPgoWrapperKernelLaunch(std::stringstream &ss) const; |
| 268 | - void GenPgoWrapper(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 304 | + void GenPgoWrapper(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, |
| 269 | - void GenPgoProfilingConstants(std::stringstream &ss) const; | 305 | + bool direct_link = false) const; |
| 306 | + void GenPgoProfilingConstants(std::stringstream &ss, bool direct_link = false) const; | ||
| 270 | void GenPgoMsptiStringTable(std::stringstream &ss) const; | 307 | void GenPgoMsptiStringTable(std::stringstream &ss) const; |
| 271 | - void GenPgoMsptiRequest(std::stringstream &ss) const; | 308 | + void GenPgoMsptiRequest(std::stringstream &ss, bool direct_link = false) const; |
| 272 | - void GenPgoMsptiComplete(std::stringstream &ss) const; | 309 | + void GenPgoDirectMsptiKernelHandlers(std::stringstream &ss) const; |
| 273 | - void GenPgoMsptiToolFunction(std::stringstream &ss) const; | 310 | + void GenPgoDirectMsptiComplete(std::stringstream &ss) const; |
| 274 | - void GenPgoMsptiProfiling(std::stringstream &ss) const; | 311 | + void GenPgoLegacyMsptiComplete(std::stringstream &ss) const; |
| 312 | + void GenPgoMsptiComplete(std::stringstream &ss, bool direct_link = false) const; | ||
| 313 | + void GenPgoMsptiToolFunction(std::stringstream &ss, bool direct_link = false) const; | ||
| 314 | + void GenPgoMsptiProfiling(std::stringstream &ss, bool direct_link = false) const; | ||
| 315 | + void GenPgoDirectBatchCallback(std::stringstream &ss) const; | ||
| 275 | void GenPgoBatchCallback(std::stringstream &ss) const; | 316 | void GenPgoBatchCallback(std::stringstream &ss) const; |
| 276 | - void GenPgoBatchProcess(std::stringstream &ss) const; | 317 | + void GenPgoDirectBatchProcess(std::stringstream &ss) const; |
| 277 | - void GenPgoGetProfilingBatch(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 318 | + void GenPgoBatchProcess(std::stringstream &ss, bool direct_link = false) const; |
| 278 | - void GenPgoProfilingCallback(std::stringstream &ss) const; | 319 | + void GenPgoProfilingBatchSetup(std::stringstream &ss, bool direct_link) const; |
| 279 | - void GenPgoGetProfiling(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 320 | + void GenPgoGetProfilingBatch(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, |
| 321 | + bool direct_link = false) const; | ||
| 322 | + void GenPgoDirectProfilingCallback(std::stringstream &ss) const; | ||
| 323 | + void GenPgoLegacyProfilingCallback(std::stringstream &ss) const; | ||
| 324 | + void GenPgoProfilingCallback(std::stringstream &ss, bool direct_link = false) const; | ||
| 325 | + void GenPgoProfilingSetup(std::stringstream &ss, bool direct_link) const; | ||
| 326 | + void GenPgoProfilingLaunch(std::stringstream &ss, bool direct_link) const; | ||
| 327 | + void GenPgoProfilingWorkspaceCleanup(std::stringstream &ss, bool direct_link) const; | ||
| 328 | + void GenPgoGetProfiling(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 329 | + bool direct_link = false) const; | ||
| 280 | void GenPgoFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 330 | void GenPgoFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 281 | void GenPgoStaticFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 331 | void GenPgoStaticFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 282 | void GenPgoProfiling(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | 332 | void GenPgoProfiling(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; |
| 283 | - | 333 | + // codegen_tiling_pgo_runtime.cpp: shared runner composition. |
| 284 | - // Common tiling helpers are implemented in codegen_tiling.cpp. | 334 | + void GenSharedPgoRuntimeLaunch(const ::ascir::FusedScheduledResult &fused_schedule_result, const std::string &pgo_dir, |
| 335 | + std::stringstream &ss, bool direct_link) const; | ||
| 336 | + void GenSharedPgoRuntimeProfiling(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 337 | + bool direct_link) const; | ||
| 338 | + std::string GenInductorPgoRunner(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | ||
| 339 | + // codegen_tiling_inductor_pgo_runner.cpp: standalone runner protocol, parsing and ACL runtime. | ||
| 340 | + void GenInductorPgoResultProtocol(std::stringstream &ss) const; | ||
| 341 | + void GenInductorPgoResultTypes(std::stringstream &ss) const; | ||
| 342 | + void GenInductorPgoRecordWriter(std::stringstream &ss) const; | ||
| 343 | + void GenInductorPgoResultWriter(std::stringstream &ss) const; | ||
| 344 | + void GenInductorPgoSearchWriter(std::stringstream &ss) const; | ||
| 345 | + void GenInductorPgoArgValidators(std::stringstream &ss) const; | ||
| 346 | + void GenInductorPgoArgParser(std::stringstream &ss) const; | ||
| 347 | + void GenInductorPgoContextGuard(std::stringstream &ss) const; | ||
| 348 | + void GenInductorPgoHostLoader(std::stringstream &ss) const; | ||
| 349 | + void GenInductorPgoRuntime(const ::ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const; | ||
| 350 | + void GenInductorPgoAclRuntime(std::stringstream &ss) const; | ||
| 351 | + void GenInductorPgoMemoryRuntime(const ::ascir::FusedScheduledResult &fused_schedule_result, | ||
| 352 | + std::stringstream &ss) const; | ||
| 353 | + void GenInductorPgoDeinitRuntime(const ::ascir::FusedScheduledResult &fused_schedule_result, | ||
| 354 | + std::stringstream &ss) const; | ||
| 355 | + void GenInductorPgoMain(std::stringstream &ss) const; | ||
| 356 | + std::string GenTopnPgoContextAbi() const; | ||
| 357 | + // codegen_tiling_inductor_pgo_proxy.cpp: parent process proxy, manifest and child process launch. | ||
| 358 | + void GenInductorPgoProxyIncludes(std::stringstream &ss, const std::string &tiling) const; | ||
| 359 | + void GenInductorPgoProxyFileHelpers(std::stringstream &ss) const; | ||
| 360 | + void GenInductorPgoProxySha256(std::stringstream &ss) const; | ||
| 361 | + void GenInductorPgoProxyManifest(std::stringstream &ss) const; | ||
| 362 | + void GenInductorPgoProxyResultParser(std::stringstream &ss, const std::string &tiling) const; | ||
| 363 | + void GenInductorPgoProxySpawn(std::stringstream &ss) const; | ||
| 364 | + void GenInductorPgoProxyFunction(std::stringstream &ss, const std::string &tiling) const; | ||
| 285 | std::string GenExternTilingFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, | 365 | std::string GenExternTilingFunc(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 286 | const std::map<std::string, std::string> &shape_info, const std::string tiling, | 366 | const std::map<std::string, std::string> &shape_info, const std::string tiling, |
| 287 | const std::string &pgo_dir, const std::string &core_num) const; | 367 | const std::string &pgo_dir, const std::string &core_num) const; |
| @@ -306,8 +386,7 @@ class TilingLib { | |||
| 306 | std::string GenGetTilingKeyCount(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 386 | std::string GenGetTilingKeyCount(const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 307 | std::string GenGetTilingKeyForStatic() const; | 387 | std::string GenGetTilingKeyForStatic() const; |
| 308 | std::string GenGetTilingKeyKernelTypeForStatic(const ::ascir::FusedScheduledResult &fused_schedule_result) const; | 388 | std::string GenGetTilingKeyKernelTypeForStatic(const ::ascir::FusedScheduledResult &fused_schedule_result) const; |
| 309 | - | 389 | + // codegen_tiling_cube.cpp: Cube/CV tiling and MatMul metadata extraction. |
| 310 | - // Cube tiling generation is implemented in codegen_tiling_cube.cpp. | ||
| 311 | std::string GenCVTilingFunc() const; | 390 | std::string GenCVTilingFunc() const; |
| 312 | std::string GenTilingDataBlockDimAndWss() const; | 391 | std::string GenTilingDataBlockDimAndWss() const; |
| 313 | std::map<std::string, std::string> GenerateCVFusionStatic( | 392 | std::map<std::string, std::string> GenerateCVFusionStatic( |
| @@ -320,6 +399,7 @@ class TilingLib { | |||
| 320 | const ::ascir::FusedScheduledResult &elemwise_schedule_result, | 399 | const ::ascir::FusedScheduledResult &elemwise_schedule_result, |
| 321 | const std::map<std::string, std::string> &shape_info, const std::string &pgo_dir, | 400 | const std::map<std::string, std::string> &shape_info, const std::string &pgo_dir, |
| 322 | const std::string &core_num) const; | 401 | const std::string &core_num) const; |
| 402 | + // codegen_tiling.cpp: Cube/CV entry orchestration shared with the common translation-unit renderer. | ||
| 323 | std::map<std::string, std::string> GenerateCVFusion(const ::ascir::FusedScheduledResult &fused_schedule_result, | 403 | std::map<std::string, std::string> GenerateCVFusion(const ::ascir::FusedScheduledResult &fused_schedule_result, |
| 324 | const std::map<std::string, std::string> &shape_info, | 404 | const std::map<std::string, std::string> &shape_info, |
| 325 | const std::string &pgo_dir, const std::string &core_num) const; | 405 | const std::string &pgo_dir, const std::string &core_num) const; |
| @@ -71,8 +71,8 @@ void AppendCvUbFusionStageSizeName(const ascir::FusedScheduledResult &fused_sche | |||
| 71 | } | 71 | } |
| 72 | ss << "}" << std::endl; | 72 | ss << "}" << std::endl; |
| 73 | } | 73 | } |
| 74 | -} // namespace | ||
| 75 | 74 | ||
| 75 | +} // namespace | ||
| 76 | std::string DtypeToStr(ge::DataType dtype) { | 76 | std::string DtypeToStr(ge::DataType dtype) { |
| 77 | const std::map<ge::DataType, const ge::char_t *> kTypeName = { | 77 | const std::map<ge::DataType, const ge::char_t *> kTypeName = { |
| 78 | {ge::DT_FLOAT, "float32"}, {ge::DT_FLOAT16, "float16"}, {ge::DT_BF16, "bfloat16"}, {ge::DT_INT8, "int8"}, | 78 | {ge::DT_FLOAT, "float32"}, {ge::DT_FLOAT16, "float16"}, {ge::DT_BF16, "bfloat16"}, {ge::DT_INT8, "int8"}, |
| @@ -0,0 +1,458 @@ | |||
| 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 | +namespace codegen { | ||
| 14 | + | ||
| 15 | +namespace { | ||
| 16 | + | ||
| 17 | +void AppendInductorPgoProxySystemIncludes(std::stringstream &ss) { | ||
| 18 | + ss << R"( | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + | ||
| 41 | + | ||
| 42 | +extern char **environ; | ||
| 43 | +)"; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +void AppendInductorPgoProxyDeclarations(std::stringstream &ss, const std::string &tiling) { | ||
| 47 | + ss << "extern \"C\" int64_t GenerateTopnSolutions(" | ||
| 48 | + << "const std::vector<std::map<std::string, std::string>> &, int64_t, std::vector<" << tiling | ||
| 49 | + << "> &, std::vector<int64_t> &, std::vector<int64_t> &, ResLimit *);" << std::endl; | ||
| 50 | + ss << "namespace inductor_pgo_fallback {" << std::endl; | ||
| 51 | + ss << "static int64_t GenerateModeledFallbackTopnSolutions(" | ||
| 52 | + << "const std::vector<std::map<std::string, std::string>> &, int64_t, std::vector<" << tiling | ||
| 53 | + << "> &, std::vector<int64_t> &, std::vector<int64_t> &, ResLimit *);" << std::endl; | ||
| 54 | + ss << "} // namespace inductor_pgo_fallback" << std::endl; | ||
| 55 | + ss << R"( | ||
| 56 | +namespace { | ||
| 57 | +constexpr uint32_t kPgoBundleSchemaVersion = 1U; | ||
| 58 | +constexpr int64_t kMaxPgoTopn = 1024; | ||
| 59 | +constexpr size_t kMaxPgoManifestSize = 1024U * 1024U; | ||
| 60 | +constexpr size_t kMaxPgoArtifactSize = 512U * 1024U * 1024U; | ||
| 61 | +constexpr size_t kMaxPgoResultSize = 256U * 1024U * 1024U; | ||
| 62 | +constexpr char kPgoKernelFormat[] = "aicore_binary_elf_v1"; | ||
| 63 | +constexpr char kPgoTopnMagic[] = "AUTOFUSE_PGO_TOPN_V1"; | ||
| 64 | +constexpr uint32_t kPgoTopnProtocolVersion = 1U; | ||
| 65 | +constexpr uint32_t kPgoTopnRecordHeaderSize = 32U; | ||
| 66 | +constexpr char kPgoGeneration[] = AUTOFUSE_PGO_GENERATION; | ||
| 67 | +static_assert(std::is_trivially_copyable<AutofuseTilingData>::value); | ||
| 68 | +static_assert(std::is_standard_layout<AutofuseTilingData>::value); | ||
| 69 | +struct InductorPgoArtifacts { | ||
| 70 | + std::string tiling_so; std::string generation_dir; std::string runner; | ||
| 71 | + std::string kernel; std::string manifest; std::string ld_preload; | ||
| 72 | +}; | ||
| 73 | +)"; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +} // namespace | ||
| 77 | + | ||
| 78 | +void TilingLib::GenInductorPgoProxyIncludes(std::stringstream &ss, const std::string &tiling) const { | ||
| 79 | + AppendInductorPgoProxySystemIncludes(ss); | ||
| 80 | + AppendInductorPgoProxyDeclarations(ss, tiling); | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +void TilingLib::GenInductorPgoProxyFileHelpers(std::stringstream &ss) const { | ||
| 84 | + ss << R"( | ||
| 85 | +bool ReadPgoFile(const std::string &path, size_t limit, std::vector<uint8_t> &data) { | ||
| 86 | + std::ifstream file(path, std::ios::binary | std::ios::ate); | ||
| 87 | + if (!file.is_open()) { return false; } | ||
| 88 | + const auto size = file.tellg(); | ||
| 89 | + if (size < 0 || static_cast<uint64_t>(size) > limit) { return false; } | ||
| 90 | + data.resize(static_cast<size_t>(size)); file.seekg(0, std::ios::beg); | ||
| 91 | + return data.empty() || static_cast<bool>(file.read(reinterpret_cast<char *>(data.data()), size)); | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +std::string PgoBaseName(const std::string &path) { | ||
| 95 | + const auto pos = path.find_last_of('/'); | ||
| 96 | + return pos == std::string::npos ? path : path.substr(pos + 1U); | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +bool GetJsonString(const std::string &json, const std::string &key, size_t begin, std::string &value) { | ||
| 100 | + const std::string token = "\"" + key + "\""; | ||
| 101 | + const auto key_pos = json.find(token, begin); | ||
| 102 | + const auto colon = key_pos == std::string::npos ? key_pos : json.find(':', key_pos + token.size()); | ||
| 103 | + const auto quote = colon == std::string::npos ? colon : json.find('"', colon + 1U); | ||
| 104 | + const auto end = quote == std::string::npos ? quote : json.find('"', quote + 1U); | ||
| 105 | + if (end == std::string::npos) { return false; } | ||
| 106 | + value = json.substr(quote + 1U, end - quote - 1U); return true; | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +bool GetJsonUint(const std::string &json, const std::string &key, uint32_t &value) { | ||
| 110 | + const std::string token = "\"" + key + "\""; const auto key_pos = json.find(token); | ||
| 111 | + const auto colon = key_pos == std::string::npos ? key_pos : json.find(':', key_pos + token.size()); | ||
| 112 | + if (colon == std::string::npos) { return false; } | ||
| 113 | + char *end = nullptr; errno = 0; const auto parsed = std::strtoul(json.c_str() + colon + 1U, &end, 10); | ||
| 114 | + if (errno != 0 || end == json.c_str() + colon + 1U || parsed > UINT32_MAX) { return false; } | ||
| 115 | + value = static_cast<uint32_t>(parsed); return true; | ||
| 116 | +} | ||
| 117 | +)"; | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +void TilingLib::GenInductorPgoProxySha256(std::stringstream &ss) const { | ||
| 121 | + ss << R"( | ||
| 122 | +bool ComputeFileSha256(const std::string &path, std::string &hex) { | ||
| 123 | + std::vector<uint8_t> data; | ||
| 124 | + if (!ReadPgoFile(path, kMaxPgoArtifactSize, data)) { return false; } | ||
| 125 | + void *crypto = dlopen("libcrypto.so.3", RTLD_NOW | RTLD_LOCAL); | ||
| 126 | + if (crypto == nullptr) { crypto = dlopen("libcrypto.so", RTLD_NOW | RTLD_LOCAL); } | ||
| 127 | + if (crypto == nullptr) { return false; } | ||
| 128 | + using Sha256Fn = unsigned char *(*)(const unsigned char *, size_t, unsigned char *); | ||
| 129 | + auto sha256 = reinterpret_cast<Sha256Fn>(dlsym(crypto, "SHA256")); | ||
| 130 | + unsigned char digest[32] = {}; | ||
| 131 | + const bool valid = sha256 != nullptr && sha256(data.data(), data.size(), digest) != nullptr; | ||
| 132 | + dlclose(crypto); | ||
| 133 | + if (!valid) { return false; } | ||
| 134 | + constexpr char digits[] = "0123456789abcdef"; hex.resize(64U); | ||
| 135 | + for (size_t i = 0; i < 32U; ++i) { | ||
| 136 | + hex[2U * i] = digits[digest[i] >> 4U]; hex[2U * i + 1U] = digits[digest[i] & 0xFU]; | ||
| 137 | + } | ||
| 138 | + return true; | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +bool ValidateArtifactHash(const std::string &json, const std::string &name, const std::string &path) { | ||
| 142 | + const auto section = json.find("\"" + name + "\""); | ||
| 143 | + std::string file_name; std::string expected_hash; std::string actual_hash; | ||
| 144 | + return section != std::string::npos && GetJsonString(json, "file", section, file_name) && | ||
| 145 | + GetJsonString(json, "sha256", section, expected_hash) && file_name == PgoBaseName(path) && | ||
| 146 | + expected_hash.size() == 64U && ComputeFileSha256(path, actual_hash) && actual_hash == expected_hash; | ||
| 147 | +} | ||
| 148 | +)"; | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +namespace { | ||
| 152 | + | ||
| 153 | +void AppendInductorPgoManifestValidation(std::stringstream &ss) { | ||
| 154 | + ss << R"( | ||
| 155 | +bool ValidateInductorPgoManifest(InductorPgoArtifacts &artifacts) { | ||
| 156 | + std::vector<uint8_t> bytes; | ||
| 157 | + if (!ReadPgoFile(artifacts.manifest, kMaxPgoManifestSize, bytes)) { | ||
| 158 | + OP_LOGE(OP_NAME, "Read Inductor PGO manifest failed: %s", artifacts.manifest.c_str()); | ||
| 159 | + return false; | ||
| 160 | + } | ||
| 161 | + const std::string json(bytes.begin(), bytes.end()); | ||
| 162 | + std::string generation; std::string ld_preload; | ||
| 163 | + uint32_t bundle_schema_version = 0; uint32_t result_protocol_version = 0; | ||
| 164 | + if (!GetJsonUint(json, "bundle_schema_version", bundle_schema_version) || | ||
| 165 | + !GetJsonString(json, "generation", 0U, generation) || | ||
| 166 | + !GetJsonUint(json, "result_protocol_version", result_protocol_version) || | ||
| 167 | + !GetJsonString(json, "ld_preload", 0U, ld_preload)) { | ||
| 168 | + OP_LOGE(OP_NAME, "Invalid Inductor PGO manifest fields: %s", artifacts.manifest.c_str()); | ||
| 169 | + return false; | ||
| 170 | + } | ||
| 171 | + const bool valid = bundle_schema_version == kPgoBundleSchemaVersion && generation == kPgoGeneration && | ||
| 172 | + result_protocol_version == kPgoTopnProtocolVersion && | ||
| 173 | + ValidateArtifactHash(json, "tiling_so", artifacts.tiling_so) && | ||
| 174 | + ValidateArtifactHash(json, "runner", artifacts.runner) && | ||
| 175 | + ValidateArtifactHash(json, "kernel", artifacts.kernel); | ||
| 176 | + if (!valid) { | ||
| 177 | + OP_LOGE(OP_NAME, "Inductor PGO manifest validation failed: %s", artifacts.manifest.c_str()); | ||
| 178 | + return false; | ||
| 179 | + } | ||
| 180 | + artifacts.ld_preload = ld_preload; | ||
| 181 | + return true; | ||
| 182 | +} | ||
| 183 | +)"; | ||
| 184 | +} | ||
| 185 | + | ||
| 186 | +void AppendInductorPgoArtifactResolution(std::stringstream &ss) { | ||
| 187 | + ss << R"( | ||
| 188 | +bool ResolveInductorPgoArtifactsUncached(InductorPgoArtifacts &artifacts) { | ||
| 189 | + Dl_info info = {}; | ||
| 190 | + if (kPgoGeneration[0] == '\0' || | ||
| 191 | + dladdr(reinterpret_cast<const void *>(&ResolveInductorPgoArtifactsUncached), &info) == 0 || | ||
| 192 | + info.dli_fname == nullptr) { | ||
| 193 | + OP_LOGE(OP_NAME, "Resolve Inductor PGO artifacts failed: invalid generation or tiling so path"); | ||
| 194 | + return false; | ||
| 195 | + } | ||
| 196 | + char real_path[PATH_MAX] = {}; | ||
| 197 | + if (realpath(info.dli_fname, real_path) == nullptr) { | ||
| 198 | + OP_LOGE(OP_NAME, "Resolve Inductor PGO tiling so realpath failed: %s", info.dli_fname); | ||
| 199 | + return false; | ||
| 200 | + } | ||
| 201 | + const std::string tiling_entry = real_path; | ||
| 202 | + artifacts.generation_dir = tiling_entry + ".pgo." + kPgoGeneration; | ||
| 203 | + const std::string base = PgoBaseName(tiling_entry); | ||
| 204 | + artifacts.tiling_so = artifacts.generation_dir + "/" + base; | ||
| 205 | + artifacts.runner = artifacts.generation_dir + "/" + base + ".pgo_runner"; | ||
| 206 | + artifacts.kernel = artifacts.generation_dir + "/" + base + ".pgo_kernel." + kPgoKernelFormat; | ||
| 207 | + artifacts.manifest = artifacts.generation_dir + "/manifest.json"; | ||
| 208 | + if (access(artifacts.tiling_so.c_str(), R_OK) != 0 || access(artifacts.runner.c_str(), X_OK) != 0 || | ||
| 209 | + access(artifacts.kernel.c_str(), R_OK) != 0) { | ||
| 210 | + OP_LOGE(OP_NAME, "Inductor PGO sidecar missing: tiling=%s runner=%s kernel=%s", artifacts.tiling_so.c_str(), | ||
| 211 | + artifacts.runner.c_str(), artifacts.kernel.c_str()); | ||
| 212 | + return false; | ||
| 213 | + } | ||
| 214 | + return ValidateInductorPgoManifest(artifacts); | ||
| 215 | +} | ||
| 216 | +)"; | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +void AppendInductorPgoArtifactCache(std::stringstream &ss) { | ||
| 220 | + ss << R"( | ||
| 221 | +bool ResolveInductorPgoArtifacts(InductorPgoArtifacts &artifacts) { | ||
| 222 | + static std::once_flag validation_once; | ||
| 223 | + static InductorPgoArtifacts cached_artifacts; | ||
| 224 | + static bool valid = false; | ||
| 225 | + std::call_once(validation_once, []() { | ||
| 226 | + InductorPgoArtifacts resolved_artifacts; | ||
| 227 | + valid = ResolveInductorPgoArtifactsUncached(resolved_artifacts); | ||
| 228 | + if (valid) { cached_artifacts = std::move(resolved_artifacts); } | ||
| 229 | + }); | ||
| 230 | + if (!valid) { return false; } | ||
| 231 | + artifacts = cached_artifacts; | ||
| 232 | + return true; | ||
| 233 | +} | ||
| 234 | +)"; | ||
| 235 | +} | ||
| 236 | + | ||
| 237 | +} // namespace | ||
| 238 | + | ||
| 239 | +void TilingLib::GenInductorPgoProxyManifest(std::stringstream &ss) const { | ||
| 240 | + AppendInductorPgoManifestValidation(ss); | ||
| 241 | + AppendInductorPgoArtifactResolution(ss); | ||
| 242 | + AppendInductorPgoArtifactCache(ss); | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +void TilingLib::GenInductorPgoProxyResultParser(std::stringstream &ss, const std::string &tiling) const { | ||
| 246 | + ss << R"( | ||
| 247 | +template <typename T> | ||
| 248 | +bool ReadPgoResultValue(const std::vector<uint8_t> &data, size_t &offset, T &value) { | ||
| 249 | + if (offset > data.size() || sizeof(T) > data.size() - offset) { return false; } | ||
| 250 | + std::memcpy(&value, data.data() + offset, sizeof(T)); offset += sizeof(T); return true; | ||
| 251 | +} | ||
| 252 | + | ||
| 253 | +uint64_t HashProxyTiling(const void *data, size_t size) { | ||
| 254 | + const auto *bytes = static_cast<const uint8_t *>(data); uint64_t hash = 1469598103934665603ULL; | ||
| 255 | + for (size_t i = 0; i < size; ++i) { hash = (hash ^ bytes[i]) * 1099511628211ULL; } | ||
| 256 | + return hash; | ||
| 257 | +} | ||
| 258 | +)"; | ||
| 259 | + ss << "bool ParseInductorPgoResult(const std::string &path, int64_t topn, std::vector<" << tiling | ||
| 260 | + << "> &tiling_datas, std::vector<int64_t> &workspaces, std::vector<int64_t> &block_dims) {" << std::endl; | ||
| 261 | + ss << R"( std::vector<uint8_t> data; | ||
| 262 | + if (!ReadPgoFile(path, kMaxPgoResultSize, data) || data.size() < 40U || | ||
| 263 | + std::memcmp(data.data(), kPgoTopnMagic, 20U) != 0) { return false; } | ||
| 264 | + size_t offset = 20U; uint32_t version = 0; uint32_t flags = 0; uint32_t count = 0; | ||
| 265 | + uint32_t tiling_size = 0; uint32_t record_header_size = 0; | ||
| 266 | + if (!ReadPgoResultValue(data, offset, version) || !ReadPgoResultValue(data, offset, flags) || | ||
| 267 | + !ReadPgoResultValue(data, offset, count) || !ReadPgoResultValue(data, offset, tiling_size) || | ||
| 268 | + !ReadPgoResultValue(data, offset, record_header_size) || version != kPgoTopnProtocolVersion || flags != 0U || | ||
| 269 | + count == 0U || count > static_cast<uint64_t>(topn) || tiling_size != sizeof(AutofuseTilingData) || | ||
| 270 | + record_header_size != kPgoTopnRecordHeaderSize) { return false; } | ||
| 271 | +)"; | ||
| 272 | + ss << " std::vector<" << tiling << "> parsed_tilings; std::vector<int64_t> parsed_workspaces;" << std::endl; | ||
| 273 | + ss << R"( std::vector<int64_t> parsed_block_dims; parsed_tilings.reserve(count); | ||
| 274 | + for (uint32_t i = 0; i < count; ++i) { | ||
| 275 | + uint64_t repr_len = 0; int64_t workspace = 0; int64_t block_dim = 0; uint64_t tiling_hash = 0; | ||
| 276 | + AutofuseTilingData tiling_data = {}; | ||
| 277 | + if (!ReadPgoResultValue(data, offset, repr_len) || !ReadPgoResultValue(data, offset, workspace) || | ||
| 278 | + !ReadPgoResultValue(data, offset, block_dim) || !ReadPgoResultValue(data, offset, tiling_hash) || | ||
| 279 | + !ReadPgoResultValue(data, offset, tiling_data) || repr_len == 0U || repr_len > 16U * 1024U * 1024U || | ||
| 280 | + repr_len > data.size() - offset || workspace < 0 || block_dim <= 0 || block_dim > UINT32_MAX) { return false; } | ||
| 281 | + const std::string repr(reinterpret_cast<const char *>(data.data() + offset), repr_len); offset += repr_len; | ||
| 282 | + if (HashProxyTiling(&tiling_data, sizeof(tiling_data)) != tiling_hash || | ||
| 283 | + GetTilingDataRepr(&tiling_data) != repr || static_cast<int64_t>(GetWorkspaceSize(tiling_data)) != workspace || | ||
| 284 | + static_cast<int64_t>(tiling_data.get_block_dim()) != block_dim) { return false; } | ||
| 285 | + parsed_tilings.push_back(tiling_data); parsed_workspaces.push_back(workspace); parsed_block_dims.push_back(block_dim); | ||
| 286 | + } | ||
| 287 | + if (offset != data.size()) { return false; } | ||
| 288 | + tiling_datas.swap(parsed_tilings); workspaces.swap(parsed_workspaces); block_dims.swap(parsed_block_dims); return true; | ||
| 289 | +} | ||
| 290 | +)"; | ||
| 291 | +} | ||
| 292 | + | ||
| 293 | +namespace { | ||
| 294 | + | ||
| 295 | +void AppendInductorPgoResultPathAndWait(std::stringstream &ss) { | ||
| 296 | + ss << R"( | ||
| 297 | +bool MakeInductorPgoResultPath(std::string &path) { | ||
| 298 | + char result_dir_template[] = "/tmp/autofuse_inductor_pgo_XXXXXX"; | ||
| 299 | + if (mkdtemp(result_dir_template) == nullptr) { return false; } | ||
| 300 | + path = std::string(result_dir_template) + "/result.bin"; return true; | ||
| 301 | +} | ||
| 302 | + | ||
| 303 | +std::string PgoParentPath(const std::string &path) { | ||
| 304 | + const auto pos = path.find_last_of('/'); | ||
| 305 | + return pos == std::string::npos ? "." : path.substr(0U, pos); | ||
| 306 | +} | ||
| 307 | + | ||
| 308 | +void RemoveInductorPgoResultPath(const std::string &path) { | ||
| 309 | + unlink(path.c_str()); unlink((path + ".tmp").c_str()); | ||
| 310 | + rmdir(PgoParentPath(path).c_str()); | ||
| 311 | +} | ||
| 312 | + | ||
| 313 | +int WaitInductorPgoRunner(pid_t pid) { | ||
| 314 | + constexpr auto timeout = std::chrono::seconds(AUTOFUSE_PGO_RUNNER_TIMEOUT_SECONDS); | ||
| 315 | + const auto deadline = std::chrono::steady_clock::now() + timeout; | ||
| 316 | + int status = 0; | ||
| 317 | + while (std::chrono::steady_clock::now() < deadline) { | ||
| 318 | + const pid_t wait_ret = waitpid(pid, &status, WNOHANG); | ||
| 319 | + if (wait_ret == pid) { return WIFEXITED(status) && WEXITSTATUS(status) == 0 ? 0 : -1; } | ||
| 320 | + if (wait_ret < 0 && errno != EINTR) { return -1; } | ||
| 321 | + std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||
| 322 | + } | ||
| 323 | + kill(pid, SIGKILL); while (waitpid(pid, &status, 0) < 0 && errno == EINTR) {} | ||
| 324 | + return -1; | ||
| 325 | +} | ||
| 326 | +)"; | ||
| 327 | +} | ||
| 328 | + | ||
| 329 | +void AppendInductorPgoRunnerEnvironment(std::stringstream &ss) { | ||
| 330 | + ss << R"( | ||
| 331 | +std::vector<std::string> BuildInductorPgoRunnerEnv(const std::string &ld_preload) { | ||
| 332 | + std::vector<std::string> env_strings; | ||
| 333 | + bool found_ld_preload = false; | ||
| 334 | + for (char **env = environ; env != nullptr && *env != nullptr; ++env) { | ||
| 335 | + std::string item(*env); | ||
| 336 | + if (item.rfind("LD_PRELOAD=", 0U) != 0U) { | ||
| 337 | + env_strings.push_back(std::move(item)); | ||
| 338 | + continue; | ||
| 339 | + } | ||
| 340 | + found_ld_preload = true; | ||
| 341 | + if (ld_preload.empty()) { | ||
| 342 | + env_strings.push_back(std::move(item)); | ||
| 343 | + continue; | ||
| 344 | + } | ||
| 345 | + const std::string old_preload = item.substr(std::strlen("LD_PRELOAD=")); | ||
| 346 | + env_strings.push_back("LD_PRELOAD=" + ld_preload + (old_preload.empty() ? "" : ":" + old_preload)); | ||
| 347 | + } | ||
| 348 | + if (!found_ld_preload && !ld_preload.empty()) { | ||
| 349 | + env_strings.push_back("LD_PRELOAD=" + ld_preload); | ||
| 350 | + } | ||
| 351 | + return env_strings; | ||
| 352 | +} | ||
| 353 | + | ||
| 354 | +std::vector<char *> BuildInductorPgoEnvp(std::vector<std::string> &env_strings) { | ||
| 355 | + std::vector<char *> envp; | ||
| 356 | + envp.reserve(env_strings.size() + 1U); | ||
| 357 | + for (auto &env : env_strings) { | ||
| 358 | + envp.push_back(const_cast<char *>(env.c_str())); | ||
| 359 | + } | ||
| 360 | + envp.push_back(nullptr); | ||
| 361 | + return envp; | ||
| 362 | +} | ||
| 363 | +)"; | ||
| 364 | +} | ||
| 365 | + | ||
| 366 | +void AppendInductorPgoRunnerSpawn(std::stringstream &ss) { | ||
| 367 | + ss << R"( | ||
| 368 | +int SpawnInductorPgoRunner(const InductorPgoArtifacts &artifacts, int32_t device_id, const ResLimit &limit, | ||
| 369 | + int64_t topn, const std::string &result_path) { | ||
| 370 | + const std::string device = std::to_string(device_id); const std::string aiv = std::to_string(limit.aiv_num); | ||
| 371 | + const std::string ub = std::to_string(limit.ub_size); const std::string topn_arg = std::to_string(topn); | ||
| 372 | + std::vector<char *> argv = {const_cast<char *>(artifacts.runner.c_str()), const_cast<char *>(device.c_str()), | ||
| 373 | + const_cast<char *>(aiv.c_str()), const_cast<char *>(ub.c_str()), const_cast<char *>("autofuse"), | ||
| 374 | + const_cast<char *>(artifacts.tiling_so.c_str()), const_cast<char *>(artifacts.kernel.c_str()), | ||
| 375 | + const_cast<char *>(result_path.c_str()), const_cast<char *>(topn_arg.c_str()), nullptr}; | ||
| 376 | + auto env_strings = BuildInductorPgoRunnerEnv(artifacts.ld_preload); | ||
| 377 | + auto envp = BuildInductorPgoEnvp(env_strings); | ||
| 378 | + pid_t pid = -1; | ||
| 379 | + const int spawn_ret = posix_spawn(&pid, artifacts.runner.c_str(), nullptr, nullptr, argv.data(), envp.data()); | ||
| 380 | + return spawn_ret == 0 ? WaitInductorPgoRunner(pid) : -1; | ||
| 381 | +} | ||
| 382 | +)"; | ||
| 383 | +} | ||
| 384 | + | ||
| 385 | +} // namespace | ||
| 386 | + | ||
| 387 | +void TilingLib::GenInductorPgoProxySpawn(std::stringstream &ss) const { | ||
| 388 | + AppendInductorPgoResultPathAndWait(ss); | ||
| 389 | + AppendInductorPgoRunnerEnvironment(ss); | ||
| 390 | + AppendInductorPgoRunnerSpawn(ss); | ||
| 391 | +} | ||
| 392 | + | ||
| 393 | +void TilingLib::GenInductorPgoProxyFunction(std::stringstream &ss, const std::string &tiling) const { | ||
| 394 | + ss << "int64_t FallbackToInductorModeledTopn(" | ||
| 395 | + << "const std::vector<std::map<std::string, std::string>> &input_configs, int64_t topn, " | ||
| 396 | + << "const ResLimit &limit, std::vector<" << tiling | ||
| 397 | + << "> &tiling_datas, std::vector<int64_t> &workspaces, std::vector<int64_t> &block_dims) {" << std::endl; | ||
| 398 | + ss << R"( OP_LOGW(OP_NAME, "Inductor PGO failed, fallback to modeled TopN"); | ||
| 399 | + ResLimit modeled_limit = limit; | ||
| 400 | + return inductor_pgo_fallback::GenerateModeledFallbackTopnSolutions( | ||
| 401 | + input_configs, topn, tiling_datas, workspaces, block_dims, &modeled_limit); | ||
| 402 | +} | ||
| 403 | + | ||
| 404 | +)"; | ||
| 405 | + ss << "static int64_t RunInductorPgoProxy(" | ||
| 406 | + << "const std::vector<std::map<std::string, std::string>> &input_configs, int64_t topn, " | ||
| 407 | + << "std::vector<" << tiling << "> &tiling_datas, std::vector<int64_t> &workspaces, " | ||
| 408 | + << "std::vector<int64_t> &block_dims, ResLimit *res_limit) {" << std::endl; | ||
| 409 | + ss << R"( tiling_datas.clear(); workspaces.clear(); block_dims.clear(); | ||
| 410 | + if (topn <= 0 || topn > kMaxPgoTopn) { return -1; } | ||
| 411 | + const ResLimit *limit = (res_limit == nullptr || res_limit->aiv_num == 0U) ? &g_no_limit_res : res_limit; | ||
| 412 | + if (limit->aiv_num == 0U || limit->ub_size <= 256U) { return -1; } | ||
| 413 | + InductorPgoArtifacts artifacts; | ||
| 414 | + if (!ResolveInductorPgoArtifacts(artifacts)) { | ||
| 415 | + return FallbackToInductorModeledTopn(input_configs, topn, *limit, tiling_datas, workspaces, block_dims); | ||
| 416 | + } | ||
| 417 | + int32_t device_id = -1; | ||
| 418 | + if (aclrtGetDevice(&device_id) != ACL_SUCCESS || device_id < 0) { | ||
| 419 | + OP_LOGW(OP_NAME, "Get current device failed for Inductor PGO"); | ||
| 420 | + return FallbackToInductorModeledTopn(input_configs, topn, *limit, tiling_datas, workspaces, block_dims); | ||
| 421 | + } | ||
| 422 | + std::string result_path; | ||
| 423 | + if (!MakeInductorPgoResultPath(result_path)) { | ||
| 424 | + OP_LOGW(OP_NAME, "Create Inductor PGO result path failed"); | ||
| 425 | + return FallbackToInductorModeledTopn(input_configs, topn, *limit, tiling_datas, workspaces, block_dims); | ||
| 426 | + } | ||
| 427 | + const int runner_ret = SpawnInductorPgoRunner(artifacts, device_id, *limit, topn, result_path); | ||
| 428 | + const bool parsed = runner_ret == 0 && | ||
| 429 | + ParseInductorPgoResult(result_path, topn, tiling_datas, workspaces, block_dims); | ||
| 430 | + RemoveInductorPgoResultPath(result_path); | ||
| 431 | + if (!parsed) { | ||
| 432 | + OP_LOGW(OP_NAME, "Inductor PGO runner or result parsing failed, runner_ret=%d", runner_ret); | ||
| 433 | + return FallbackToInductorModeledTopn(input_configs, topn, *limit, tiling_datas, workspaces, block_dims); | ||
| 434 | + } | ||
| 435 | + return 0; | ||
| 436 | +} | ||
| 437 | +} // namespace | ||
| 438 | +)"; | ||
| 439 | +} | ||
| 440 | + | ||
| 441 | +void TilingLib::GenInductorPgoProxyEntry(std::stringstream &ss, const std::string &tiling) const { | ||
| 442 | + GenInductorPgoProxyIncludes(ss, tiling); | ||
| 443 | + GenInductorPgoProxyFileHelpers(ss); | ||
| 444 | + GenInductorPgoProxySha256(ss); | ||
| 445 | + GenInductorPgoProxyManifest(ss); | ||
| 446 | + GenInductorPgoProxyResultParser(ss, tiling); | ||
| 447 | + GenInductorPgoProxySpawn(ss); | ||
| 448 | + GenInductorPgoProxyFunction(ss, tiling); | ||
| 449 | + ss << "extern \"C\" int64_t GenerateTopnSolutions(" | ||
| 450 | + << "const std::vector<std::map<std::string, std::string>> &input_configs, int64_t topn, " | ||
| 451 | + << "std::vector<" << tiling << "> &tiling_datas, std::vector<int64_t> &workspaces, " | ||
| 452 | + << "std::vector<int64_t> &block_dims, ResLimit *res_limit) {" << std::endl; | ||
| 453 | + ss << " return RunInductorPgoProxy(input_configs, topn, tiling_datas, workspaces, block_dims," << std::endl; | ||
| 454 | + ss << " res_limit);" << std::endl; | ||
| 455 | + ss << "}" << std::endl; | ||
| 456 | +} | ||
| 457 | + | ||
| 458 | +} // namespace codegen | ||
| @@ -0,0 +1,412 @@ | |||
| 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 | +namespace codegen { | ||
| 14 | + | ||
| 15 | +void TilingLib::GenInductorPgoHostLoader(std::stringstream &ss) const { | ||
| 16 | + ss << R"( | ||
| 17 | +void *g_pgo_tiling_handle = nullptr; | ||
| 18 | +GenerateMeasuredTopnSolutionsType generate_measured_topn_solutions_fn = nullptr; | ||
| 19 | +SetTopnPgoContextType set_topn_pgo_context_fn = nullptr; | ||
| 20 | +ClearTopnPgoContextType clear_topn_pgo_context_fn = nullptr; | ||
| 21 | +GetTilingDataReprType get_tiling_data_repr_fn = nullptr; | ||
| 22 | + | ||
| 23 | +template <typename T> | ||
| 24 | +bool LoadInductorPgoSymbol(T &target, const char *name) { | ||
| 25 | + target = reinterpret_cast<T>(dlsym(g_pgo_tiling_handle, name)); | ||
| 26 | + if (target == nullptr) { DLOGE("dlsym %s failed: %s", name, dlerror()); return false; } | ||
| 27 | + return true; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +int LoadInductorPgoHost(const InductorPgoRunnerArgs &args) { | ||
| 31 | + g_pgo_tiling_handle = dlopen(args.tiling_file.c_str(), RTLD_NOW | RTLD_LOCAL); | ||
| 32 | + if (g_pgo_tiling_handle == nullptr) { DLOGE("dlopen tiling failed: %s", dlerror()); return FAILED; } | ||
| 33 | + bool valid = LoadInductorPgoSymbol(generate_measured_topn_solutions_fn, "GenerateMeasuredTopnSolutions") && | ||
| 34 | + LoadInductorPgoSymbol(set_topn_pgo_context_fn, "SetTopnPgoContext") && | ||
| 35 | + LoadInductorPgoSymbol(clear_topn_pgo_context_fn, "ClearTopnPgoContext") && | ||
| 36 | + LoadInductorPgoSymbol(get_tiling_data_repr_fn, "GetTilingDataRepr") && | ||
| 37 | + LoadInductorPgoSymbol(get_tiling_key_count_fn, "GetTilingKeyCount") && | ||
| 38 | + LoadInductorPgoSymbol(find_best_tiling_key_fn, "FindBestTilingKey"); | ||
| 39 | + return valid ? SUCCESS : FAILED; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +void UnloadInductorPgoHost() { | ||
| 43 | + generate_measured_topn_solutions_fn = nullptr; set_topn_pgo_context_fn = nullptr; | ||
| 44 | + clear_topn_pgo_context_fn = nullptr; get_tiling_data_repr_fn = nullptr; | ||
| 45 | + get_tiling_key_count_fn = nullptr; find_best_tiling_key_fn = nullptr; | ||
| 46 | + if (g_pgo_tiling_handle != nullptr) { dlclose(g_pgo_tiling_handle); g_pgo_tiling_handle = nullptr; } | ||
| 47 | +} | ||
| 48 | +)" << std::endl; | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +void TilingLib::GenInductorPgoResultProtocol(std::stringstream &ss) const { | ||
| 52 | + GenInductorPgoRecordWriter(ss); | ||
| 53 | + GenInductorPgoResultWriter(ss); | ||
| 54 | + GenPgoSaveTilingKey(ss); | ||
| 55 | + GenInductorPgoSearchWriter(ss); | ||
| 56 | + GenInductorPgoArgValidators(ss); | ||
| 57 | + GenInductorPgoArgParser(ss); | ||
| 58 | + GenInductorPgoContextGuard(ss); | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +void TilingLib::GenInductorPgoResultTypes(std::stringstream &ss) const { | ||
| 62 | + ss << R"( | ||
| 63 | +namespace { | ||
| 64 | +constexpr char kPgoTopnMagic[] = "AUTOFUSE_PGO_TOPN_V1"; | ||
| 65 | +constexpr size_t kPgoTopnMagicSize = 20U; | ||
| 66 | +constexpr uint32_t kPgoTopnProtocolVersion = 1U; | ||
| 67 | +constexpr uint32_t kPgoTopnProtocolFlags = 0U; | ||
| 68 | +constexpr uint32_t kPgoTopnRecordHeaderSize = 32U; | ||
| 69 | +constexpr size_t kMaxPgoReprSize = 16U * 1024U * 1024U; | ||
| 70 | +constexpr int64_t kMaxPgoTopn = 1024; | ||
| 71 | +static_assert(sizeof(kPgoTopnMagic) - 1U == kPgoTopnMagicSize); | ||
| 72 | +static_assert(std::is_trivially_copyable<AutofuseTilingData>::value); | ||
| 73 | +static_assert(std::is_standard_layout<AutofuseTilingData>::value); | ||
| 74 | + | ||
| 75 | +using GenerateMeasuredTopnSolutionsType = int64_t (*)( | ||
| 76 | + const std::vector<std::map<std::string, std::string>> &, int64_t, | ||
| 77 | + std::vector<AutofuseTilingData> &, std::vector<int64_t> &, std::vector<int64_t> &, ResLimit *); | ||
| 78 | +using InductorPgoProfilingCallback = long int (*)( | ||
| 79 | + PgoTensorArgs *, void *, uint32_t, AutofuseTilingData *, double *); | ||
| 80 | +using InductorPgoProfilingBatchCallback = long int (*)( | ||
| 81 | + PgoTensorArgs *, void *, uint32_t, std::vector<AutofuseTilingDataPerf> *); | ||
| 82 | +using SetTopnPgoContextType = int64_t (*)( | ||
| 83 | + PgoTensorArgs *, void *, InductorPgoProfilingCallback, InductorPgoProfilingBatchCallback, | ||
| 84 | + std::vector<AutofuseTilingDataPerf> *); | ||
| 85 | +using ClearTopnPgoContextType = void (*)(void); | ||
| 86 | +using GetTilingDataReprType = std::string (*)(const AutofuseTilingData *); | ||
| 87 | + | ||
| 88 | +struct InductorPgoRunnerArgs { | ||
| 89 | + int32_t device_id = -1; | ||
| 90 | + uint32_t aiv_num = 0; | ||
| 91 | + uint32_t ub_size = 0; | ||
| 92 | + std::string kernel_name; | ||
| 93 | + std::string tiling_file; | ||
| 94 | + std::string kernel_file; | ||
| 95 | + std::string result_file; | ||
| 96 | + int64_t topn = 0; | ||
| 97 | +}; | ||
| 98 | +)" << std::endl; | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +void TilingLib::GenInductorPgoRecordWriter(std::stringstream &ss) const { | ||
| 102 | + ss << R"( | ||
| 103 | +template <typename T> | ||
| 104 | +bool WritePgoValue(std::ofstream &out, const T &value) { | ||
| 105 | + out.write(reinterpret_cast<const char *>(&value), sizeof(value)); | ||
| 106 | + return out.good(); | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +uint64_t HashPgoBytes(const void *data, size_t size) { | ||
| 110 | + const auto *bytes = static_cast<const uint8_t *>(data); | ||
| 111 | + uint64_t hash = 1469598103934665603ULL; | ||
| 112 | + for (size_t i = 0; i < size; ++i) { hash = (hash ^ bytes[i]) * 1099511628211ULL; } | ||
| 113 | + return hash; | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +bool WritePgoRecord(std::ofstream &out, const AutofuseTilingData &tiling_data, | ||
| 117 | + int64_t workspace, int64_t block_dim) { | ||
| 118 | + if (get_tiling_data_repr_fn == nullptr) { return false; } | ||
| 119 | + const std::string repr = get_tiling_data_repr_fn(&tiling_data); | ||
| 120 | + if (repr.empty() || repr.size() > kMaxPgoReprSize || workspace < 0 || block_dim <= 0 || | ||
| 121 | + block_dim > UINT32_MAX) { | ||
| 122 | + return false; | ||
| 123 | + } | ||
| 124 | + const uint64_t repr_len = repr.size(); | ||
| 125 | + const uint64_t tiling_hash = HashPgoBytes(&tiling_data, sizeof(tiling_data)); | ||
| 126 | + return WritePgoValue(out, repr_len) && WritePgoValue(out, workspace) && | ||
| 127 | + WritePgoValue(out, block_dim) && WritePgoValue(out, tiling_hash) && | ||
| 128 | + WritePgoValue(out, tiling_data) && | ||
| 129 | + static_cast<bool>(out.write(repr.data(), static_cast<std::streamsize>(repr.size()))); | ||
| 130 | +} | ||
| 131 | +)" << std::endl; | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +void TilingLib::GenInductorPgoResultWriter(std::stringstream &ss) const { | ||
| 135 | + ss << R"( | ||
| 136 | +int WritePgoTopnResult(const std::string &path, const std::vector<AutofuseTilingData> &tiling_datas, | ||
| 137 | + const std::vector<int64_t> &workspaces, const std::vector<int64_t> &block_dims) { | ||
| 138 | + if (tiling_datas.empty() || tiling_datas.size() != workspaces.size() || tiling_datas.size() != block_dims.size() || | ||
| 139 | + tiling_datas.size() > std::numeric_limits<uint32_t>::max()) { | ||
| 140 | + return FAILED; | ||
| 141 | + } | ||
| 142 | + const std::string tmp_path = path + ".tmp"; | ||
| 143 | + std::ofstream out(tmp_path, std::ios::binary | std::ios::trunc); | ||
| 144 | + const uint32_t count = static_cast<uint32_t>(tiling_datas.size()); | ||
| 145 | + const uint32_t tiling_size = sizeof(AutofuseTilingData); | ||
| 146 | + out.write(kPgoTopnMagic, kPgoTopnMagicSize); | ||
| 147 | + if (!out.good() || !WritePgoValue(out, kPgoTopnProtocolVersion) || !WritePgoValue(out, kPgoTopnProtocolFlags) || | ||
| 148 | + !WritePgoValue(out, count) || !WritePgoValue(out, tiling_size) || | ||
| 149 | + !WritePgoValue(out, kPgoTopnRecordHeaderSize)) { | ||
| 150 | + out.close(); std::remove(tmp_path.c_str()); return FAILED; | ||
| 151 | + } | ||
| 152 | + for (size_t i = 0; i < tiling_datas.size(); ++i) { | ||
| 153 | + if (!WritePgoRecord(out, tiling_datas[i], workspaces[i], block_dims[i])) { | ||
| 154 | + out.close(); std::remove(tmp_path.c_str()); return FAILED; | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + out.flush(); | ||
| 158 | + if (!out.good()) { out.close(); std::remove(tmp_path.c_str()); return FAILED; } | ||
| 159 | + out.close(); | ||
| 160 | + const int fd = ::open(tmp_path.c_str(), O_RDONLY); | ||
| 161 | + if (fd < 0 || ::fsync(fd) != 0) { | ||
| 162 | + if (fd >= 0) { ::close(fd); } | ||
| 163 | + std::remove(tmp_path.c_str()); return FAILED; | ||
| 164 | + } | ||
| 165 | + ::close(fd); | ||
| 166 | + if (std::rename(tmp_path.c_str(), path.c_str()) != 0) { std::remove(tmp_path.c_str()); return FAILED; } | ||
| 167 | + return SUCCESS; | ||
| 168 | +} | ||
| 169 | +)" << std::endl; | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +void TilingLib::GenInductorPgoSearchWriter(std::stringstream &ss) const { | ||
| 173 | + ss << R"( | ||
| 174 | +std::string PgoParentPath(const std::string &path) { | ||
| 175 | + const auto pos = path.find_last_of('/'); | ||
| 176 | + return pos == std::string::npos ? "." : path.substr(0U, pos); | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +int PublishPgoSearchFile(const std::string &tmp_path, const std::string &path) { | ||
| 180 | + const int fd = ::open(tmp_path.c_str(), O_RDONLY); | ||
| 181 | + if (fd < 0 || ::fsync(fd) != 0) { | ||
| 182 | + if (fd >= 0) { ::close(fd); } | ||
| 183 | + std::remove(tmp_path.c_str()); | ||
| 184 | + return FAILED; | ||
| 185 | + } | ||
| 186 | + ::close(fd); | ||
| 187 | + if (std::rename(tmp_path.c_str(), path.c_str()) != 0) { | ||
| 188 | + std::remove(tmp_path.c_str()); | ||
| 189 | + return FAILED; | ||
| 190 | + } | ||
| 191 | + return SUCCESS; | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +int WritePgoSearchResult(const InductorPgoRunnerArgs &args, | ||
| 195 | + const std::vector<AutofuseTilingDataPerf> &measured_candidates) { | ||
| 196 | + if (measured_candidates.empty()) { return FAILED; } | ||
| 197 | + const std::string path = PgoParentPath(args.kernel_file) + "/" + std::string(PGO_GRAPH_NAME) + "_search.txt"; | ||
| 198 | + const std::string tmp_path = path + ".tmp." + std::to_string(getpid()); | ||
| 199 | + std::ofstream out(tmp_path, std::ios::out | std::ios::trunc); | ||
| 200 | + if (!out.is_open()) { return FAILED; } | ||
| 201 | + for (const auto &candidate : measured_candidates) { | ||
| 202 | + PgoSaveTilingKey(candidate.tiling_data, candidate.best_perf, out); | ||
| 203 | + if (!out.good()) { out.close(); std::remove(tmp_path.c_str()); return FAILED; } | ||
| 204 | + } | ||
| 205 | + out.flush(); | ||
| 206 | + if (!out.good()) { out.close(); std::remove(tmp_path.c_str()); return FAILED; } | ||
| 207 | + out.close(); | ||
| 208 | + return PublishPgoSearchFile(tmp_path, path); | ||
| 209 | +} | ||
| 210 | +)" << std::endl; | ||
| 211 | +} | ||
| 212 | + | ||
| 213 | +void TilingLib::GenInductorPgoArgValidators(std::stringstream &ss) const { | ||
| 214 | + ss << R"( | ||
| 215 | +bool ParseRunnerInteger(const char *text, int64_t min_value, int64_t max_value, int64_t &value) { | ||
| 216 | + if (text == nullptr || *text == '\0') { return false; } | ||
| 217 | + errno = 0; | ||
| 218 | + char *end = nullptr; | ||
| 219 | + const long long parsed = std::strtoll(text, &end, 10); | ||
| 220 | + if (errno != 0 || end == text || *end != '\0' || parsed < min_value || parsed > max_value) { return false; } | ||
| 221 | + value = static_cast<int64_t>(parsed); | ||
| 222 | + return true; | ||
| 223 | +} | ||
| 224 | + | ||
| 225 | +bool IsValidKernelName(const std::string &name) { | ||
| 226 | + return !name.empty() && std::all_of(name.begin(), name.end(), [](unsigned char c) { | ||
| 227 | + return std::isalnum(c) != 0 || c == '_'; | ||
| 228 | + }); | ||
| 229 | +} | ||
| 230 | +)" << std::endl; | ||
| 231 | +} | ||
| 232 | + | ||
| 233 | +void TilingLib::GenInductorPgoArgParser(std::stringstream &ss) const { | ||
| 234 | + ss << R"( | ||
| 235 | +int ParseInductorPgoRunnerArgs(int argc, char *argv[], InductorPgoRunnerArgs &args) { | ||
| 236 | + if (argc != 9) { | ||
| 237 | + DLOGE("Usage: %s <device_id> <aiv_num> <ub_size> <kernel_name> <tiling_so> <dynamic_kernel> <result> <topn>", | ||
| 238 | + argv[0]); | ||
| 239 | + return FAILED; | ||
| 240 | + } | ||
| 241 | + int64_t device_id = 0; | ||
| 242 | + int64_t aiv_num = 0; | ||
| 243 | + int64_t ub_size = 0; | ||
| 244 | + if (!ParseRunnerInteger(argv[1], 0, INT32_MAX, device_id) || | ||
| 245 | + !ParseRunnerInteger(argv[2], 1, UINT32_MAX, aiv_num) || | ||
| 246 | + !ParseRunnerInteger(argv[3], 257, UINT32_MAX, ub_size) || | ||
| 247 | + !ParseRunnerInteger(argv[8], 1, kMaxPgoTopn, args.topn)) { | ||
| 248 | + DLOGE("invalid numeric runner argument"); return FAILED; | ||
| 249 | + } | ||
| 250 | + args.device_id = static_cast<int32_t>(device_id); | ||
| 251 | + args.aiv_num = static_cast<uint32_t>(aiv_num); | ||
| 252 | + args.ub_size = static_cast<uint32_t>(ub_size); | ||
| 253 | + args.kernel_name = argv[4]; args.tiling_file = argv[5]; | ||
| 254 | + args.kernel_file = argv[6]; args.result_file = argv[7]; | ||
| 255 | + if (!IsValidKernelName(args.kernel_name) || args.tiling_file.empty() || args.kernel_file.empty() || | ||
| 256 | + args.result_file.empty() || ::access(args.tiling_file.c_str(), R_OK) != 0 || | ||
| 257 | + ::access(args.kernel_file.c_str(), R_OK) != 0) { | ||
| 258 | + DLOGE("invalid runner path or kernel name"); return FAILED; | ||
| 259 | + } | ||
| 260 | + return SUCCESS; | ||
| 261 | +} | ||
| 262 | +)" << std::endl; | ||
| 263 | +} | ||
| 264 | + | ||
| 265 | +void TilingLib::GenInductorPgoContextGuard(std::stringstream &ss) const { | ||
| 266 | + ss << R"( | ||
| 267 | +class PgoContextGuard { | ||
| 268 | + public: | ||
| 269 | + explicit PgoContextGuard(std::vector<AutofuseTilingDataPerf> *measured_candidates) { | ||
| 270 | + if (set_topn_pgo_context_fn != nullptr) { | ||
| 271 | + valid_ = set_topn_pgo_context_fn(&g_pgo_tensor_args, g_stream, PGOGetProfiling, PGOGetProfilingBatch, | ||
| 272 | + measured_candidates) == 0; | ||
| 273 | + } | ||
| 274 | + } | ||
| 275 | + ~PgoContextGuard() { | ||
| 276 | + if (valid_ && clear_topn_pgo_context_fn != nullptr) { clear_topn_pgo_context_fn(); } | ||
| 277 | + } | ||
| 278 | + bool IsValid() const { return valid_; } | ||
| 279 | + PgoContextGuard(const PgoContextGuard &) = delete; | ||
| 280 | + PgoContextGuard &operator=(const PgoContextGuard &) = delete; | ||
| 281 | + private: | ||
| 282 | + bool valid_ = false; | ||
| 283 | +}; | ||
| 284 | +} // namespace | ||
| 285 | +)" << std::endl; | ||
| 286 | +} | ||
| 287 | + | ||
| 288 | +void TilingLib::GenInductorPgoRuntime(const ascir::FusedScheduledResult &fused_schedule_result, | ||
| 289 | + std::stringstream &ss) const { | ||
| 290 | + GenInductorPgoAclRuntime(ss); | ||
| 291 | + GenInductorPgoMemoryRuntime(fused_schedule_result, ss); | ||
| 292 | + GenInductorPgoDeinitRuntime(fused_schedule_result, ss); | ||
| 293 | +} | ||
| 294 | + | ||
| 295 | +void TilingLib::GenInductorPgoAclRuntime(std::stringstream &ss) const { | ||
| 296 | + ss << R"( | ||
| 297 | +int InitInductorPgoAcl(const InductorPgoRunnerArgs &args) { | ||
| 298 | + g_res_limit.aiv_num = args.aiv_num; | ||
| 299 | + g_res_limit.ub_size = args.ub_size; | ||
| 300 | + auto ret = aclInit(nullptr); | ||
| 301 | + if (ret != ACL_SUCCESS) { DLOGE("acl init failed, ERROR: %d", ret); return FAILED; } | ||
| 302 | + g_acl_initialized = true; | ||
| 303 | + ret = aclrtSetDevice(args.device_id); | ||
| 304 | + if (ret != ACL_SUCCESS) { DLOGE("acl set device failed, ERROR: %d", ret); return FAILED; } | ||
| 305 | + g_device_id = args.device_id; | ||
| 306 | + g_device_set = true; | ||
| 307 | + ret = aclrtCreateStream(&g_stream); | ||
| 308 | + if (ret != ACL_SUCCESS) { DLOGE("acl create stream failed, ERROR: %d", ret); return FAILED; } | ||
| 309 | + return SUCCESS; | ||
| 310 | +} | ||
| 311 | +)" << std::endl; | ||
| 312 | +} | ||
| 313 | + | ||
| 314 | +void TilingLib::GenInductorPgoMemoryRuntime(const ascir::FusedScheduledResult &fused_schedule_result, | ||
| 315 | + std::stringstream &ss) const { | ||
| 316 | + ss << R"( | ||
| 317 | +int InitInductorPgoMemory() { | ||
| 318 | + aclError ret = ACL_SUCCESS; | ||
| 319 | +)"; | ||
| 320 | + ss << PGOSearchTensorMallocDef(fused_schedule_result); | ||
| 321 | + ss << PGOSearchTensorArgsUpdateDef(fused_schedule_result); | ||
| 322 | + ss << R"( ret = LaunchParamsInit(&g_pgo_tensor_args); | ||
| 323 | + if (ret != ACL_SUCCESS) { return FAILED; } | ||
| 324 | + return SUCCESS; | ||
| 325 | +} | ||
| 326 | +)" << std::endl; | ||
| 327 | +} | ||
| 328 | + | ||
| 329 | +void TilingLib::GenInductorPgoDeinitRuntime(const ascir::FusedScheduledResult &fused_schedule_result, | ||
| 330 | + std::stringstream &ss) const { | ||
| 331 | + ss << R"( | ||
| 332 | +void DeInitInductorPgoMemory() { | ||
| 333 | + aclError ret = ACL_SUCCESS; | ||
| 334 | + if (g_workspace != nullptr) { | ||
| 335 | + ret = aclrtFree(g_workspace); | ||
| 336 | + if (ret != ACL_SUCCESS) { DLOGW("acl free workspace failed, ERROR: %d", ret); } | ||
| 337 | + g_workspace = nullptr; | ||
| 338 | + } | ||
| 339 | + PgoBinaryDeInit(); | ||
| 340 | + LaunchParamsDeInit(); | ||
| 341 | +)"; | ||
| 342 | + ss << PGOSearchTensorFreeDef(fused_schedule_result); | ||
| 343 | + ss << R"(} | ||
| 344 | + | ||
| 345 | +void DeInitInductorPgoAcl() { | ||
| 346 | + if (g_stream != nullptr) { | ||
| 347 | + auto ret = aclrtDestroyStream(g_stream); | ||
| 348 | + if (ret != ACL_SUCCESS) { DLOGW("acl destroy stream failed, ERROR: %d", ret); } | ||
| 349 | + g_stream = nullptr; | ||
| 350 | + } | ||
| 351 | + if (g_device_set) { | ||
| 352 | + auto ret = aclrtResetDevice(g_device_id); | ||
| 353 | + if (ret != ACL_SUCCESS) { DLOGW("acl reset device failed, ERROR: %d", ret); } | ||
| 354 | + g_device_set = false; | ||
| 355 | + } | ||
| 356 | + if (g_acl_initialized) { | ||
| 357 | + auto ret = aclFinalize(); | ||
| 358 | + if (ret != ACL_SUCCESS) { DLOGW("acl finalize failed, ERROR: %d", ret); } | ||
| 359 | + g_acl_initialized = false; | ||
| 360 | + } | ||
| 361 | +} | ||
| 362 | + | ||
| 363 | +void DeInitInductorPgoRuntime() { | ||
| 364 | + DeInitInductorPgoMemory(); | ||
| 365 | + DeInitInductorPgoAcl(); | ||
| 366 | +} | ||
| 367 | +)" << std::endl; | ||
| 368 | +} | ||
| 369 | + | ||
| 370 | +void TilingLib::GenInductorPgoMain(std::stringstream &ss) const { | ||
| 371 | + ss << R"( | ||
| 372 | +int RunInductorPgo(const InductorPgoRunnerArgs &args) { | ||
| 373 | + std::vector<AutofuseTilingDataPerf> measured_candidates; | ||
| 374 | + PgoContextGuard context_guard(&measured_candidates); | ||
| 375 | + if (!context_guard.IsValid() || generate_measured_topn_solutions_fn == nullptr) { return FAILED; } | ||
| 376 | + std::vector<AutofuseTilingData> tiling_datas; | ||
| 377 | + std::vector<int64_t> workspaces; | ||
| 378 | + std::vector<int64_t> block_dims; | ||
| 379 | + const auto ret = generate_measured_topn_solutions_fn( | ||
| 380 | + {}, args.topn, tiling_datas, workspaces, block_dims, &g_res_limit); | ||
| 381 | + if (ret != 0) { DLOGE("GenerateMeasuredTopnSolutions failed, ERROR: %" PRId64, ret); return FAILED; } | ||
| 382 | + if (tiling_datas.empty()) { return FAILED; } | ||
| 383 | + if (WritePgoTopnResult(args.result_file, tiling_datas, workspaces, block_dims) != SUCCESS) { | ||
| 384 | + DLOGE("Write PGO TopN result failed"); return FAILED; | ||
| 385 | + } | ||
| 386 | + if (WritePgoSearchResult(args, measured_candidates) != SUCCESS) { | ||
| 387 | + DLOGW("Write PGO search result failed"); | ||
| 388 | + } | ||
| 389 | + return SUCCESS; | ||
| 390 | +} | ||
| 391 | + | ||
| 392 | +int main(int argc, char *argv[]) { | ||
| 393 | + InductorPgoRunnerArgs args; | ||
| 394 | + if (ParseInductorPgoRunnerArgs(argc, argv, args) != SUCCESS) { return FAILED; } | ||
| 395 | + g_kernel_o_file = args.kernel_file; | ||
| 396 | + DLOGI("execute info: device_id: %d, graph_name: %s", args.device_id, args.kernel_name.c_str()); | ||
| 397 | + const char *tmp_dir = std::getenv("TMPDIR"); | ||
| 398 | + const std::string lock_dir = (tmp_dir != nullptr && *tmp_dir != '\0') ? tmp_dir : "/tmp"; | ||
| 399 | + g_npu_lock_file = lock_dir + "/autofuse_pgo_npu_lock_" + std::to_string(args.device_id) + ".lock"; | ||
| 400 | + CardLock lock(g_npu_lock_file.c_str()); | ||
| 401 | + int ret = LoadInductorPgoHost(args); | ||
| 402 | + if (ret == SUCCESS) { ret = InitInductorPgoAcl(args); } | ||
| 403 | + if (ret == SUCCESS) { ret = InitInductorPgoMemory(); } | ||
| 404 | + if (ret == SUCCESS) { ret = RunInductorPgo(args); } | ||
| 405 | + DeInitInductorPgoRuntime(); | ||
| 406 | + UnloadInductorPgoHost(); | ||
| 407 | + return ret; | ||
| 408 | +} | ||
| 409 | +)" << std::endl; | ||
| 410 | +} | ||
| 411 | + | ||
| 412 | +} // namespace codegen | ||
| @@ -28,6 +28,83 @@ void AppendTopnEntryInitialization(std::stringstream &ss) { | |||
| 28 | } | 28 | } |
| 29 | } // namespace | 29 | } // namespace |
| 30 | 30 | ||
| 31 | +void TilingLib::GenInductorTopnSources(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 32 | + std::map<std::string, std::string> &tiling_file_name_to_content) const { | ||
| 33 | + ss << GenCandidateSolutionProtocolForInductor("AutofuseTilingData") << std::endl; | ||
| 34 | + ss << (enable_autofuse_pgo_ ? GenMeasuredTopnSelectorHelpersForInductor() : GenTopnSelectorHelpersForInductor()) | ||
| 35 | + << std::endl; | ||
| 36 | + if (!enable_autofuse_pgo_) { | ||
| 37 | + ss << GenInductorConfigParserForInductor() << std::endl; | ||
| 38 | + } | ||
| 39 | + ss << GenGetTilingKeyCount(fused_schedule_result) << std::endl; | ||
| 40 | + if (!ascgen_utils::IsSingleGroup(fused_schedule_result)) { | ||
| 41 | + ss << GenUpdateCurPerfAndBlockByGroupHelper() << std::endl; | ||
| 42 | + } | ||
| 43 | + if (enable_autofuse_pgo_) { | ||
| 44 | + ss << GenFindBestTilingKeyFunc(fused_schedule_result, "AutofuseTilingData") << std::endl; | ||
| 45 | + ss << GenTopnPgoContextAbi() << std::endl; | ||
| 46 | + } else { | ||
| 47 | + ss << GenEvaluateModeledPerfForInductor("AutofuseTilingData", fused_schedule_result) << std::endl; | ||
| 48 | + } | ||
| 49 | + ss << GenGetTopnSolutionsFuncForInductor(fused_schedule_result, "AutofuseTilingData", enable_autofuse_pgo_) | ||
| 50 | + << std::endl; | ||
| 51 | + ss << GenGetTilingDataReprFuncForInductor(fused_schedule_result, "AutofuseTilingData") << std::endl; | ||
| 52 | + if (enable_autofuse_pgo_) { | ||
| 53 | + ss << GenModeledFallbackTopnForInductor(fused_schedule_result, "AutofuseTilingData") << std::endl; | ||
| 54 | + tiling_file_name_to_content[kPgoRunnerIdentify] = GenInductorPgoRunner(fused_schedule_result); | ||
| 55 | + } | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +std::string TilingLib::GenModeledFallbackTopnForInductor(const ascir::FusedScheduledResult &fused_schedule_result, | ||
| 59 | + const std::string &tiling) const { | ||
| 60 | + std::stringstream ss; | ||
| 61 | + ss << "namespace inductor_pgo_fallback {" << std::endl; | ||
| 62 | + ss << GenCandidateSolutionProtocolForInductor(tiling) << std::endl; | ||
| 63 | + ss << GenTopnSelectorHelpersForInductor() << std::endl; | ||
| 64 | + ss << GenInductorConfigParserForInductor() << std::endl; | ||
| 65 | + if (!ascgen_utils::IsSingleGroup(fused_schedule_result)) { | ||
| 66 | + ss << GenUpdateCurPerfAndBlockByGroupHelper() << std::endl; | ||
| 67 | + } | ||
| 68 | + ss << GenEvaluateModeledPerfForInductor(tiling, fused_schedule_result) << std::endl; | ||
| 69 | + ss << GenGetTopnSolutionsFuncForInductor(fused_schedule_result, tiling, false, | ||
| 70 | + "static int64_t GenerateModeledFallbackTopnSolutions") | ||
| 71 | + << std::endl; | ||
| 72 | + ss << "} // namespace inductor_pgo_fallback" << std::endl; | ||
| 73 | + return ss.str(); | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +std::string TilingLib::GenTopnPgoContextAbi() const { | ||
| 77 | + return R"( | ||
| 78 | +extern "C" int64_t SetTopnPgoContext(PgoTensorArgs *tensor_args, void *stream, | ||
| 79 | + ProfilingCallback single_callback, | ||
| 80 | + ProfilingBatchCallback batch_callback, | ||
| 81 | + std::vector<AutofuseTilingDataPerf> *measured_candidates) { | ||
| 82 | + auto &config = PgoConfig::Instance(); | ||
| 83 | + if (tensor_args == nullptr || stream == nullptr || single_callback == nullptr || batch_callback == nullptr || | ||
| 84 | + measured_candidates == nullptr || | ||
| 85 | + config.tensor_args != nullptr || config.stream != nullptr || config.single_callback != nullptr || | ||
| 86 | + config.batch_callback != nullptr || config.measured_candidates != nullptr) { | ||
| 87 | + return -1; | ||
| 88 | + } | ||
| 89 | + config.tensor_args = tensor_args; | ||
| 90 | + config.stream = stream; | ||
| 91 | + PgoConfig::Instance().single_callback = single_callback; | ||
| 92 | + PgoConfig::Instance().batch_callback = batch_callback; | ||
| 93 | + PgoConfig::Instance().measured_candidates = measured_candidates; | ||
| 94 | + return 0; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +extern "C" void ClearTopnPgoContext() { | ||
| 98 | + auto &config = PgoConfig::Instance(); | ||
| 99 | + config.batch_callback = nullptr; | ||
| 100 | + config.single_callback = nullptr; | ||
| 101 | + config.stream = nullptr; | ||
| 102 | + config.tensor_args = nullptr; | ||
| 103 | + config.measured_candidates = nullptr; | ||
| 104 | +} | ||
| 105 | +)"; | ||
| 106 | +} | ||
| 107 | + | ||
| 31 | void TilingLib::GenReprScheduleGroupFields(std::stringstream &ss, const ascir::ScheduleGroup &sg, | 108 | void TilingLib::GenReprScheduleGroupFields(std::stringstream &ss, const ascir::ScheduleGroup &sg, |
| 32 | const std::string &field_prefix, const std::string &emit_fn, | 109 | const std::string &field_prefix, const std::string &emit_fn, |
| 33 | const std::string &indent, bool emit_first_arg) const { | 110 | const std::string &indent, bool emit_first_arg) const { |
| @@ -271,7 +348,8 @@ void TilingLib::GenGroupPerfForScheduleResult(std::stringstream &ss, size_t asc_ | |||
| 271 | } | 348 | } |
| 272 | 349 | ||
| 273 | std::string TilingLib::GenGetTopnSolutionsFuncForInductor(const ascir::FusedScheduledResult &fused_schedule_result, | 350 | std::string TilingLib::GenGetTopnSolutionsFuncForInductor(const ascir::FusedScheduledResult &fused_schedule_result, |
| 274 | - const std::string &tiling) const { | 351 | + const std::string &tiling, bool use_measured_perf, |
| 352 | + const std::string &entry_declaration) const { | ||
| 275 | std::stringstream ss; | 353 | std::stringstream ss; |
| 276 | codegen::PgoShapeStringStream pgo_shape_dim; | 354 | codegen::PgoShapeStringStream pgo_shape_dim; |
| 277 | int symbol_value_count = 0; | 355 | int symbol_value_count = 0; |
| @@ -285,13 +363,22 @@ std::string TilingLib::GenGetTopnSolutionsFuncForInductor(const ascir::FusedSche | |||
| 285 | } | 363 | } |
| 286 | } | 364 | } |
| 287 | 365 | ||
| 288 | - GenTopnGetTilingFunc(ss, fused_schedule_result, tiling, symbol_value_count); | 366 | + GenTopnGetTilingFunc(ss, fused_schedule_result, tiling, symbol_value_count, use_measured_perf); |
| 289 | - GenGenerateTopnSolutionsEntry(ss, fused_schedule_result, tiling, pgo_shape_dim); | 367 | + const std::string resolved_entry_declaration = |
| 368 | + entry_declaration.empty() | ||
| 369 | + ? "extern \"C\" int64_t " + | ||
| 370 | + std::string(use_measured_perf ? "GenerateMeasuredTopnSolutions" : "GenerateTopnSolutions") | ||
| 371 | + : entry_declaration; | ||
| 372 | + GenGenerateTopnSolutionsEntry(ss, fused_schedule_result, tiling, pgo_shape_dim, resolved_entry_declaration); | ||
| 373 | + if (use_measured_perf) { | ||
| 374 | + GenInductorPgoProxyEntry(ss, tiling); | ||
| 375 | + } | ||
| 290 | return ss.str(); | 376 | return ss.str(); |
| 291 | } | 377 | } |
| 292 | 378 | ||
| 293 | void TilingLib::GenTopnInitSearchTiling(std::stringstream &ss, const ascir::FusedScheduledResult &fused_schedule_result, | 379 | void TilingLib::GenTopnInitSearchTiling(std::stringstream &ss, const ascir::FusedScheduledResult &fused_schedule_result, |
| 294 | - const std::string &tiling, int symbol_value_count) const { | 380 | + const std::string &tiling, int symbol_value_count, |
| 381 | + bool use_measured_perf) const { | ||
| 295 | ss << " const ResLimit *limit = (request.res_limit == nullptr || request.res_limit->aiv_num == 0) " | 382 | ss << " const ResLimit *limit = (request.res_limit == nullptr || request.res_limit->aiv_num == 0) " |
| 296 | << "? &g_no_limit_res : request.res_limit;" << std::endl; | 383 | << "? &g_no_limit_res : request.res_limit;" << std::endl; |
| 297 | ss << " if (request.symbol_values.size() != " << symbol_value_count << "ULL) {" << std::endl; | 384 | ss << " if (request.symbol_values.size() != " << symbol_value_count << "ULL) {" << std::endl; |
| @@ -299,8 +386,12 @@ void TilingLib::GenTopnInitSearchTiling(std::stringstream &ss, const ascir::Fuse | |||
| 299 | ss << " return -1;" << std::endl; | 386 | ss << " return -1;" << std::endl; |
| 300 | ss << " }" << std::endl; | 387 | ss << " }" << std::endl; |
| 301 | ss << std::endl; | 388 | ss << std::endl; |
| 389 | + if (use_measured_perf) { | ||
| 390 | + ss << " const uint32_t measured_aiv_num = std::min(limit->aiv_num, g_no_limit_res.aiv_num);" << std::endl; | ||
| 391 | + } | ||
| 302 | ss << " " << tiling << " search_tiling = {};" << std::endl; | 392 | ss << " " << tiling << " search_tiling = {};" << std::endl; |
| 303 | - ss << " search_tiling.set_block_dim(limit->aiv_num);" << std::endl; | 393 | + ss << " search_tiling.set_block_dim(" << (use_measured_perf ? "measured_aiv_num" : "limit->aiv_num") << ");" |
| 394 | + << std::endl; | ||
| 304 | ss << " search_tiling.set_ub_size(limit->ub_size - 256);" << std::endl; | 395 | ss << " search_tiling.set_ub_size(limit->ub_size - 256);" << std::endl; |
| 305 | { | 396 | { |
| 306 | int idx = 0; | 397 | int idx = 0; |
| @@ -318,7 +409,7 @@ void TilingLib::GenTopnInitSearchTiling(std::stringstream &ss, const ascir::Fuse | |||
| 318 | } | 409 | } |
| 319 | 410 | ||
| 320 | void TilingLib::GenTopnGetTilingFunc(std::stringstream &ss, const ascir::FusedScheduledResult &fused_schedule_result, | 411 | void TilingLib::GenTopnGetTilingFunc(std::stringstream &ss, const ascir::FusedScheduledResult &fused_schedule_result, |
| 321 | - const std::string &tiling, int symbol_value_count) const { | 412 | + const std::string &tiling, int symbol_value_count, bool use_measured_perf) const { |
| 322 | ss << "static int64_t GetTopnCandidateSolutions(const GetTilingRequest &request, GetTilingResponse &response) {" | 413 | ss << "static int64_t GetTopnCandidateSolutions(const GetTilingRequest &request, GetTilingResponse &response) {" |
| 323 | << std::endl; | 414 | << std::endl; |
| 324 | ss << " response.candidate_solutions.clear();" << std::endl; | 415 | ss << " response.candidate_solutions.clear();" << std::endl; |
| @@ -331,29 +422,34 @@ void TilingLib::GenTopnGetTilingFunc(std::stringstream &ss, const ascir::FusedSc | |||
| 331 | ss << " return -1;" << std::endl; | 422 | ss << " return -1;" << std::endl; |
| 332 | ss << " }" << std::endl; | 423 | ss << " }" << std::endl; |
| 333 | 424 | ||
| 334 | - GenTopnInitSearchTiling(ss, fused_schedule_result, tiling, symbol_value_count); | 425 | + GenTopnInitSearchTiling(ss, fused_schedule_result, tiling, symbol_value_count, use_measured_perf); |
| 335 | GenTopnDefaultTiling(ss, tiling); | 426 | GenTopnDefaultTiling(ss, tiling); |
| 336 | 427 | ||
| 337 | - ss << " const bool internal_no_config_path = (request.input_configs == nullptr);" << std::endl; | 428 | + if (use_measured_perf) { |
| 338 | - ss << " const bool explicit_no_config_path = request.input_configs != nullptr && request.input_configs->size() == 1 " | 429 | + ss << " (void)request.input_configs;" << std::endl; |
| 339 | - << "&& request.input_configs->front().empty();" << std::endl; | 430 | + } else { |
| 340 | - ss << " const bool original_config_path = internal_no_config_path || explicit_no_config_path;" << std::endl; | 431 | + ss << " const bool internal_no_config_path = (request.input_configs == nullptr);" << std::endl; |
| 341 | - ss << " std::vector<SearchConfig> configs;" << std::endl; | 432 | + ss << " const bool explicit_no_config_path = request.input_configs != nullptr && " |
| 342 | - ss << " std::vector<const SearchConfig *> config_ptrs;" << std::endl; | 433 | + "request.input_configs->size() == 1 && request.input_configs->front().empty();" |
| 343 | - ss << " if (original_config_path) {" << std::endl; | 434 | + << std::endl; |
| 344 | - ss << " config_ptrs.push_back(nullptr);" << std::endl; | 435 | + ss << " const bool original_config_path = internal_no_config_path || explicit_no_config_path;" << std::endl; |
| 345 | - ss << " } else {" << std::endl; | 436 | + ss << " std::vector<SearchConfig> configs;" << std::endl; |
| 346 | - ss << " configs = ParseSearchConfigs(*request.input_configs);" << std::endl; | 437 | + ss << " std::vector<const SearchConfig *> config_ptrs;" << std::endl; |
| 347 | - ss << " if (configs.empty()) {" << std::endl; | 438 | + ss << " if (original_config_path) {" << std::endl; |
| 348 | - GenTopnSetFailureMessage(ss, " ", "invalid input configs"); | 439 | + ss << " config_ptrs.push_back(nullptr);" << std::endl; |
| 349 | - ss << " return -1;" << std::endl; | 440 | + ss << " } else {" << std::endl; |
| 350 | - ss << " }" << std::endl; | 441 | + ss << " configs = ParseSearchConfigs(*request.input_configs);" << std::endl; |
| 351 | - ss << " config_ptrs.reserve(configs.size());" << std::endl; | 442 | + ss << " if (configs.empty()) {" << std::endl; |
| 352 | - ss << " for (const auto &cfg : configs) { config_ptrs.push_back(&cfg); }" << std::endl; | 443 | + GenTopnSetFailureMessage(ss, " ", "invalid input configs"); |
| 353 | - ss << " }" << std::endl; | 444 | + ss << " return -1;" << std::endl; |
| 445 | + ss << " }" << std::endl; | ||
| 446 | + ss << " config_ptrs.reserve(configs.size());" << std::endl; | ||
| 447 | + ss << " for (const auto &cfg : configs) { config_ptrs.push_back(&cfg); }" << std::endl; | ||
| 448 | + ss << " }" << std::endl; | ||
| 449 | + } | ||
| 354 | ss << std::endl; | 450 | ss << std::endl; |
| 355 | 451 | ||
| 356 | - GenTopnSearchAndFinalChecks(ss, tiling, fused_schedule_result); | 452 | + GenTopnSearchAndFinalChecks(ss, tiling, fused_schedule_result, use_measured_perf); |
| 357 | ss << " return 0;" << std::endl; | 453 | ss << " return 0;" << std::endl; |
| 358 | ss << "}" << std::endl; | 454 | ss << "}" << std::endl; |
| 359 | ss << std::endl; | 455 | ss << std::endl; |
| @@ -423,6 +519,88 @@ void TilingLib::GenTopnCollectCandidates(std::stringstream &ss, const std::strin | |||
| 423 | ss << std::endl; | 519 | ss << std::endl; |
| 424 | } | 520 | } |
| 425 | 521 | ||
| 522 | +namespace { | ||
| 523 | +void GenTopnCollectMeasuredCandidatesForList(std::stringstream &ss, const std::string &indent, | ||
| 524 | + const std::string &candidate_list) { | ||
| 525 | + ss << indent << "for (const auto &raw_candidate : " << candidate_list << ") {" << std::endl; | ||
| 526 | + ss << indent | ||
| 527 | + << " if (!std::isfinite(raw_candidate.best_perf) || raw_candidate.best_perf <= 0.0 || " | ||
| 528 | + "raw_candidate.best_perf >= DBL_MAX) { continue; }" | ||
| 529 | + << std::endl; | ||
| 530 | + ss << indent << " CandidateSolution solution;" << std::endl; | ||
| 531 | + ss << indent << " solution.tiling_data = raw_candidate.tiling_data;" << std::endl; | ||
| 532 | + ss << indent << " solution.canonical_repr = GetTilingDataRepr(&raw_candidate.tiling_data);" << std::endl; | ||
| 533 | + ss << indent << " if (solution.canonical_repr.empty()) { continue; }" << std::endl; | ||
| 534 | + ss << indent << " solution.modeled_perf = raw_candidate.best_perf;" << std::endl; | ||
| 535 | + ss << indent << " solution.is_default = !default_repr.empty() && (solution.canonical_repr == default_repr);" | ||
| 536 | + << std::endl; | ||
| 537 | + ss << indent << " if (solution.is_default) { found_default_candidate = true; }" << std::endl; | ||
| 538 | + ss << indent << " response.candidate_solutions.push_back(solution);" << std::endl; | ||
| 539 | + ss << indent << "}" << std::endl; | ||
| 540 | +} | ||
| 541 | +} // namespace | ||
| 542 | + | ||
| 543 | +void TilingLib::GenTopnMeasuredCoreSearch(std::stringstream &ss, const std::string &tiling) const { | ||
| 544 | + ss << " PgoConfigRuntimeGuard pgo_config_guard;" << std::endl; | ||
| 545 | + ss << " std::vector<" << tiling << "> measured_tiling_datas;" << std::endl; | ||
| 546 | + ss << " PgoConfig::Instance().need_change_solver_run = true;" << std::endl; | ||
| 547 | + ss << " while (PgoConfig::Instance().pgo_threshold_index < PgoConfig::Instance().pgo_threshold_list_size) {" | ||
| 548 | + << std::endl; | ||
| 549 | + ss << " " << tiling << " cur_search_tiling = search_tiling;" << std::endl; | ||
| 550 | + ss << " if (!optiling::PGOByCoreNumSearchTilingKey(measured_tiling_datas, &cur_search_tiling, " | ||
| 551 | + "measured_aiv_num)) {" | ||
| 552 | + << std::endl; | ||
| 553 | + GenTopnSetFailureMessage(ss, " ", "PGOByCoreNumSearchTilingKey failed"); | ||
| 554 | + ss << " return -1;" << std::endl; | ||
| 555 | + ss << " }" << std::endl; | ||
| 556 | + ss << " ++PgoConfig::Instance().pgo_threshold_index;" << std::endl; | ||
| 557 | + ss << " }" << std::endl; | ||
| 558 | +} | ||
| 559 | + | ||
| 560 | +void TilingLib::GenTopnMeasuredBatchProfiling(std::stringstream &ss) const { | ||
| 561 | + ss << " std::vector<AutofuseTilingDataPerf> raw_candidates;" << std::endl; | ||
| 562 | + ss << " uint32_t workspace_size = 0U;" << std::endl; | ||
| 563 | + ss << " for (const auto &tiling_data : measured_tiling_datas) {" << std::endl; | ||
| 564 | + ss << " workspace_size = std::max(workspace_size, GetWorkspaceSize(tiling_data));" << std::endl; | ||
| 565 | + ss << " raw_candidates.push_back({tiling_data, DBL_MAX});" << std::endl; | ||
| 566 | + ss << " }" << std::endl; | ||
| 567 | + ss << " if (raw_candidates.empty()) {" << std::endl; | ||
| 568 | + GenTopnSetFailureMessage(ss, " ", "PGOByCoreNumSearchTilingKey returned no candidate"); | ||
| 569 | + ss << " return -1;" << std::endl; | ||
| 570 | + ss << " }" << std::endl; | ||
| 571 | + ss << " auto measured_candidates = NormalizePgoMeasuredCandidates(std::move(raw_candidates));" << std::endl; | ||
| 572 | + ss << " if (PgoConfig::Instance().batch_callback(PgoConfig::Instance().tensor_args, " | ||
| 573 | + "PgoConfig::Instance().stream, workspace_size, &measured_candidates) != 0) {" | ||
| 574 | + << std::endl; | ||
| 575 | + GenTopnSetFailureMessage(ss, " ", "batch profiling callback failed"); | ||
| 576 | + ss << " return -1;" << std::endl; | ||
| 577 | + ss << " }" << std::endl; | ||
| 578 | + GenTopnCollectMeasuredCandidatesForList(ss, " ", "measured_candidates"); | ||
| 579 | +} | ||
| 580 | + | ||
| 581 | +void TilingLib::GenTopnAppendMeasuredDefault(std::stringstream &ss) const { | ||
| 582 | + ss << " if (!default_repr.empty() && !found_default_candidate) {" << std::endl; | ||
| 583 | + ss << " if (PgoConfig::Instance().single_callback == nullptr) {" << std::endl; | ||
| 584 | + ss << " response.error_message = \"single profiling callback is not set\";" << std::endl; | ||
| 585 | + ss << " return -1;" << std::endl; | ||
| 586 | + ss << " }" << std::endl; | ||
| 587 | + ss << " double default_perf = DBL_MAX;" << std::endl; | ||
| 588 | + ss << " const uint32_t default_workspace = GetWorkspaceSize(default_tiling);" << std::endl; | ||
| 589 | + ss << " const auto callback_ret = PgoConfig::Instance().single_callback(PgoConfig::Instance().tensor_args, " | ||
| 590 | + "PgoConfig::Instance().stream, default_workspace, &default_tiling, &default_perf);" | ||
| 591 | + << std::endl; | ||
| 592 | + ss << " if (callback_ret != 0 || !std::isfinite(default_perf) || default_perf <= 0.0 || " | ||
| 593 | + "default_perf >= DBL_MAX) {" | ||
| 594 | + << std::endl; | ||
| 595 | + ss << " response.error_message = \"default topn candidate profiling failed\";" << std::endl; | ||
| 596 | + ss << " return -1;" << std::endl; | ||
| 597 | + ss << " }" << std::endl; | ||
| 598 | + ss << " response.candidate_solutions.push_back({default_tiling, default_perf, true, default_repr});" << std::endl; | ||
| 599 | + ss << " found_default_candidate = true;" << std::endl; | ||
| 600 | + ss << " }" << std::endl; | ||
| 601 | + ss << std::endl; | ||
| 602 | +} | ||
| 603 | + | ||
| 426 | void TilingLib::GenTopnSearchTilingKeyCall(std::stringstream &ss, | 604 | void TilingLib::GenTopnSearchTilingKeyCall(std::stringstream &ss, |
| 427 | const ascir::FusedScheduledResult &fused_schedule_result, | 605 | const ascir::FusedScheduledResult &fused_schedule_result, |
| 428 | const std::string &search_cfg) const { | 606 | const std::string &search_cfg) const { |
| @@ -430,9 +608,11 @@ void TilingLib::GenTopnSearchTilingKeyCall(std::stringstream &ss, | |||
| 430 | ss << "nullptr, "; | 608 | ss << "nullptr, "; |
| 431 | const bool is_single_group = ascgen_utils::IsSingleGroup(fused_schedule_result); | 609 | const bool is_single_group = ascgen_utils::IsSingleGroup(fused_schedule_result); |
| 432 | if (is_single_group) { | 610 | if (is_single_group) { |
| 433 | - ss << "nullptr, 0, best_perf, workspace_map, {}, " << search_cfg << ");" << std::endl; | 611 | + ss << "nullptr, "; |
| 612 | + ss << "0, best_perf, workspace_map, {}, " << search_cfg << ");" << std::endl; | ||
| 434 | } else { | 613 | } else { |
| 435 | - ss << "nullptr, 0, best_perf, " << search_cfg << ");" << std::endl; | 614 | + ss << "nullptr, "; |
| 615 | + ss << "0, best_perf, " << search_cfg << ");" << std::endl; | ||
| 436 | } | 616 | } |
| 437 | } | 617 | } |
| 438 | 618 | ||
| @@ -454,11 +634,24 @@ void TilingLib::GenTopnDefaultTiling(std::stringstream &ss, const std::string &t | |||
| 454 | } | 634 | } |
| 455 | 635 | ||
| 456 | void TilingLib::GenTopnSearchAndFinalChecks(std::stringstream &ss, const std::string &tiling, | 636 | void TilingLib::GenTopnSearchAndFinalChecks(std::stringstream &ss, const std::string &tiling, |
| 457 | - const ascir::FusedScheduledResult &fused_schedule_result) const { | 637 | + const ascir::FusedScheduledResult &fused_schedule_result, |
| 458 | - ss << " PgoConfig::Instance().ResetRuntimeOverrides();" << std::endl; | 638 | + bool use_measured_perf) const { |
| 459 | - ss << " size_t failed_config_count = 0U;" << std::endl; | 639 | + if (use_measured_perf) { |
| 460 | - GenTopnSearchTilingSetup(ss, tiling, fused_schedule_result); | 640 | + ss << " if (PgoConfig::Instance().tensor_args == nullptr || PgoConfig::Instance().stream == nullptr || " |
| 461 | - GenTopnCollectCandidates(ss, tiling); | 641 | + "PgoConfig::Instance().single_callback == nullptr || PgoConfig::Instance().batch_callback == nullptr) {" |
| 642 | + << std::endl; | ||
| 643 | + ss << " response.error_message = \"PGO runtime context is not set\";" << std::endl; | ||
| 644 | + ss << " return -1;" << std::endl; | ||
| 645 | + ss << " }" << std::endl; | ||
| 646 | + GenTopnMeasuredCoreSearch(ss, tiling); | ||
| 647 | + GenTopnMeasuredBatchProfiling(ss); | ||
| 648 | + GenTopnAppendMeasuredDefault(ss); | ||
| 649 | + } else { | ||
| 650 | + ss << " PgoConfig::Instance().ResetRuntimeOverrides();" << std::endl; | ||
| 651 | + ss << " size_t failed_config_count = 0U;" << std::endl; | ||
| 652 | + GenTopnSearchTilingSetup(ss, tiling, fused_schedule_result); | ||
| 653 | + GenTopnCollectCandidates(ss, tiling); | ||
| 654 | + } | ||
| 462 | ss << " if (!found_default_candidate) {" << std::endl; | 655 | ss << " if (!found_default_candidate) {" << std::endl; |
| 463 | ss << " if (response.error_message.empty()) {" << std::endl; | 656 | ss << " if (response.error_message.empty()) {" << std::endl; |
| 464 | GenTopnSetFailureMessage(ss, " ", "default topn candidate not found"); | 657 | GenTopnSetFailureMessage(ss, " ", "default topn candidate not found"); |
| @@ -479,8 +672,9 @@ void TilingLib::GenTopnSearchAndFinalChecks(std::stringstream &ss, const std::st | |||
| 479 | void TilingLib::GenGenerateTopnSolutionsEntry(std::stringstream &ss, | 672 | void TilingLib::GenGenerateTopnSolutionsEntry(std::stringstream &ss, |
| 480 | const ascir::FusedScheduledResult &fused_schedule_result, | 673 | const ascir::FusedScheduledResult &fused_schedule_result, |
| 481 | const std::string &tiling, | 674 | const std::string &tiling, |
| 482 | - const codegen::PgoShapeStringStream &pgo_shape_dim) const { | 675 | + const codegen::PgoShapeStringStream &pgo_shape_dim, |
| 483 | - ss << "extern \"C\" int64_t GenerateTopnSolutions("; | 676 | + const std::string &entry_declaration) const { |
| 677 | + ss << entry_declaration << "("; | ||
| 484 | ss << pgo_shape_dim.shape_dim_def.str(); | 678 | ss << pgo_shape_dim.shape_dim_def.str(); |
| 485 | ss << "const std::vector<std::map<std::string, std::string>> &input_configs, int64_t topn, "; | 679 | ss << "const std::vector<std::map<std::string, std::string>> &input_configs, int64_t topn, "; |
| 486 | ss << "std::vector<" << tiling << "> &tiling_datas, std::vector<int64_t> &workspaces, "; | 680 | ss << "std::vector<" << tiling << "> &tiling_datas, std::vector<int64_t> &workspaces, "; |
| @@ -551,7 +745,7 @@ std::string TilingLib::GenCandidateSolutionProtocolForInductor(const std::string | |||
| 551 | return ss.str(); | 745 | return ss.str(); |
| 552 | } | 746 | } |
| 553 | 747 | ||
| 554 | -void TilingLib::GenDeduplicateCandidateSolutions(std::stringstream &ss) const { | 748 | +void TilingLib::GenDeduplicateCandidateSolutionsPrefix(std::stringstream &ss) const { |
| 555 | ss << "inline void DeduplicateCandidateSolutions(std::vector<CandidateSolution> &solutions) {" << std::endl; | 749 | ss << "inline void DeduplicateCandidateSolutions(std::vector<CandidateSolution> &solutions) {" << std::endl; |
| 556 | ss << " std::unordered_map<std::string, size_t> repr_to_index;" << std::endl; | 750 | ss << " std::unordered_map<std::string, size_t> repr_to_index;" << std::endl; |
| 557 | ss << " std::vector<CandidateSolution> deduplicated;" << std::endl; | 751 | ss << " std::vector<CandidateSolution> deduplicated;" << std::endl; |
| @@ -565,6 +759,10 @@ void TilingLib::GenDeduplicateCandidateSolutions(std::stringstream &ss) const { | |||
| 565 | ss << " continue;" << std::endl; | 759 | ss << " continue;" << std::endl; |
| 566 | ss << " }" << std::endl; | 760 | ss << " }" << std::endl; |
| 567 | ss << " auto &kept = deduplicated[iter->second];" << std::endl; | 761 | ss << " auto &kept = deduplicated[iter->second];" << std::endl; |
| 762 | +} | ||
| 763 | + | ||
| 764 | +void TilingLib::GenDeduplicateCandidateSolutions(std::stringstream &ss) const { | ||
| 765 | + GenDeduplicateCandidateSolutionsPrefix(ss); | ||
| 568 | ss << " if (!(std::fabs(kept.modeled_perf - solution.modeled_perf) < 1e-8)) {" << std::endl; | 766 | ss << " if (!(std::fabs(kept.modeled_perf - solution.modeled_perf) < 1e-8)) {" << std::endl; |
| 569 | ss << " OP_LOGW(OP_NAME, \"same repr with different modeled_perf, keep first: kept=%.6f, current=%.6f, " | 767 | ss << " OP_LOGW(OP_NAME, \"same repr with different modeled_perf, keep first: kept=%.6f, current=%.6f, " |
| 570 | "repr=%s\", " | 768 | "repr=%s\", " |
| @@ -580,6 +778,15 @@ void TilingLib::GenDeduplicateCandidateSolutions(std::stringstream &ss) const { | |||
| 580 | ss << std::endl; | 778 | ss << std::endl; |
| 581 | } | 779 | } |
| 582 | 780 | ||
| 781 | +void TilingLib::GenDeduplicateMeasuredCandidateSolutions(std::stringstream &ss) const { | ||
| 782 | + GenDeduplicateCandidateSolutionsPrefix(ss); | ||
| 783 | + ss << " if (kept.modeled_perf > solution.modeled_perf) { kept = solution; }" << std::endl; | ||
| 784 | + ss << " }" << std::endl; | ||
| 785 | + ss << " solutions.swap(deduplicated);" << std::endl; | ||
| 786 | + ss << "}" << std::endl; | ||
| 787 | + ss << std::endl; | ||
| 788 | +} | ||
| 789 | + | ||
| 583 | std::string TilingLib::GenTopnSelectorHelpersForInductor() const { | 790 | std::string TilingLib::GenTopnSelectorHelpersForInductor() const { |
| 584 | std::stringstream ss; | 791 | std::stringstream ss; |
| 585 | ss << "// Topn selector helpers: default-first, modeled_perf ascending, canonical_repr tiebreak." << std::endl; | 792 | ss << "// Topn selector helpers: default-first, modeled_perf ascending, canonical_repr tiebreak." << std::endl; |
| @@ -619,31 +826,40 @@ std::string TilingLib::GenTopnSelectorHelpersForInductor() const { | |||
| 619 | return ss.str(); | 826 | return ss.str(); |
| 620 | } | 827 | } |
| 621 | 828 | ||
| 622 | -std::string TilingLib::GenSearchConfigProtocolForInductor() const { | 829 | +std::string TilingLib::GenMeasuredTopnSelectorHelpersForInductor() const { |
| 623 | std::stringstream ss; | 830 | std::stringstream ss; |
| 624 | - ss << "// SearchConfig for dual-path PGO: TF builtin and Inductor request configs." << std::endl; | 831 | + ss << "// Topn selector helpers: measured best first, default fallback second." << std::endl; |
| 625 | - ss << "struct SearchConfig {" << std::endl; | 832 | + ss << "inline bool CompareCandidateSolution(const CandidateSolution &lhs, const CandidateSolution &rhs) {" |
| 626 | - ss << " bool ub_threshold_enabled = false;" << std::endl; | 833 | + << std::endl; |
| 627 | - ss << " double ub_threshold = 0.0;" << std::endl; | 834 | + ss << " if (lhs.modeled_perf < rhs.modeled_perf || rhs.modeled_perf < lhs.modeled_perf) { " |
| 628 | - ss << " bool corenum_threshold_enabled = false;" << std::endl; | 835 | + "return lhs.modeled_perf < rhs.modeled_perf; }" |
| 629 | - ss << " double corenum_threshold = 1.0;" << std::endl; | 836 | + << std::endl; |
| 630 | - ss << " bool enable_multicore_ub_tradeoff = false;" << std::endl; | 837 | + ss << " return lhs.canonical_repr < rhs.canonical_repr;" << std::endl; |
| 631 | - ss << "};" << std::endl; | 838 | + ss << "}" << std::endl; |
| 632 | ss << std::endl; | 839 | ss << std::endl; |
| 633 | - return ss.str(); | 840 | + GenDeduplicateMeasuredCandidateSolutions(ss); |
| 634 | -} | 841 | + ss << "inline void SelectTopnCandidateSolutions(std::vector<CandidateSolution> &solutions, int64_t topn) {" |
| 635 | - | 842 | + << std::endl; |
| 636 | -std::string TilingLib::GenBuiltinTfPgoConfigsForInductor() const { | 843 | + ss << " DeduplicateCandidateSolutions(solutions);" << std::endl; |
| 637 | - std::stringstream ss; | 844 | + ss << " std::sort(solutions.begin(), solutions.end(), CompareCandidateSolution);" << std::endl; |
| 638 | - ss << "// Builtin TF PGO search configs: 5 fixed threshold configurations." << std::endl; | 845 | + ss << " auto *measured_candidates = PgoConfig::Instance().measured_candidates;" << std::endl; |
| 639 | - ss << "inline std::vector<SearchConfig> GetBuiltinTfPgoConfigs() {" << std::endl; | 846 | + ss << " if (measured_candidates != nullptr) {" << std::endl; |
| 640 | - ss << " return {" << std::endl; | 847 | + ss << " measured_candidates->clear();" << std::endl; |
| 641 | - ss << " {true, 0.2, true, 0.4, false}," << std::endl; | 848 | + ss << " measured_candidates->reserve(solutions.size());" << std::endl; |
| 642 | - ss << " {true, 0.1, true, 0.4, false}," << std::endl; | 849 | + ss << " for (const auto &solution : solutions) {" << std::endl; |
| 643 | - ss << " {true, 0.0, true, 1.0, false}," << std::endl; | 850 | + ss << " measured_candidates->push_back({solution.tiling_data, solution.modeled_perf});" << std::endl; |
| 644 | - ss << " {true, 0.05, true, 1.0, false}," << std::endl; | 851 | + ss << " }" << std::endl; |
| 645 | - ss << " {true, 0.1, true, 0.8, false}," << std::endl; | 852 | + ss << " }" << std::endl; |
| 646 | - ss << " };" << std::endl; | 853 | + ss << " if (topn > 1 && solutions.size() > 1U) {" << std::endl; |
| 854 | + ss << " const auto default_solution = std::find_if(solutions.begin(), solutions.end()," << std::endl; | ||
| 855 | + ss << " [](const CandidateSolution &solution) { return solution.is_default; });" << std::endl; | ||
| 856 | + ss << " if (default_solution != solutions.end() && default_solution != solutions.begin()) {" << std::endl; | ||
| 857 | + ss << " std::rotate(solutions.begin() + 1, default_solution, default_solution + 1);" << std::endl; | ||
| 858 | + ss << " }" << std::endl; | ||
| 859 | + ss << " }" << std::endl; | ||
| 860 | + ss << " if (topn > 0 && static_cast<int64_t>(solutions.size()) > topn) {" << std::endl; | ||
| 861 | + ss << " solutions.resize(static_cast<size_t>(topn));" << std::endl; | ||
| 862 | + ss << " }" << std::endl; | ||
| 647 | ss << "}" << std::endl; | 863 | ss << "}" << std::endl; |
| 648 | ss << std::endl; | 864 | ss << std::endl; |
| 649 | return ss.str(); | 865 | return ss.str(); |
| @@ -16,7 +16,6 @@ | |||
| 16 | 16 | ||
| 17 | namespace codegen { | 17 | namespace codegen { |
| 18 | using namespace ascgen_utils; | 18 | using namespace ascgen_utils; |
| 19 | - | ||
| 20 | namespace { | 19 | namespace { |
| 21 | bool IsNeedFfts() { | 20 | bool IsNeedFfts() { |
| 22 | const auto backend_spec = optimize::BackendSpec::GetInstance(); | 21 | const auto backend_spec = optimize::BackendSpec::GetInstance(); |
| @@ -42,7 +41,7 @@ void AppendPgoLogDefs(std::stringstream &ss) { | |||
| 42 | } | 41 | } |
| 43 | } // namespace | 42 | } // namespace |
| 44 | 43 | ||
| 45 | -void TilingLib::GenPgoHeaders(std::stringstream &ss) const { | 44 | +void TilingLib::GenPgoHeaders(std::stringstream &ss, bool direct_link) const { |
| 46 | ss << "#include <cinttypes>" << std::endl; | 45 | ss << "#include <cinttypes>" << std::endl; |
| 47 | ss << "#include <unistd.h>" << std::endl; | 46 | ss << "#include <unistd.h>" << std::endl; |
| 48 | ss << "#include <fcntl.h>" << std::endl; | 47 | ss << "#include <fcntl.h>" << std::endl; |
| @@ -59,10 +58,21 @@ void TilingLib::GenPgoHeaders(std::stringstream &ss) const { | |||
| 59 | ss << "#include <cstring>" << std::endl; | 58 | ss << "#include <cstring>" << std::endl; |
| 60 | ss << "#include <securec.h>" << std::endl; | 59 | ss << "#include <securec.h>" << std::endl; |
| 61 | ss << "#include <fstream>" << std::endl; | 60 | ss << "#include <fstream>" << std::endl; |
| 61 | + if (direct_link) { | ||
| 62 | + ss << "#include <atomic>" << std::endl; | ||
| 63 | + ss << "#include <cctype>" << std::endl; | ||
| 64 | + ss << "#include <climits>" << std::endl; | ||
| 65 | + ss << "#include <cstdio>" << std::endl; | ||
| 66 | + ss << "#include <cstdlib>" << std::endl; | ||
| 67 | + ss << "#include <limits>" << std::endl; | ||
| 68 | + ss << "#include <type_traits>" << std::endl; | ||
| 69 | + } | ||
| 62 | ss << "#include <map>" << std::endl; | 70 | ss << "#include <map>" << std::endl; |
| 63 | ss << "#include <string>" << std::endl; | 71 | ss << "#include <string>" << std::endl; |
| 64 | ss << "#include <thread>" << std::endl; | 72 | ss << "#include <thread>" << std::endl; |
| 73 | + ss << "#include <utility>" << std::endl; | ||
| 65 | ss << "#include <unordered_map>" << std::endl; | 74 | ss << "#include <unordered_map>" << std::endl; |
| 75 | + ss << "#include <unordered_set>" << std::endl; | ||
| 66 | ss << "#include <vector>" << std::endl << std::endl; | 76 | ss << "#include <vector>" << std::endl << std::endl; |
| 67 | 77 | ||
| 68 | ss << "#include \"acl/acl.h\"" << std::endl; | 78 | ss << "#include \"acl/acl.h\"" << std::endl; |
| @@ -186,11 +196,11 @@ void TilingLib::GenPgoAppendSearchTilingData(std::stringstream &ss) const { | |||
| 186 | } | 196 | } |
| 187 | 197 | ||
| 188 | void TilingLib::GenPgoKernelLaunchOpArgs(const ascir::FusedScheduledResult &fused_schedule_result, | 198 | void TilingLib::GenPgoKernelLaunchOpArgs(const ascir::FusedScheduledResult &fused_schedule_result, |
| 189 | - std::stringstream &ss) const { | 199 | + std::stringstream &ss, bool direct_link) const { |
| 190 | ss << "struct AivKernelLaunchOpArgs {" << std::endl; | 200 | ss << "struct AivKernelLaunchOpArgs {" << std::endl; |
| 191 | ss << PGOSearchStructInputOutputDef(fused_schedule_result); | 201 | ss << PGOSearchStructInputOutputDef(fused_schedule_result); |
| 192 | ss << " uint64_t workspace_addr;" << std::endl; | 202 | ss << " uint64_t workspace_addr;" << std::endl; |
| 193 | - ss << " uint64_t tiling_addr;" << std::endl; | 203 | + ss << (direct_link ? " AutofuseTilingData tiling_data;" : " uint64_t tiling_addr;") << std::endl; |
| 194 | ss << "};" << std::endl; | 204 | ss << "};" << std::endl; |
| 195 | 205 | ||
| 196 | ss << "struct MixKernelLaunchOpArgs {" << std::endl; | 206 | ss << "struct MixKernelLaunchOpArgs {" << std::endl; |
| @@ -199,7 +209,7 @@ void TilingLib::GenPgoKernelLaunchOpArgs(const ascir::FusedScheduledResult &fuse | |||
| 199 | } | 209 | } |
| 200 | ss << PGOSearchStructInputOutputDef(fused_schedule_result); | 210 | ss << PGOSearchStructInputOutputDef(fused_schedule_result); |
| 201 | ss << " uint64_t workspace_addr;" << std::endl; | 211 | ss << " uint64_t workspace_addr;" << std::endl; |
| 202 | - ss << " uint64_t tiling_addr;" << std::endl; | 212 | + ss << (direct_link ? " AutofuseTilingData tiling_data;" : " uint64_t tiling_addr;") << std::endl; |
| 203 | ss << "};" << std::endl; | 213 | ss << "};" << std::endl; |
| 204 | 214 | ||
| 205 | ss << "void *g_workspace = nullptr;" << std::endl; | 215 | ss << "void *g_workspace = nullptr;" << std::endl; |
| @@ -227,8 +237,8 @@ void TilingLib::GenPgoCheckTilingIsMix(const ascir::FusedScheduledResult &fused_ | |||
| 227 | ss << "}" << std::endl; | 237 | ss << "}" << std::endl; |
| 228 | } | 238 | } |
| 229 | 239 | ||
| 230 | -void TilingLib::GenPgoLaunchParamsInit(const ascir::FusedScheduledResult &fused_schedule_result, | 240 | +void TilingLib::GenPgoLaunchParamsInit(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, |
| 231 | - std::stringstream &ss) const { | 241 | + bool direct_link) const { |
| 232 | ss << "aclError LaunchParamsInit(PgoTensorArgs *tensor_args) {" << std::endl; | 242 | ss << "aclError LaunchParamsInit(PgoTensorArgs *tensor_args) {" << std::endl; |
| 233 | ss << " static void *ffts = nullptr;" << std::endl; | 243 | ss << " static void *ffts = nullptr;" << std::endl; |
| 234 | ss << " aclError ret = ACL_SUCCESS;" << std::endl; | 244 | ss << " aclError ret = ACL_SUCCESS;" << std::endl; |
| @@ -243,7 +253,9 @@ void TilingLib::GenPgoLaunchParamsInit(const ascir::FusedScheduledResult &fused_ | |||
| 243 | ss << " return FAILED;" << std::endl; | 253 | ss << " return FAILED;" << std::endl; |
| 244 | ss << " }" << std::endl; | 254 | ss << " }" << std::endl; |
| 245 | ss << PGOSearchFuncInputOutputStructAssignDef(fused_schedule_result, " g_launch_params.aiv_args"); | 255 | ss << PGOSearchFuncInputOutputStructAssignDef(fused_schedule_result, " g_launch_params.aiv_args"); |
| 246 | - ss << " g_launch_params.aiv_args.tiling_addr = reinterpret_cast<uint64_t>(g_tiling_device_addr);" << std::endl; | 256 | + if (!direct_link) { |
| 257 | + ss << " g_launch_params.aiv_args.tiling_addr = reinterpret_cast<uint64_t>(g_tiling_device_addr);" << std::endl; | ||
| 258 | + } | ||
| 247 | if (IsNeedFfts()) { | 259 | if (IsNeedFfts()) { |
| 248 | ss << " ret = aclrtGetHardwareSyncAddr(&ffts);" << std::endl; | 260 | ss << " ret = aclrtGetHardwareSyncAddr(&ffts);" << std::endl; |
| 249 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; | 261 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; |
| @@ -253,7 +265,9 @@ void TilingLib::GenPgoLaunchParamsInit(const ascir::FusedScheduledResult &fused_ | |||
| 253 | ss << " g_launch_params.mix_args.ffts = reinterpret_cast<uint64_t>(ffts);" << std::endl; | 265 | ss << " g_launch_params.mix_args.ffts = reinterpret_cast<uint64_t>(ffts);" << std::endl; |
| 254 | } | 266 | } |
| 255 | ss << PGOSearchFuncInputOutputStructAssignDef(fused_schedule_result, " g_launch_params.mix_args"); | 267 | ss << PGOSearchFuncInputOutputStructAssignDef(fused_schedule_result, " g_launch_params.mix_args"); |
| 256 | - ss << " g_launch_params.mix_args.tiling_addr = reinterpret_cast<uint64_t>(g_tiling_device_addr);" << std::endl; | 268 | + if (!direct_link) { |
| 269 | + ss << " g_launch_params.mix_args.tiling_addr = reinterpret_cast<uint64_t>(g_tiling_device_addr);" << std::endl; | ||
| 270 | + } | ||
| 257 | ss << " ret = aclrtMalloc(&g_launch_params.aiv_args_device, sizeof(AivKernelLaunchOpArgs), " | 271 | ss << " ret = aclrtMalloc(&g_launch_params.aiv_args_device, sizeof(AivKernelLaunchOpArgs), " |
| 258 | "ACL_MEM_MALLOC_HUGE_FIRST);" | 272 | "ACL_MEM_MALLOC_HUGE_FIRST);" |
| 259 | << std::endl; | 273 | << std::endl; |
| @@ -291,6 +305,17 @@ void TilingLib::GenPgoLaunchParamsDeInit(std::stringstream &ss) const { | |||
| 291 | ss << "}" << std::endl; | 305 | ss << "}" << std::endl; |
| 292 | } | 306 | } |
| 293 | 307 | ||
| 308 | +void TilingLib::GenPgoCopyLaunchArgs(std::stringstream &ss, const std::string &kernel_type, | ||
| 309 | + const std::string &assignment) const { | ||
| 310 | + ss << " " << assignment << "aclrtMemcpy(g_launch_params." << kernel_type << "_args_device, sizeof(g_launch_params." | ||
| 311 | + << kernel_type << "_args), (void *)&g_launch_params." << kernel_type << "_args, sizeof(g_launch_params." | ||
| 312 | + << kernel_type << "_args), ACL_MEMCPY_HOST_TO_DEVICE);" << std::endl; | ||
| 313 | + ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 314 | + ss << " DLOGE(\"memcpy " << kernel_type << "_args to device failed, ERROR: %d\", ret);" << std::endl; | ||
| 315 | + ss << " return FAILED;" << std::endl; | ||
| 316 | + ss << " }" << std::endl; | ||
| 317 | +} | ||
| 318 | + | ||
| 294 | void TilingLib::GenPgoUpdateLaunchParams(std::stringstream &ss) const { | 319 | void TilingLib::GenPgoUpdateLaunchParams(std::stringstream &ss) const { |
| 295 | ss << "aclError UpdateLaunchParam(const AutofuseTilingData &tiling_data) {" << std::endl; | 320 | ss << "aclError UpdateLaunchParam(const AutofuseTilingData &tiling_data) {" << std::endl; |
| 296 | ss << " if (IsMixTiling(tiling_data)) {" << std::endl; | 321 | ss << " if (IsMixTiling(tiling_data)) {" << std::endl; |
| @@ -302,13 +327,7 @@ void TilingLib::GenPgoUpdateLaunchParams(std::stringstream &ss) const { | |||
| 302 | ss << " return FAILED;" << std::endl; | 327 | ss << " return FAILED;" << std::endl; |
| 303 | ss << " }" << std::endl; | 328 | ss << " }" << std::endl; |
| 304 | ss << " g_launch_params.mix_args.workspace_addr = reinterpret_cast<uint64_t>(g_workspace);" << std::endl; | 329 | ss << " g_launch_params.mix_args.workspace_addr = reinterpret_cast<uint64_t>(g_workspace);" << std::endl; |
| 305 | - ss << " ret = aclrtMemcpy(g_launch_params.mix_args_device, sizeof(g_launch_params.mix_args), (void " | 330 | + GenPgoCopyLaunchArgs(ss, "mix", "ret = "); |
| 306 | - "*)&g_launch_params.mix_args, sizeof(g_launch_params.mix_args), ACL_MEMCPY_HOST_TO_DEVICE);" | ||
| 307 | - << std::endl; | ||
| 308 | - ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 309 | - ss << " DLOGE(\"memcpy mix_args to device failed, ERROR: %d\", ret);" << std::endl; | ||
| 310 | - ss << " return FAILED;" << std::endl; | ||
| 311 | - ss << " }" << std::endl; | ||
| 312 | ss << " } else {" << std::endl; | 331 | ss << " } else {" << std::endl; |
| 313 | ss << " auto ret = aclrtMemcpy((void *)g_launch_params.aiv_args.tiling_addr, sizeof(AutofuseTilingData), (void " | 332 | ss << " auto ret = aclrtMemcpy((void *)g_launch_params.aiv_args.tiling_addr, sizeof(AutofuseTilingData), (void " |
| 314 | "*)&tiling_data, " | 333 | "*)&tiling_data, " |
| @@ -318,20 +337,29 @@ void TilingLib::GenPgoUpdateLaunchParams(std::stringstream &ss) const { | |||
| 318 | ss << " return FAILED;" << std::endl; | 337 | ss << " return FAILED;" << std::endl; |
| 319 | ss << " }" << std::endl; | 338 | ss << " }" << std::endl; |
| 320 | ss << " g_launch_params.aiv_args.workspace_addr = reinterpret_cast<uint64_t>(g_workspace);" << std::endl; | 339 | ss << " g_launch_params.aiv_args.workspace_addr = reinterpret_cast<uint64_t>(g_workspace);" << std::endl; |
| 321 | - ss << " ret = aclrtMemcpy(g_launch_params.aiv_args_device, sizeof(g_launch_params.aiv_args), (void " | 340 | + GenPgoCopyLaunchArgs(ss, "aiv", "ret = "); |
| 322 | - "*)&g_launch_params.aiv_args, sizeof(g_launch_params.aiv_args), ACL_MEMCPY_HOST_TO_DEVICE);" | ||
| 323 | - << std::endl; | ||
| 324 | - ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 325 | - ss << " DLOGE(\"memcpy aiv_args to device failed, ERROR: %d\", ret);" << std::endl; | ||
| 326 | - ss << " return FAILED;" << std::endl; | ||
| 327 | - ss << " }" << std::endl; | ||
| 328 | ss << " }" << std::endl; | 341 | ss << " }" << std::endl; |
| 329 | ss << " return ACL_SUCCESS;" << std::endl; | 342 | ss << " return ACL_SUCCESS;" << std::endl; |
| 330 | ss << "}" << std::endl; | 343 | ss << "}" << std::endl; |
| 331 | } | 344 | } |
| 332 | 345 | ||
| 333 | -void TilingLib::GenPgoLaunchParams(const ascir::FusedScheduledResult &fused_schedule_result, | 346 | +void TilingLib::GenInductorPgoUpdateLaunchParams(std::stringstream &ss) const { |
| 334 | - std::stringstream &ss) const { | 347 | + ss << "aclError UpdateLaunchParam(const AutofuseTilingData &tiling_data) {" << std::endl; |
| 348 | + ss << " if (IsMixTiling(tiling_data)) {" << std::endl; | ||
| 349 | + ss << " g_launch_params.mix_args.tiling_data = tiling_data;" << std::endl; | ||
| 350 | + ss << " g_launch_params.mix_args.workspace_addr = reinterpret_cast<uint64_t>(g_workspace);" << std::endl; | ||
| 351 | + GenPgoCopyLaunchArgs(ss, "mix", "auto ret = "); | ||
| 352 | + ss << " } else {" << std::endl; | ||
| 353 | + ss << " g_launch_params.aiv_args.tiling_data = tiling_data;" << std::endl; | ||
| 354 | + ss << " g_launch_params.aiv_args.workspace_addr = reinterpret_cast<uint64_t>(g_workspace);" << std::endl; | ||
| 355 | + GenPgoCopyLaunchArgs(ss, "aiv", "auto ret = "); | ||
| 356 | + ss << " }" << std::endl; | ||
| 357 | + ss << " return ACL_SUCCESS;" << std::endl; | ||
| 358 | + ss << "}" << std::endl; | ||
| 359 | +} | ||
| 360 | + | ||
| 361 | +void TilingLib::GenPgoLaunchParams(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 362 | + bool direct_link) const { | ||
| 335 | ss << "struct LaunchParams {" << std::endl; | 363 | ss << "struct LaunchParams {" << std::endl; |
| 336 | ss << " AivKernelLaunchOpArgs aiv_args;" << std::endl; | 364 | ss << " AivKernelLaunchOpArgs aiv_args;" << std::endl; |
| 337 | ss << " void *aiv_args_device;" << std::endl; | 365 | ss << " void *aiv_args_device;" << std::endl; |
| @@ -339,28 +367,38 @@ void TilingLib::GenPgoLaunchParams(const ascir::FusedScheduledResult &fused_sche | |||
| 339 | ss << " void *mix_args_device;" << std::endl; | 367 | ss << " void *mix_args_device;" << std::endl; |
| 340 | ss << "} g_launch_params;" << std::endl; | 368 | ss << "} g_launch_params;" << std::endl; |
| 341 | 369 | ||
| 342 | - GenPgoLaunchParamsInit(fused_schedule_result, ss); | 370 | + GenPgoLaunchParamsInit(fused_schedule_result, ss, direct_link); |
| 343 | GenPgoLaunchParamsDeInit(ss); | 371 | GenPgoLaunchParamsDeInit(ss); |
| 344 | - GenPgoUpdateLaunchParams(ss); | 372 | + if (direct_link) { |
| 373 | + GenInductorPgoUpdateLaunchParams(ss); | ||
| 374 | + } else { | ||
| 375 | + GenPgoUpdateLaunchParams(ss); | ||
| 376 | + } | ||
| 345 | } | 377 | } |
| 346 | 378 | ||
| 347 | -void TilingLib::GenPgoToolFunction(const ascir::FusedScheduledResult &fused_schedule_result, const std::string &pgo_dir, | 379 | +void TilingLib::GenPgoToolDeclarations(const ascir::FusedScheduledResult &fused_schedule_result, |
| 348 | - std::stringstream &ss) const { | 380 | + const std::string &pgo_dir, std::stringstream &ss, bool direct_link) const { |
| 349 | - std::string graph_name = CamelToLowerSneak(fused_schedule_result.fused_graph_name.GetString()); | 381 | + const std::string graph_name = CamelToLowerSneak(GenValidName(fused_schedule_result.fused_graph_name.GetString())); |
| 350 | ss << "namespace {" << std::endl; | 382 | ss << "namespace {" << std::endl; |
| 351 | ss << "constexpr bool g_is_mix_operator = " << (IsMixKernelTaskType(fused_schedule_result) ? "true;" : "false;") | 383 | ss << "constexpr bool g_is_mix_operator = " << (IsMixKernelTaskType(fused_schedule_result) ? "true;" : "false;") |
| 352 | << std::endl; | 384 | << std::endl; |
| 353 | ss << "static bool g_is_static_kernel = false;" << std::endl; | 385 | ss << "static bool g_is_static_kernel = false;" << std::endl; |
| 354 | GenPgoMixTilingTable(fused_schedule_result, ss); | 386 | GenPgoMixTilingTable(fused_schedule_result, ss); |
| 355 | GenPgoCheckTilingIsMix(fused_schedule_result, ss); | 387 | GenPgoCheckTilingIsMix(fused_schedule_result, ss); |
| 356 | - ss << "static std::string g_kernel_name;" << std::endl; | 388 | + if (direct_link) { |
| 389 | + ss << "constexpr char kInductorPgoKernelName[] = \"" << graph_name << "\";" << std::endl; | ||
| 390 | + } else { | ||
| 391 | + ss << "static std::string g_kernel_name;" << std::endl; | ||
| 392 | + } | ||
| 357 | ss << "static std::string g_kernel_o_file;" << std::endl; | 393 | ss << "static std::string g_kernel_o_file;" << std::endl; |
| 358 | ss << "static std::string g_npu_lock_file;" << std::endl; | 394 | ss << "static std::string g_npu_lock_file;" << std::endl; |
| 359 | ss << "#define PGO_GRAPH_NAME \"" << graph_name << "\"" << std::endl; | 395 | ss << "#define PGO_GRAPH_NAME \"" << graph_name << "\"" << std::endl; |
| 360 | - ss << "const char *pgo_dir = \"" << pgo_dir << "\";" << std::endl; | 396 | + if (!direct_link) { |
| 361 | - ss << "const char *config_file = \"" << pgo_dir << "/" << graph_name << "_config.txt" << "\";" << std::endl; | 397 | + ss << "const char *pgo_dir = \"" << pgo_dir << "\";" << std::endl; |
| 362 | - ss << "const char *search_file = \"" << pgo_dir << "/" << graph_name << "_search.txt" << "\";" << std::endl; | 398 | + ss << "const char *config_file = \"" << pgo_dir << "/" << graph_name << "_config.txt" << "\";" << std::endl; |
| 363 | - ss << "const char *kernel_file = \"" << pgo_dir << "/lib" << graph_name << ".so" << "\";" << std::endl; | 399 | + ss << "const char *search_file = \"" << pgo_dir << "/" << graph_name << "_search.txt" << "\";" << std::endl; |
| 400 | + ss << "const char *kernel_file = \"" << pgo_dir << "/lib" << graph_name << ".so" << "\";" << std::endl; | ||
| 401 | + } | ||
| 364 | ss << "#define SUCCESS 0" << std::endl; | 402 | ss << "#define SUCCESS 0" << std::endl; |
| 365 | ss << "#define FAILED 1" << std::endl; | 403 | ss << "#define FAILED 1" << std::endl; |
| 366 | 404 | ||
| @@ -370,16 +408,31 @@ void TilingLib::GenPgoToolFunction(const ascir::FusedScheduledResult &fused_sche | |||
| 370 | AppendPgoLogDefs(ss); | 408 | AppendPgoLogDefs(ss); |
| 371 | 409 | ||
| 372 | GenPgoCardLock(ss); | 410 | GenPgoCardLock(ss); |
| 373 | - GenPgoAppendSearchTilingData(ss); | 411 | + if (!direct_link) { |
| 374 | - GenPgoKernelLaunchOpArgs(fused_schedule_result, ss); | 412 | + GenPgoAppendSearchTilingData(ss); |
| 413 | + } | ||
| 414 | + GenPgoKernelLaunchOpArgs(fused_schedule_result, ss, direct_link); | ||
| 375 | 415 | ||
| 376 | - GenDynamicLibraryLoaderCode(ss); | 416 | + if (!direct_link) { |
| 417 | + GenDynamicLibraryLoaderCode(ss); | ||
| 418 | + } | ||
| 419 | +} | ||
| 377 | 420 | ||
| 378 | - ss << "aclrtStream g_stream;" << std::endl; | 421 | +void TilingLib::GenPgoToolFunction(const ascir::FusedScheduledResult &fused_schedule_result, const std::string &pgo_dir, |
| 422 | + std::stringstream &ss, bool direct_link) const { | ||
| 423 | + GenPgoToolDeclarations(fused_schedule_result, pgo_dir, ss, direct_link); | ||
| 424 | + | ||
| 425 | + ss << (direct_link ? "aclrtStream g_stream = nullptr;" : "aclrtStream g_stream;") << std::endl; | ||
| 379 | ss << PGOSearchTensorInputOutputDef(fused_schedule_result) << std::endl; | 426 | ss << PGOSearchTensorInputOutputDef(fused_schedule_result) << std::endl; |
| 380 | - ss << "void *g_tiling_device_addr = nullptr;" << std::endl; | 427 | + if (direct_link) { |
| 428 | + ss << "bool g_acl_initialized = false;" << std::endl; | ||
| 429 | + ss << "bool g_device_set = false;" << std::endl; | ||
| 430 | + ss << "int32_t g_device_id = -1;" << std::endl; | ||
| 431 | + } else { | ||
| 432 | + ss << "void *g_tiling_device_addr = nullptr;" << std::endl; | ||
| 433 | + } | ||
| 381 | 434 | ||
| 382 | - GenPgoLaunchParams(fused_schedule_result, ss); | 435 | + GenPgoLaunchParams(fused_schedule_result, ss, direct_link); |
| 383 | 436 | ||
| 384 | ss << "struct ResLimit {" << std::endl; | 437 | ss << "struct ResLimit {" << std::endl; |
| 385 | ss << " uint32_t valid_num = 0;" << std::endl; | 438 | ss << " uint32_t valid_num = 0;" << std::endl; |
| @@ -388,7 +441,7 @@ void TilingLib::GenPgoToolFunction(const ascir::FusedScheduledResult &fused_sche | |||
| 388 | ss << " uint32_t ub_size = 0;" << std::endl; | 441 | ss << " uint32_t ub_size = 0;" << std::endl; |
| 389 | ss << " uint32_t resv[10];" << std::endl; | 442 | ss << " uint32_t resv[10];" << std::endl; |
| 390 | ss << "};" << std::endl; | 443 | ss << "};" << std::endl; |
| 391 | - ss << "ResLimit g_res_limit = {1, {}};" << std::endl; | 444 | + ss << (direct_link ? "ResLimit g_res_limit = {1, 0, 0, 0, {}};" : "ResLimit g_res_limit = {1, {}};") << std::endl; |
| 392 | ss << "inline bool IsEqual(double a, double b) {" << std::endl; | 445 | ss << "inline bool IsEqual(double a, double b) {" << std::endl; |
| 393 | ss << " const double epsilon = 1e-8;" << std::endl; | 446 | ss << " const double epsilon = 1e-8;" << std::endl; |
| 394 | ss << " double abs = (a > b) ? (a - b) : (b - a);" << std::endl; | 447 | ss << " double abs = (a > b) ? (a - b) : (b - a);" << std::endl; |
| @@ -409,7 +462,7 @@ void TilingLib::GenPgoWrapperParmCall(const ascir::FusedScheduledResult &fused_s | |||
| 409 | if (CanUseTilingKey(fused_schedule_result)) { | 462 | if (CanUseTilingKey(fused_schedule_result)) { |
| 410 | ss << " if (find_best_tiling_key_fn != nullptr) {" << std::endl; | 463 | ss << " if (find_best_tiling_key_fn != nullptr) {" << std::endl; |
| 411 | ss << " tiling_key = find_best_tiling_key_fn(*tiling_data);" << std::endl; | 464 | ss << " tiling_key = find_best_tiling_key_fn(*tiling_data);" << std::endl; |
| 412 | - ss << " if (tiling_key == -1) {" << std::endl; | 465 | + ss << " if (tiling_key < 0 || static_cast<uint64_t>(tiling_key) >= tiling_key_count) {" << std::endl; |
| 413 | ss << " DLOGE(\"find best tiling key failed\");" << std::endl; | 466 | ss << " DLOGE(\"find best tiling key failed\");" << std::endl; |
| 414 | ss << " return FAILED;" << std::endl; | 467 | ss << " return FAILED;" << std::endl; |
| 415 | ss << " }" << std::endl; | 468 | ss << " }" << std::endl; |
| @@ -439,20 +492,26 @@ void TilingLib::GenPgoWrapperKernelLaunch(std::stringstream &ss) const { | |||
| 439 | ss << " auto ret_async = aclrtSynchronizeStream(g_stream);" << std::endl; | 492 | ss << " auto ret_async = aclrtSynchronizeStream(g_stream);" << std::endl; |
| 440 | } | 493 | } |
| 441 | 494 | ||
| 442 | -void TilingLib::GenPgoWrapper(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const { | 495 | +void TilingLib::GenPgoWrapperInit(std::stringstream &ss, bool direct_link) const { |
| 443 | ss << "typedef uint64_t (*GetTilingKeyCountType)(void);" << std::endl; | 496 | ss << "typedef uint64_t (*GetTilingKeyCountType)(void);" << std::endl; |
| 444 | ss << "GetTilingKeyCountType get_tiling_key_count_fn = " | 497 | ss << "GetTilingKeyCountType get_tiling_key_count_fn = " |
| 445 | - "reinterpret_cast<GetTilingKeyCountType>(GetFunc(\"GetTilingKeyCount\"));" | 498 | + << (direct_link ? "nullptr;" : "reinterpret_cast<GetTilingKeyCountType>(GetFunc(\"GetTilingKeyCount\"));") |
| 446 | << std::endl; | 499 | << std::endl; |
| 447 | - if (CanUseTilingKey(fused_schedule_result)) { | 500 | + ss << "typedef int64_t (*FindBestTilingKeyType)(AutofuseTilingData &t);" << std::endl; |
| 448 | - ss << "typedef int64_t (*FindBestTilingKeyType)(AutofuseTilingData &t);" << std::endl; | 501 | + ss << "FindBestTilingKeyType find_best_tiling_key_fn = " |
| 449 | - ss << "FindBestTilingKeyType find_best_tiling_key_fn = " | 502 | + << (direct_link ? "nullptr;" : "reinterpret_cast<FindBestTilingKeyType>(GetFunc(\"FindBestTilingKey\"));") |
| 450 | - "reinterpret_cast<FindBestTilingKeyType>(GetFunc(\"FindBestTilingKey\"));" | 503 | + << std::endl; |
| 451 | - << std::endl; | 504 | + if (direct_link) { |
| 505 | + ss << "static aclrtBinHandle g_pgo_bin_handle = nullptr;" << std::endl; | ||
| 452 | } | 506 | } |
| 453 | ss << "int WrapperOnlyLaunch(uint32_t workspace_size, AutofuseTilingData *tiling_data) {" << std::endl; | 507 | ss << "int WrapperOnlyLaunch(uint32_t workspace_size, AutofuseTilingData *tiling_data) {" << std::endl; |
| 508 | + if (direct_link) { | ||
| 509 | + ss << " (void)workspace_size;" << std::endl; | ||
| 510 | + } | ||
| 454 | ss << " static bool inited = false;" << std::endl; | 511 | ss << " static bool inited = false;" << std::endl; |
| 455 | - ss << " static aclrtBinHandle bin_handle = nullptr;" << std::endl; | 512 | + if (!direct_link) { |
| 513 | + ss << " static aclrtBinHandle bin_handle = nullptr;" << std::endl; | ||
| 514 | + } | ||
| 456 | const auto backend_spce = optimize::BackendSpec::GetInstance(); | 515 | const auto backend_spce = optimize::BackendSpec::GetInstance(); |
| 457 | if (backend_spce != nullptr && backend_spce->set_local_memory_size > 0) { | 516 | if (backend_spce != nullptr && backend_spce->set_local_memory_size > 0) { |
| 458 | ss << " static aclrtLaunchKernelCfg kernel_cfg{};" << std::endl; | 517 | ss << " static aclrtLaunchKernelCfg kernel_cfg{};" << std::endl; |
| @@ -464,9 +523,14 @@ void TilingLib::GenPgoWrapper(const ascir::FusedScheduledResult &fused_schedule_ | |||
| 464 | ss << " }" << std::endl; | 523 | ss << " }" << std::endl; |
| 465 | ss << " static uint64_t tiling_key_count = get_tiling_key_count_fn();" << std::endl; | 524 | ss << " static uint64_t tiling_key_count = get_tiling_key_count_fn();" << std::endl; |
| 466 | ss << " static std::vector<aclrtFuncHandle> func_handles(tiling_key_count);" << std::endl; | 525 | ss << " static std::vector<aclrtFuncHandle> func_handles(tiling_key_count);" << std::endl; |
| 526 | +} | ||
| 527 | + | ||
| 528 | +void TilingLib::GenPgoWrapper(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 529 | + bool direct_link) const { | ||
| 530 | + GenPgoWrapperInit(ss, direct_link); | ||
| 467 | 531 | ||
| 468 | GenPgoWrapperParmCall(fused_schedule_result, ss); | 532 | GenPgoWrapperParmCall(fused_schedule_result, ss); |
| 469 | - GenPgoLaunchKernelInit(ss); | 533 | + GenPgoLaunchKernelInit(ss, direct_link); |
| 470 | GenPgoWrapperKernelLaunch(ss); | 534 | GenPgoWrapperKernelLaunch(ss); |
| 471 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; | 535 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; |
| 472 | ss << " DLOGE(\"aclrtLaunchKernelV2 failed, ERROR: %d\", ret);" << std::endl; | 536 | ss << " DLOGE(\"aclrtLaunchKernelV2 failed, ERROR: %d\", ret);" << std::endl; |
| @@ -478,9 +542,17 @@ void TilingLib::GenPgoWrapper(const ascir::FusedScheduledResult &fused_schedule_ | |||
| 478 | ss << " }" << std::endl; | 542 | ss << " }" << std::endl; |
| 479 | ss << " return ret;" << std::endl; | 543 | ss << " return ret;" << std::endl; |
| 480 | ss << "}" << std::endl << std::endl; | 544 | ss << "}" << std::endl << std::endl; |
| 545 | + if (direct_link) { | ||
| 546 | + ss << "void PgoBinaryDeInit() {" << std::endl; | ||
| 547 | + ss << " if (g_pgo_bin_handle == nullptr) { return; }" << std::endl; | ||
| 548 | + ss << " auto ret = aclrtBinaryUnLoad(g_pgo_bin_handle);" << std::endl; | ||
| 549 | + ss << " if (ret != ACL_SUCCESS) { DLOGW(\"acl unload binary failed, ERROR: %d\", ret); }" << std::endl; | ||
| 550 | + ss << " g_pgo_bin_handle = nullptr;" << std::endl; | ||
| 551 | + ss << "}" << std::endl << std::endl; | ||
| 552 | + } | ||
| 481 | } | 553 | } |
| 482 | 554 | ||
| 483 | -void TilingLib::GenPgoProfilingConstants(std::stringstream &ss) const { | 555 | +void TilingLib::GenPgoProfilingConstants(std::stringstream &ss, bool direct_link) const { |
| 484 | ss << "#define ALIGN_SIZE (8)" << std::endl; | 556 | ss << "#define ALIGN_SIZE (8)" << std::endl; |
| 485 | ss << "#define ALIGN_BUFFER(buffer, align) \\" << std::endl; | 557 | ss << "#define ALIGN_BUFFER(buffer, align) \\" << std::endl; |
| 486 | ss << " (((uintptr_t) (buffer) & ((align)-1)) ? ((buffer) + (align) - ((uintptr_t) (buffer) & ((align)-1))) : " | 558 | ss << " (((uintptr_t) (buffer) & ((align)-1)) ? ((buffer) + (align) - ((uintptr_t) (buffer) & ((align)-1))) : " |
| @@ -491,6 +563,22 @@ void TilingLib::GenPgoProfilingConstants(std::stringstream &ss) const { | |||
| 491 | ss << "constexpr int max_flush_times = 5;" << std::endl; | 563 | ss << "constexpr int max_flush_times = 5;" << std::endl; |
| 492 | ss << "constexpr size_t mspti_buffer_size = 16ULL * 1024 * 1024;" << std::endl; | 564 | ss << "constexpr size_t mspti_buffer_size = 16ULL * 1024 * 1024;" << std::endl; |
| 493 | ss << "static double best_perf = DBL_MAX;" << std::endl; | 565 | ss << "static double best_perf = DBL_MAX;" << std::endl; |
| 566 | + if (direct_link) { | ||
| 567 | + ss << R"( | ||
| 568 | +static std::atomic<bool> g_mspti_activity_error{false}; | ||
| 569 | +static std::atomic<uint64_t> g_profiling_record_count{0U}; | ||
| 570 | + | ||
| 571 | +void ClearProfilingRecords() { | ||
| 572 | + for (auto &item : g_profiling_map) { free(item.second); } | ||
| 573 | + g_profiling_map.clear(); | ||
| 574 | + g_profiling_record_count.store(0U, std::memory_order_release); | ||
| 575 | +} | ||
| 576 | + | ||
| 577 | +void ResetProfilingRound() { | ||
| 578 | + ClearProfilingRecords(); | ||
| 579 | + g_mspti_activity_error = false; | ||
| 580 | +})" << std::endl; | ||
| 581 | + } | ||
| 494 | } | 582 | } |
| 495 | 583 | ||
| 496 | void TilingLib::GenPgoMsptiStringTable(std::stringstream &ss) const { | 584 | void TilingLib::GenPgoMsptiStringTable(std::stringstream &ss) const { |
| @@ -525,7 +613,26 @@ static const char* GetResultCodeString(msptiResult result) { | |||
| 525 | })" << std::endl; | 613 | })" << std::endl; |
| 526 | } | 614 | } |
| 527 | 615 | ||
| 528 | -void TilingLib::GenPgoMsptiRequest(std::stringstream &ss) const { | 616 | +void TilingLib::GenPgoMsptiRequest(std::stringstream &ss, bool direct_link) const { |
| 617 | + if (direct_link) { | ||
| 618 | + ss << R"( | ||
| 619 | +void UserBufferRequest(uint8_t **buffer, size_t *size, size_t *records_num) { | ||
| 620 | + DLOGD("[mspti] UserBufferRequest..."); | ||
| 621 | + uint8_t *mspti_buffer = reinterpret_cast<uint8_t *>(malloc(mspti_buffer_size + ALIGN_SIZE)); | ||
| 622 | + if (mspti_buffer == nullptr) { | ||
| 623 | + DLOGE("[mspti] malloc mspti_buffer failed"); | ||
| 624 | + g_mspti_activity_error = true; | ||
| 625 | + *buffer = nullptr; | ||
| 626 | + *size = 0; | ||
| 627 | + *records_num = 0; | ||
| 628 | + return; | ||
| 629 | + } | ||
| 630 | + *buffer = ALIGN_BUFFER(mspti_buffer, ALIGN_SIZE); | ||
| 631 | + *size = mspti_buffer_size; | ||
| 632 | + *records_num = 0; | ||
| 633 | +})" << std::endl; | ||
| 634 | + return; | ||
| 635 | + } | ||
| 529 | ss << R"( | 636 | ss << R"( |
| 530 | void UserBufferRequest(uint8_t **buffer, size_t *size, size_t *records_num) { | 637 | void UserBufferRequest(uint8_t **buffer, size_t *size, size_t *records_num) { |
| 531 | DLOGD("[mspti] UserBufferRequest..."); | 638 | DLOGD("[mspti] UserBufferRequest..."); |
| @@ -543,7 +650,44 @@ void UserBufferRequest(uint8_t **buffer, size_t *size, size_t *records_num) { | |||
| 543 | })" << std::endl; | 650 | })" << std::endl; |
| 544 | } | 651 | } |
| 545 | 652 | ||
| 546 | -void TilingLib::GenPgoMsptiComplete(std::stringstream &ss) const { | 653 | +void TilingLib::GenPgoDirectMsptiKernelHandlers(std::stringstream &ss) const { |
| 654 | + ss << R"( | ||
| 655 | +void SavePgoKernel(const msptiActivityKernel *kernel) { | ||
| 656 | + if (kernel == nullptr) { g_mspti_activity_error = true; return; } | ||
| 657 | + auto *record_copy = static_cast<msptiActivity *>(malloc(sizeof(msptiActivityKernel))); | ||
| 658 | + if (record_copy == nullptr) { g_mspti_activity_error = true; return; } | ||
| 659 | + std::memcpy(record_copy, kernel, sizeof(msptiActivityKernel)); | ||
| 660 | + if (!g_profiling_map.emplace(kernel->start, record_copy).second) { | ||
| 661 | + free(record_copy); | ||
| 662 | + g_mspti_activity_error = true; | ||
| 663 | + } else { | ||
| 664 | + g_profiling_record_count.fetch_add(1U, std::memory_order_release); | ||
| 665 | + } | ||
| 666 | +} | ||
| 667 | +)"; | ||
| 668 | +} | ||
| 669 | + | ||
| 670 | +void TilingLib::GenPgoDirectMsptiComplete(std::stringstream &ss) const { | ||
| 671 | + ss << R"( | ||
| 672 | +void UserBufferComplete(uint8_t *buffer, size_t size, size_t valid_size) { | ||
| 673 | + DLOGD("[mspti] UserBufferComplete, buf addr: %" PRIuPTR ", size: %zu, valid size: %zu", (uintptr_t)buffer, size, valid_size); | ||
| 674 | + if (buffer == nullptr && valid_size > 0U) { g_mspti_activity_error = true; return; } | ||
| 675 | + msptiActivity *mspti_record = nullptr; | ||
| 676 | + msptiResult status = MSPTI_SUCCESS; | ||
| 677 | + while (valid_size > 0U) { | ||
| 678 | + status = msptiActivityGetNextRecord(buffer, valid_size, &mspti_record); | ||
| 679 | + if (status == MSPTI_ERROR_MAX_LIMIT_REACHED) { break; } | ||
| 680 | + if (status != MSPTI_SUCCESS) { g_mspti_activity_error = true; break; } | ||
| 681 | + if (mspti_record->kind == MSPTI_ACTIVITY_KIND_KERNEL) { | ||
| 682 | + auto *kernel = reinterpret_cast<msptiActivityKernel *>(mspti_record); | ||
| 683 | + SavePgoKernel(kernel); | ||
| 684 | + } | ||
| 685 | + } | ||
| 686 | + free(buffer); | ||
| 687 | +})" << std::endl; | ||
| 688 | +} | ||
| 689 | + | ||
| 690 | +void TilingLib::GenPgoLegacyMsptiComplete(std::stringstream &ss) const { | ||
| 547 | ss << R"( | 691 | ss << R"( |
| 548 | void UserBufferComplete(uint8_t *buffer, size_t size, size_t valid_size) { | 692 | void UserBufferComplete(uint8_t *buffer, size_t size, size_t valid_size) { |
| 549 | DLOGD("[mspti] UserBufferComplete, buf addr: %" PRIuPTR ", size: %zu, valid size: %zu", (uintptr_t)buffer, size, valid_size); | 693 | DLOGD("[mspti] UserBufferComplete, buf addr: %" PRIuPTR ", size: %zu, valid size: %zu", (uintptr_t)buffer, size, valid_size); |
| @@ -575,7 +719,52 @@ void UserBufferComplete(uint8_t *buffer, size_t size, size_t valid_size) { | |||
| 575 | })" << std::endl; | 719 | })" << std::endl; |
| 576 | } | 720 | } |
| 577 | 721 | ||
| 578 | -void TilingLib::GenPgoMsptiToolFunction(std::stringstream &ss) const { | 722 | +void TilingLib::GenPgoMsptiComplete(std::stringstream &ss, bool direct_link) const { |
| 723 | + if (direct_link) { | ||
| 724 | + GenPgoDirectMsptiKernelHandlers(ss); | ||
| 725 | + GenPgoDirectMsptiComplete(ss); | ||
| 726 | + return; | ||
| 727 | + } | ||
| 728 | + GenPgoLegacyMsptiComplete(ss); | ||
| 729 | +} | ||
| 730 | + | ||
| 731 | +void TilingLib::GenPgoMsptiToolFunction(std::stringstream &ss, bool direct_link) const { | ||
| 732 | + if (direct_link) { | ||
| 733 | + ss << R"( | ||
| 734 | +msptiResult SetUpMspti(msptiSubscriberHandle *subscriber) { | ||
| 735 | + DLOGD("[mspti] setup mspti"); | ||
| 736 | + *subscriber = nullptr; | ||
| 737 | + msptiResult result = msptiSubscribe(subscriber, nullptr, nullptr); | ||
| 738 | + if (result != MSPTI_SUCCESS) { return result; } | ||
| 739 | + result = msptiActivityRegisterCallbacks(UserBufferRequest, UserBufferComplete); | ||
| 740 | + if (result != MSPTI_SUCCESS) { msptiUnsubscribe(*subscriber); return result; } | ||
| 741 | + result = msptiActivityEnable(MSPTI_ACTIVITY_KIND_KERNEL); | ||
| 742 | + if (result != MSPTI_SUCCESS) { msptiUnsubscribe(*subscriber); } | ||
| 743 | + return result; | ||
| 744 | +} | ||
| 745 | + | ||
| 746 | +msptiResult FlushPgoActivities(uint64_t expected_records) { | ||
| 747 | + if (g_profiling_record_count.load(std::memory_order_acquire) >= expected_records) { return MSPTI_SUCCESS; } | ||
| 748 | + msptiResult result = MSPTI_SUCCESS; | ||
| 749 | + for (int flush_count = 0; flush_count < max_flush_times; ++flush_count) { | ||
| 750 | + result = msptiActivityFlushAll(1); | ||
| 751 | + if (result != MSPTI_SUCCESS || | ||
| 752 | + g_profiling_record_count.load(std::memory_order_acquire) >= expected_records) { break; } | ||
| 753 | + std::this_thread::sleep_for(std::chrono::milliseconds(10 * (flush_count + 1))); | ||
| 754 | + } | ||
| 755 | + return result; | ||
| 756 | +} | ||
| 757 | + | ||
| 758 | +msptiResult TearDownMspti(msptiSubscriberHandle *subscriber) { | ||
| 759 | + DLOGD("[mspti] tear down mspti"); | ||
| 760 | + msptiResult result = *subscriber == nullptr ? MSPTI_SUCCESS : msptiUnsubscribe(*subscriber); | ||
| 761 | + *subscriber = nullptr; | ||
| 762 | + const msptiResult flush_result = msptiActivityFlushAll(1); | ||
| 763 | + if (result == MSPTI_SUCCESS) { result = flush_result; } | ||
| 764 | + return result; | ||
| 765 | +})" << std::endl; | ||
| 766 | + return; | ||
| 767 | + } | ||
| 579 | ss << R"( | 768 | ss << R"( |
| 580 | void SetUpMspti(msptiSubscriberHandle* subscriber) { | 769 | void SetUpMspti(msptiSubscriberHandle* subscriber) { |
| 581 | DLOGD("[mspti] setup mspti"); | 770 | DLOGD("[mspti] setup mspti"); |
| @@ -591,12 +780,44 @@ void TearDownMspti(msptiSubscriberHandle *subscriber) { | |||
| 591 | })" << std::endl; | 780 | })" << std::endl; |
| 592 | } | 781 | } |
| 593 | 782 | ||
| 594 | -void TilingLib::GenPgoMsptiProfiling(std::stringstream &ss) const { | 783 | +void TilingLib::GenPgoMsptiProfiling(std::stringstream &ss, bool direct_link) const { |
| 595 | - GenPgoProfilingConstants(ss); | 784 | + GenPgoProfilingConstants(ss, direct_link); |
| 596 | GenPgoMsptiStringTable(ss); | 785 | GenPgoMsptiStringTable(ss); |
| 597 | - GenPgoMsptiRequest(ss); | 786 | + GenPgoMsptiRequest(ss, direct_link); |
| 598 | - GenPgoMsptiComplete(ss); | 787 | + GenPgoMsptiComplete(ss, direct_link); |
| 599 | - GenPgoMsptiToolFunction(ss); | 788 | + GenPgoMsptiToolFunction(ss, direct_link); |
| 789 | +} | ||
| 790 | + | ||
| 791 | +void TilingLib::GenPgoDirectBatchCallback(std::stringstream &ss) const { | ||
| 792 | + ss << R"( result = aclrtSynchronizeStream(g_stream); | ||
| 793 | + const uint64_t expected_records = batch_size * loop; | ||
| 794 | + const msptiResult teardown_result = TearDownMspti(&subscriber); | ||
| 795 | + const msptiResult flush_result = FlushPgoActivities(expected_records); | ||
| 796 | + if (result != ACL_SUCCESS || g_mspti_activity_error || teardown_result != MSPTI_SUCCESS || | ||
| 797 | + flush_result != MSPTI_SUCCESS || | ||
| 798 | + g_profiling_map.size() != expected_records) { | ||
| 799 | + DLOGE("invalid batch activity: sync=%" PRId64 ", flush=%d, teardown=%d, error=%d, actual=%zu, expected=%" PRIu64, | ||
| 800 | + result, flush_result, teardown_result, g_mspti_activity_error.load(), g_profiling_map.size(), expected_records); | ||
| 801 | + ClearProfilingRecords(); | ||
| 802 | + return -1; | ||
| 803 | + } | ||
| 804 | + auto record = g_profiling_map.begin(); | ||
| 805 | + for (uint64_t i = 0; i < batch_size; ++i) { | ||
| 806 | + uint64_t total_duration = 0; | ||
| 807 | + std::vector<uint64_t> durations; | ||
| 808 | + for (uint64_t j = 0; j < loop; ++j) { | ||
| 809 | + auto *kernel = reinterpret_cast<msptiActivityKernel *>(record->second); | ||
| 810 | + durations.push_back(kernel->end - kernel->start); | ||
| 811 | + ++record; | ||
| 812 | + } | ||
| 813 | + std::sort(durations.begin(), durations.end(), std::greater<uint64_t>()); | ||
| 814 | + for (size_t k = 1; k < 6; ++k) { total_duration += durations[k]; } | ||
| 815 | + const double average_duration = static_cast<double>(total_duration) / 5; | ||
| 816 | + (begin + i)->best_perf = average_duration; | ||
| 817 | + if (best_perf > average_duration) { best_perf = average_duration; } | ||
| 818 | + } | ||
| 819 | + ClearProfilingRecords(); | ||
| 820 | +)"; | ||
| 600 | } | 821 | } |
| 601 | 822 | ||
| 602 | void TilingLib::GenPgoBatchCallback(std::stringstream &ss) const { | 823 | void TilingLib::GenPgoBatchCallback(std::stringstream &ss) const { |
| @@ -626,7 +847,7 @@ void TilingLib::GenPgoBatchCallback(std::stringstream &ss) const { | |||
| 626 | ss << " durations.push_back(kernel->end - kernel->start);" << std::endl; | 847 | ss << " durations.push_back(kernel->end - kernel->start);" << std::endl; |
| 627 | ss << " std::advance(it, 1);" << std::endl; | 848 | ss << " std::advance(it, 1);" << std::endl; |
| 628 | ss << " }" << std::endl; | 849 | ss << " }" << std::endl; |
| 629 | - ss << " std::sort(durations.begin(), durations.end(), std::greater<int>());" << std::endl; | 850 | + ss << " std::sort(durations.begin(), durations.end(), std::greater<uint64_t>());" << std::endl; |
| 630 | ss << " for (size_t k = 1; k < 6; ++k) {" << std::endl; | 851 | ss << " for (size_t k = 1; k < 6; ++k) {" << std::endl; |
| 631 | ss << " total_duration += durations[k];" << std::endl; | 852 | ss << " total_duration += durations[k];" << std::endl; |
| 632 | ss << " }" << std::endl; | 853 | ss << " }" << std::endl; |
| @@ -644,7 +865,47 @@ void TilingLib::GenPgoBatchCallback(std::stringstream &ss) const { | |||
| 644 | ss << " }" << std::endl; | 865 | ss << " }" << std::endl; |
| 645 | } | 866 | } |
| 646 | 867 | ||
| 647 | -void TilingLib::GenPgoBatchProcess(std::stringstream &ss) const { | 868 | +void TilingLib::GenPgoDirectBatchProcess(std::stringstream &ss) const { |
| 869 | + ss << R"(int ProfilingBatchProcess(uint32_t workspace_size, std::vector<AutofuseTilingDataPerf>::iterator begin, | ||
| 870 | + std::vector<AutofuseTilingDataPerf>::iterator end) { | ||
| 871 | + const uint64_t batch_size = end - begin; | ||
| 872 | + ResetProfilingRound(); | ||
| 873 | + msptiSubscriberHandle subscriber = nullptr; | ||
| 874 | + if (SetUpMspti(&subscriber) != MSPTI_SUCCESS) { return -1; } | ||
| 875 | + static int64_t count = 0; | ||
| 876 | + ++count; | ||
| 877 | + int64_t result = 0; | ||
| 878 | + for (auto it = begin; it != end; ++it) { | ||
| 879 | + it->best_perf = DBL_MAX; | ||
| 880 | + AutofuseTilingData &tiling_data = it->tiling_data; | ||
| 881 | + if (UpdateLaunchParam(tiling_data) != ACL_SUCCESS) { | ||
| 882 | + TearDownMspti(&subscriber); | ||
| 883 | + ClearProfilingRecords(); | ||
| 884 | + return -1; | ||
| 885 | + } | ||
| 886 | + for (uint64_t i = 0; i < loop; ++i) { | ||
| 887 | + result = WrapperOnlyLaunch(workspace_size, &tiling_data); | ||
| 888 | + if (result != 0) { | ||
| 889 | + DLOGE("ProfilingBatchProcess launch failed loop:%" PRIu64, i); | ||
| 890 | + TearDownMspti(&subscriber); | ||
| 891 | + ClearProfilingRecords(); | ||
| 892 | + return -1; | ||
| 893 | + } | ||
| 894 | + } | ||
| 895 | + } | ||
| 896 | +)"; | ||
| 897 | + GenPgoDirectBatchCallback(ss); | ||
| 898 | + ss << R"( return 0; | ||
| 899 | +} | ||
| 900 | + | ||
| 901 | +)"; | ||
| 902 | +} | ||
| 903 | + | ||
| 904 | +void TilingLib::GenPgoBatchProcess(std::stringstream &ss, bool direct_link) const { | ||
| 905 | + if (direct_link) { | ||
| 906 | + GenPgoDirectBatchProcess(ss); | ||
| 907 | + return; | ||
| 908 | + } | ||
| 648 | ss << "int ProfilingBatchProcess(uint32_t workspace_size, std::vector<AutofuseTilingDataPerf>::iterator begin, " | 909 | ss << "int ProfilingBatchProcess(uint32_t workspace_size, std::vector<AutofuseTilingDataPerf>::iterator begin, " |
| 649 | "std::vector<AutofuseTilingDataPerf>::iterator end) {" | 910 | "std::vector<AutofuseTilingDataPerf>::iterator end) {" |
| 650 | << std::endl; | 911 | << std::endl; |
| @@ -673,10 +934,11 @@ void TilingLib::GenPgoBatchProcess(std::stringstream &ss) const { | |||
| 673 | ss << "}" << std::endl << std::endl; | 934 | ss << "}" << std::endl << std::endl; |
| 674 | } | 935 | } |
| 675 | 936 | ||
| 676 | -void TilingLib::GenPgoGetProfilingBatch(const ascir::FusedScheduledResult &fused_schedule_result, | 937 | +void TilingLib::GenPgoProfilingBatchSetup(std::stringstream &ss, bool direct_link) const { |
| 677 | - std::stringstream &ss) const { | 938 | + if (direct_link) { |
| 678 | - ss << "extern \"C\" long int PGOGetProfilingBatch(" << PGOSearchFuncInputOutputCallBackDef(fused_schedule_result) | 939 | + ss << " (void)tensor_args;" << std::endl; |
| 679 | - << "void* stream, uint32_t workspace_size, std::vector<AutofuseTilingDataPerf> *profiles) {" << std::endl; | 940 | + ss << " (void)stream;" << std::endl; |
| 941 | + } | ||
| 680 | ss << " int case_num = profiles->size();" << std::endl; | 942 | ss << " int case_num = profiles->size();" << std::endl; |
| 681 | ss << " DLOGI(\"PGOGetProfilingBatch case_num:%d\", case_num);" << std::endl; | 943 | ss << " DLOGI(\"PGOGetProfilingBatch case_num:%d\", case_num);" << std::endl; |
| 682 | ss << " if (workspace_size > 0) {" << std::endl; | 944 | ss << " if (workspace_size > 0) {" << std::endl; |
| @@ -686,6 +948,13 @@ void TilingLib::GenPgoGetProfilingBatch(const ascir::FusedScheduledResult &fused | |||
| 686 | ss << " return FAILED;" << std::endl; | 948 | ss << " return FAILED;" << std::endl; |
| 687 | ss << " }" << std::endl; | 949 | ss << " }" << std::endl; |
| 688 | ss << " }" << std::endl; | 950 | ss << " }" << std::endl; |
| 951 | +} | ||
| 952 | + | ||
| 953 | +void TilingLib::GenPgoGetProfilingBatch(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 954 | + bool direct_link) const { | ||
| 955 | + ss << "extern \"C\" long int PGOGetProfilingBatch(" << PGOSearchFuncInputOutputCallBackDef(fused_schedule_result) | ||
| 956 | + << "void* stream, uint32_t workspace_size, std::vector<AutofuseTilingDataPerf> *profiles) {" << std::endl; | ||
| 957 | + GenPgoProfilingBatchSetup(ss, direct_link); | ||
| 689 | ss << " int64_t result = 0;" << std::endl; | 958 | ss << " int64_t result = 0;" << std::endl; |
| 690 | ss << " auto it = profiles->begin();" << std::endl; | 959 | ss << " auto it = profiles->begin();" << std::endl; |
| 691 | ss << " while (it != profiles->end()) {" << std::endl; | 960 | ss << " while (it != profiles->end()) {" << std::endl; |
| @@ -700,6 +969,15 @@ void TilingLib::GenPgoGetProfilingBatch(const ascir::FusedScheduledResult &fused | |||
| 700 | ss << " break;" << std::endl; | 969 | ss << " break;" << std::endl; |
| 701 | ss << " }" << std::endl; | 970 | ss << " }" << std::endl; |
| 702 | ss << " }" << std::endl; | 971 | ss << " }" << std::endl; |
| 972 | + if (direct_link) { | ||
| 973 | + ss << " if (result != 0) {" << std::endl; | ||
| 974 | + ss << " if (g_workspace != nullptr) {" << std::endl; | ||
| 975 | + ss << " aclrtFree(g_workspace);" << std::endl; | ||
| 976 | + ss << " g_workspace = nullptr;" << std::endl; | ||
| 977 | + ss << " }" << std::endl; | ||
| 978 | + ss << " return FAILED;" << std::endl; | ||
| 979 | + ss << " }" << std::endl; | ||
| 980 | + } | ||
| 703 | ss << " it = end_it;" << std::endl; | 981 | ss << " it = end_it;" << std::endl; |
| 704 | ss << " }" << std::endl; | 982 | ss << " }" << std::endl; |
| 705 | ss << " if (g_workspace != nullptr) {" << std::endl; | 983 | ss << " if (g_workspace != nullptr) {" << std::endl; |
| @@ -708,12 +986,41 @@ void TilingLib::GenPgoGetProfilingBatch(const ascir::FusedScheduledResult &fused | |||
| 708 | ss << " DLOGE(\"free workspace failed, ERROR: %d\", ret);" << std::endl; | 986 | ss << " DLOGE(\"free workspace failed, ERROR: %d\", ret);" << std::endl; |
| 709 | ss << " return FAILED;" << std::endl; | 987 | ss << " return FAILED;" << std::endl; |
| 710 | ss << " }" << std::endl; | 988 | ss << " }" << std::endl; |
| 989 | + if (direct_link) { | ||
| 990 | + ss << " g_workspace = nullptr;" << std::endl; | ||
| 991 | + } | ||
| 711 | ss << " }" << std::endl; | 992 | ss << " }" << std::endl; |
| 712 | ss << " return 0;" << std::endl; | 993 | ss << " return 0;" << std::endl; |
| 713 | ss << "}" << std::endl << std::endl; | 994 | ss << "}" << std::endl << std::endl; |
| 714 | } | 995 | } |
| 715 | 996 | ||
| 716 | -void TilingLib::GenPgoProfilingCallback(std::stringstream &ss) const { | 997 | +void TilingLib::GenPgoDirectProfilingCallback(std::stringstream &ss) const { |
| 998 | + ss << R"( result = aclrtSynchronizeStream(g_stream); | ||
| 999 | + const msptiResult teardown_result = TearDownMspti(&subscriber); | ||
| 1000 | + const msptiResult flush_result = FlushPgoActivities(loop); | ||
| 1001 | + if (result != ACL_SUCCESS || g_mspti_activity_error || teardown_result != MSPTI_SUCCESS || | ||
| 1002 | + flush_result != MSPTI_SUCCESS || | ||
| 1003 | + g_profiling_map.size() != loop) { | ||
| 1004 | + DLOGE("invalid activity: sync=%" PRId64 ", flush=%d, teardown=%d, error=%d, actual=%zu, expected=%" PRIu64, | ||
| 1005 | + result, flush_result, teardown_result, g_mspti_activity_error.load(), g_profiling_map.size(), loop); | ||
| 1006 | + ClearProfilingRecords(); | ||
| 1007 | + return -1; | ||
| 1008 | + } | ||
| 1009 | + uint64_t total_duration = 0; | ||
| 1010 | + std::vector<uint64_t> durations; | ||
| 1011 | + for (const auto &pair : g_profiling_map) { | ||
| 1012 | + auto *kernel = reinterpret_cast<msptiActivityKernel *>(pair.second); | ||
| 1013 | + durations.push_back(kernel->end - kernel->start); | ||
| 1014 | + } | ||
| 1015 | + std::sort(durations.begin(), durations.end(), std::greater<uint64_t>()); | ||
| 1016 | + for (size_t i = 1; i < 6; ++i) { total_duration += durations[i]; } | ||
| 1017 | + *outCostTime = static_cast<double>(total_duration) / 5; | ||
| 1018 | + if (best_perf > *outCostTime) { best_perf = *outCostTime; } | ||
| 1019 | + ClearProfilingRecords(); | ||
| 1020 | +)"; | ||
| 1021 | +} | ||
| 1022 | + | ||
| 1023 | +void TilingLib::GenPgoLegacyProfilingCallback(std::stringstream &ss) const { | ||
| 717 | ss << " result = aclrtSynchronizeStream(g_stream);" << std::endl; | 1024 | ss << " result = aclrtSynchronizeStream(g_stream);" << std::endl; |
| 718 | ss << " if (result != 0) {" << std::endl; | 1025 | ss << " if (result != 0) {" << std::endl; |
| 719 | ss << " DLOGE(\"sync stream failed\");" << std::endl; | 1026 | ss << " DLOGE(\"sync stream failed\");" << std::endl; |
| @@ -744,7 +1051,7 @@ void TilingLib::GenPgoProfilingCallback(std::stringstream &ss) const { | |||
| 744 | ss << " durations.push_back(kernel->end - kernel->start);" << std::endl; | 1051 | ss << " durations.push_back(kernel->end - kernel->start);" << std::endl; |
| 745 | ss << " DLOGD(\"kernel duration:%\" PRIu64 \"\", kernel->end - kernel->start);" << std::endl; | 1052 | ss << " DLOGD(\"kernel duration:%\" PRIu64 \"\", kernel->end - kernel->start);" << std::endl; |
| 746 | ss << " }" << std::endl; | 1053 | ss << " }" << std::endl; |
| 747 | - ss << " std::sort(durations.begin(), durations.end(), std::greater<int>());" << std::endl; | 1054 | + ss << " std::sort(durations.begin(), durations.end(), std::greater<uint64_t>());" << std::endl; |
| 748 | ss << " for (size_t i = 1; i < 6; ++i) {" << std::endl; | 1055 | ss << " for (size_t i = 1; i < 6; ++i) {" << std::endl; |
| 749 | ss << " total_duration += durations[i];" << std::endl; | 1056 | ss << " total_duration += durations[i];" << std::endl; |
| 750 | ss << " }" << std::endl; | 1057 | ss << " }" << std::endl; |
| @@ -762,10 +1069,19 @@ void TilingLib::GenPgoProfilingCallback(std::stringstream &ss) const { | |||
| 762 | ss << " }" << std::endl; | 1069 | ss << " }" << std::endl; |
| 763 | } | 1070 | } |
| 764 | 1071 | ||
| 765 | -void TilingLib::GenPgoGetProfiling(const ascir::FusedScheduledResult &fused_schedule_result, | 1072 | +void TilingLib::GenPgoProfilingCallback(std::stringstream &ss, bool direct_link) const { |
| 766 | - std::stringstream &ss) const { | 1073 | + if (direct_link) { |
| 767 | - ss << "extern \"C\" long int PGOGetProfiling(" << PGOSearchFuncInputOutputCallBackDef(fused_schedule_result) | 1074 | + GenPgoDirectProfilingCallback(ss); |
| 768 | - << "void *stream, uint32_t workspace_size, AutofuseTilingData *tiling_data, double *outCostTime) {" << std::endl; | 1075 | + return; |
| 1076 | + } | ||
| 1077 | + GenPgoLegacyProfilingCallback(ss); | ||
| 1078 | +} | ||
| 1079 | + | ||
| 1080 | +void TilingLib::GenPgoProfilingSetup(std::stringstream &ss, bool direct_link) const { | ||
| 1081 | + if (direct_link) { | ||
| 1082 | + ss << " (void)tensor_args;" << std::endl; | ||
| 1083 | + ss << " (void)stream;" << std::endl; | ||
| 1084 | + } | ||
| 769 | ss << " if (workspace_size > 0) {" << std::endl; | 1085 | ss << " if (workspace_size > 0) {" << std::endl; |
| 770 | ss << " auto ret = aclrtMalloc(&g_workspace, workspace_size, ACL_MEM_MALLOC_HUGE_FIRST);" << std::endl; | 1086 | ss << " auto ret = aclrtMalloc(&g_workspace, workspace_size, ACL_MEM_MALLOC_HUGE_FIRST);" << std::endl; |
| 771 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; | 1087 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; |
| @@ -773,33 +1089,74 @@ void TilingLib::GenPgoGetProfiling(const ascir::FusedScheduledResult &fused_sche | |||
| 773 | ss << " return FAILED;" << std::endl; | 1089 | ss << " return FAILED;" << std::endl; |
| 774 | ss << " }" << std::endl; | 1090 | ss << " }" << std::endl; |
| 775 | ss << " }" << std::endl; | 1091 | ss << " }" << std::endl; |
| 776 | - ss << " g_profiling_map.clear();" << std::endl; | 1092 | + if (direct_link) { |
| 777 | - ss << " msptiSubscriberHandle subscriber;" << std::endl; | 1093 | + ss << " ResetProfilingRound();" << std::endl; |
| 778 | - ss << " SetUpMspti(&subscriber);" << std::endl << std::endl; | 1094 | + ss << " msptiSubscriberHandle subscriber = nullptr;" << std::endl; |
| 1095 | + ss << " if (SetUpMspti(&subscriber) != MSPTI_SUCCESS) {" << std::endl; | ||
| 1096 | + ss << " if (g_workspace != nullptr) { aclrtFree(g_workspace); g_workspace = nullptr; }" << std::endl; | ||
| 1097 | + ss << " return -1;" << std::endl; | ||
| 1098 | + ss << " }" << std::endl << std::endl; | ||
| 1099 | + } else { | ||
| 1100 | + ss << " g_profiling_map.clear();" << std::endl; | ||
| 1101 | + ss << " msptiSubscriberHandle subscriber;" << std::endl; | ||
| 1102 | + ss << " SetUpMspti(&subscriber);" << std::endl << std::endl; | ||
| 1103 | + } | ||
| 779 | ss << " int64_t result = -1;" << std::endl; | 1104 | ss << " int64_t result = -1;" << std::endl; |
| 780 | ss << " *outCostTime = DBL_MAX;" << std::endl; | 1105 | ss << " *outCostTime = DBL_MAX;" << std::endl; |
| 781 | ss << " static int64_t count = 0;" << std::endl; | 1106 | ss << " static int64_t count = 0;" << std::endl; |
| 782 | ss << " count++;" << std::endl << std::endl; | 1107 | ss << " count++;" << std::endl << std::endl; |
| 1108 | +} | ||
| 783 | 1109 | ||
| 784 | - ss << " UpdateLaunchParam(*tiling_data);" << std::endl; | 1110 | +void TilingLib::GenPgoProfilingLaunch(std::stringstream &ss, bool direct_link) const { |
| 1111 | + if (direct_link) { | ||
| 1112 | + ss << " if (UpdateLaunchParam(*tiling_data) != ACL_SUCCESS) {" << std::endl; | ||
| 1113 | + ss << " TearDownMspti(&subscriber);" << std::endl; | ||
| 1114 | + ss << " ClearProfilingRecords();" << std::endl; | ||
| 1115 | + ss << " if (g_workspace != nullptr) { aclrtFree(g_workspace); g_workspace = nullptr; }" << std::endl; | ||
| 1116 | + ss << " return -1;" << std::endl; | ||
| 1117 | + ss << " }" << std::endl; | ||
| 1118 | + } else { | ||
| 1119 | + ss << " UpdateLaunchParam(*tiling_data);" << std::endl; | ||
| 1120 | + } | ||
| 785 | ss << " for (uint64_t j = 0; j < loop; ++j) {" << std::endl; | 1121 | ss << " for (uint64_t j = 0; j < loop; ++j) {" << std::endl; |
| 786 | ss << " result = WrapperOnlyLaunch(workspace_size, tiling_data);" << std::endl; | 1122 | ss << " result = WrapperOnlyLaunch(workspace_size, tiling_data);" << std::endl; |
| 787 | ss << " if (result != 0) {" << std::endl; | 1123 | ss << " if (result != 0) {" << std::endl; |
| 788 | ss << " DLOGE(\"launch failed loop:%\" PRIu64 \"\", j);" << std::endl; | 1124 | ss << " DLOGE(\"launch failed loop:%\" PRIu64 \"\", j);" << std::endl; |
| 789 | ss << " TearDownMspti(&subscriber);" << std::endl; | 1125 | ss << " TearDownMspti(&subscriber);" << std::endl; |
| 1126 | + if (direct_link) { | ||
| 1127 | + ss << " ClearProfilingRecords();" << std::endl; | ||
| 1128 | + ss << " if (g_workspace != nullptr) { aclrtFree(g_workspace); g_workspace = nullptr; }" << std::endl; | ||
| 1129 | + } | ||
| 790 | ss << " return -1;" << std::endl; | 1130 | ss << " return -1;" << std::endl; |
| 791 | ss << " }" << std::endl; | 1131 | ss << " }" << std::endl; |
| 792 | ss << " }" << std::endl << std::endl; | 1132 | ss << " }" << std::endl << std::endl; |
| 1133 | +} | ||
| 793 | 1134 | ||
| 1135 | +void TilingLib::GenPgoProfilingWorkspaceCleanup(std::stringstream &ss, bool direct_link) const { | ||
| 794 | ss << " if (g_workspace != nullptr) {" << std::endl; | 1136 | ss << " if (g_workspace != nullptr) {" << std::endl; |
| 795 | ss << " auto ret = aclrtFree(g_workspace);" << std::endl; | 1137 | ss << " auto ret = aclrtFree(g_workspace);" << std::endl; |
| 796 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; | 1138 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; |
| 797 | ss << " DLOGE(\"free workspace failed, ERROR: %d\", ret);" << std::endl; | 1139 | ss << " DLOGE(\"free workspace failed, ERROR: %d\", ret);" << std::endl; |
| 798 | ss << " TearDownMspti(&subscriber);" << std::endl; | 1140 | ss << " TearDownMspti(&subscriber);" << std::endl; |
| 1141 | + if (direct_link) { | ||
| 1142 | + ss << " ClearProfilingRecords();" << std::endl; | ||
| 1143 | + } | ||
| 799 | ss << " return FAILED;" << std::endl; | 1144 | ss << " return FAILED;" << std::endl; |
| 800 | ss << " }" << std::endl; | 1145 | ss << " }" << std::endl; |
| 1146 | + if (direct_link) { | ||
| 1147 | + ss << " g_workspace = nullptr;" << std::endl; | ||
| 1148 | + } | ||
| 801 | ss << " }" << std::endl; | 1149 | ss << " }" << std::endl; |
| 802 | - GenPgoProfilingCallback(ss); | 1150 | +} |
| 1151 | + | ||
| 1152 | +void TilingLib::GenPgoGetProfiling(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss, | ||
| 1153 | + bool direct_link) const { | ||
| 1154 | + ss << "extern \"C\" long int PGOGetProfiling(" << PGOSearchFuncInputOutputCallBackDef(fused_schedule_result) | ||
| 1155 | + << "void *stream, uint32_t workspace_size, AutofuseTilingData *tiling_data, double *outCostTime) {" << std::endl; | ||
| 1156 | + GenPgoProfilingSetup(ss, direct_link); | ||
| 1157 | + GenPgoProfilingLaunch(ss, direct_link); | ||
| 1158 | + GenPgoProfilingWorkspaceCleanup(ss, direct_link); | ||
| 1159 | + GenPgoProfilingCallback(ss, direct_link); | ||
| 803 | ss << " return 0;" << std::endl; | 1160 | ss << " return 0;" << std::endl; |
| 804 | ss << "}" << std::endl << std::endl; | 1161 | ss << "}" << std::endl << std::endl; |
| 805 | } | 1162 | } |
| @@ -861,10 +1218,6 @@ void TilingLib::GenPgoStaticFunc(const ascir::FusedScheduledResult &fused_schedu | |||
| 861 | } | 1218 | } |
| 862 | 1219 | ||
| 863 | void TilingLib::GenPgoProfiling(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const { | 1220 | void TilingLib::GenPgoProfiling(const ascir::FusedScheduledResult &fused_schedule_result, std::stringstream &ss) const { |
| 864 | - GenPgoMsptiProfiling(ss); | ||
| 865 | - GenPgoBatchProcess(ss); | ||
| 866 | - GenPgoGetProfilingBatch(fused_schedule_result, ss); | ||
| 867 | - GenPgoGetProfiling(fused_schedule_result, ss); | ||
| 868 | ss << "typedef int64_t (*PGOSearchType)(char *search_file, char *config_file, AutofuseTilingData *tiling_data, " | 1221 | ss << "typedef int64_t (*PGOSearchType)(char *search_file, char *config_file, AutofuseTilingData *tiling_data, " |
| 869 | "uint32_t *workspace_size, uint32_t *blockDim, void *resource_limit, " | 1222 | "uint32_t *workspace_size, uint32_t *blockDim, void *resource_limit, " |
| 870 | << PGOSearchFuncInputOutputCallBackDef(fused_schedule_result) | 1223 | << PGOSearchFuncInputOutputCallBackDef(fused_schedule_result) |
| @@ -948,18 +1301,21 @@ void TilingLib::GenPgoEnvInit(const ascir::FusedScheduledResult &fused_schedule_ | |||
| 948 | ss << " }" << std::endl; | 1301 | ss << " }" << std::endl; |
| 949 | } | 1302 | } |
| 950 | 1303 | ||
| 951 | -void TilingLib::GenPgoLaunchKernelInit(std::stringstream &ss) const { | 1304 | +void TilingLib::GenInductorPgoKernelFunctionInit(std::stringstream &ss) const { |
| 952 | - ss << " if (!inited) {" << std::endl; | 1305 | + ss << " aclrtFuncHandle func_handle = nullptr;" << std::endl; |
| 953 | - ss << " auto ret = aclrtBinaryLoadFromFile(g_kernel_o_file.c_str(), nullptr, &bin_handle);" << std::endl; | 1306 | + ss << " ret = aclrtBinaryGetFunction(g_pgo_bin_handle, kInductorPgoKernelName, &func_handle);" << std::endl; |
| 954 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; | 1307 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; |
| 955 | - ss << " DLOGE(\"acl load binary from file failed, ERROR: %d\", ret);" << std::endl; | 1308 | + ss << " DLOGE(\"acl get function failed, ERROR: %d\", ret);" << std::endl; |
| 956 | ss << " return FAILED;" << std::endl; | 1309 | ss << " return FAILED;" << std::endl; |
| 957 | ss << " }" << std::endl; | 1310 | ss << " }" << std::endl; |
| 1311 | + ss << " std::fill(func_handles.begin(), func_handles.end(), func_handle);" << std::endl; | ||
| 1312 | +} | ||
| 1313 | + | ||
| 1314 | +void TilingLib::GenPgoKernelFunctionsInit(const std::string &bin_handle, std::stringstream &ss) const { | ||
| 958 | ss << " if (g_is_static_kernel) {" << std::endl; | 1315 | ss << " if (g_is_static_kernel) {" << std::endl; |
| 959 | ss << " aclrtFuncHandle func_handle = nullptr;" << std::endl; | 1316 | ss << " aclrtFuncHandle func_handle = nullptr;" << std::endl; |
| 960 | - ss << " ret = aclrtBinaryGetFunction(bin_handle, (g_kernel_name + \"_\" + std::to_string(tiling_key)).c_str(), " | 1317 | + ss << " ret = aclrtBinaryGetFunction(" << bin_handle |
| 961 | - "&func_handle);" | 1318 | + << ", (g_kernel_name + \"_\" + std::to_string(tiling_key)).c_str(), &func_handle);" << std::endl; |
| 962 | - << std::endl; | ||
| 963 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; | 1319 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; |
| 964 | ss << " DLOGE(\"acl get function failed, ERROR: %d\", ret);" << std::endl; | 1320 | ss << " DLOGE(\"acl get function failed, ERROR: %d\", ret);" << std::endl; |
| 965 | ss << " return FAILED;" << std::endl; | 1321 | ss << " return FAILED;" << std::endl; |
| @@ -968,9 +1324,8 @@ void TilingLib::GenPgoLaunchKernelInit(std::stringstream &ss) const { | |||
| 968 | ss << " } else {" << std::endl; | 1324 | ss << " } else {" << std::endl; |
| 969 | ss << " for (uint64_t i = 0; i < tiling_key_count; ++i) {" << std::endl; | 1325 | ss << " for (uint64_t i = 0; i < tiling_key_count; ++i) {" << std::endl; |
| 970 | ss << " aclrtFuncHandle func_handle = nullptr;" << std::endl; | 1326 | ss << " aclrtFuncHandle func_handle = nullptr;" << std::endl; |
| 971 | - ss << " ret = aclrtBinaryGetFunction(bin_handle, (g_kernel_name + \"_\" + std::to_string(i)).c_str(), " | 1327 | + ss << " ret = aclrtBinaryGetFunction(" << bin_handle |
| 972 | - "&func_handle);" | 1328 | + << ", (g_kernel_name + \"_\" + std::to_string(i)).c_str(), &func_handle);" << std::endl; |
| 973 | - << std::endl; | ||
| 974 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; | 1329 | ss << " if (ret != ACL_SUCCESS) {" << std::endl; |
| 975 | ss << " DLOGE(\"acl get function failed, ERROR: %d\", ret);" << std::endl; | 1330 | ss << " DLOGE(\"acl get function failed, ERROR: %d\", ret);" << std::endl; |
| 976 | ss << " return FAILED;" << std::endl; | 1331 | ss << " return FAILED;" << std::endl; |
| @@ -978,6 +1333,21 @@ void TilingLib::GenPgoLaunchKernelInit(std::stringstream &ss) const { | |||
| 978 | ss << " func_handles[i] = func_handle;" << std::endl; | 1333 | ss << " func_handles[i] = func_handle;" << std::endl; |
| 979 | ss << " }" << std::endl; | 1334 | ss << " }" << std::endl; |
| 980 | ss << " }" << std::endl; | 1335 | ss << " }" << std::endl; |
| 1336 | +} | ||
| 1337 | + | ||
| 1338 | +void TilingLib::GenPgoLaunchKernelInit(std::stringstream &ss, bool direct_link) const { | ||
| 1339 | + const std::string bin_handle = direct_link ? "g_pgo_bin_handle" : "bin_handle"; | ||
| 1340 | + ss << " if (!inited) {" << std::endl; | ||
| 1341 | + ss << " auto ret = aclrtBinaryLoadFromFile(g_kernel_o_file.c_str(), nullptr, &" << bin_handle << ");" << std::endl; | ||
| 1342 | + ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 1343 | + ss << " DLOGE(\"acl load binary from file failed, ERROR: %d\", ret);" << std::endl; | ||
| 1344 | + ss << " return FAILED;" << std::endl; | ||
| 1345 | + ss << " }" << std::endl; | ||
| 1346 | + if (direct_link) { | ||
| 1347 | + GenInductorPgoKernelFunctionInit(ss); | ||
| 1348 | + } else { | ||
| 1349 | + GenPgoKernelFunctionsInit(bin_handle, ss); | ||
| 1350 | + } | ||
| 981 | const auto backend_spce = optimize::BackendSpec::GetInstance(); | 1351 | const auto backend_spce = optimize::BackendSpec::GetInstance(); |
| 982 | if (backend_spce != nullptr && backend_spce->set_local_memory_size > 0) { | 1352 | if (backend_spce != nullptr && backend_spce->set_local_memory_size > 0) { |
| 983 | ss << " local_memory_size_attr.id = ACL_RT_LAUNCH_KERNEL_ATTR_DYN_UBUF_SIZE;" << std::endl; | 1353 | ss << " local_memory_size_attr.id = ACL_RT_LAUNCH_KERNEL_ATTR_DYN_UBUF_SIZE;" << std::endl; |
| @@ -11,6 +11,8 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | + | ||
| 15 | + | ||
| 14 | 16 | ||
| 15 | 17 | ||
| 16 | 18 | ||
| @@ -24,6 +26,323 @@ using namespace af::ops; | |||
| 24 | using namespace ascgen_utils; | 26 | using namespace ascgen_utils; |
| 25 | using namespace ascir; | 27 | using namespace ascir; |
| 26 | 28 | ||
| 29 | +std::string TilingLib::CalculateTensorMemorySizeStr(const ascir::TensorAttr &tensor) const { | ||
| 30 | + return CalculateTensorMemorySizeStr(tensor, af::ops::Zero); | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +namespace { | ||
| 34 | +bool GetTensorTypeSize(ge::DataType dtype, uint64_t &type_size) { | ||
| 35 | + static const std::unordered_map<ge::DataType, uint64_t> type_size_map = { | ||
| 36 | + {ge::DT_FLOAT, 4U}, {ge::DT_FLOAT16, 2U}, {ge::DT_INT8, 1U}, {ge::DT_INT16, 2U}, {ge::DT_INT32, 4U}, | ||
| 37 | + {ge::DT_INT64, 8U}, {ge::DT_UINT8, 1U}, {ge::DT_UINT16, 2U}, {ge::DT_UINT32, 4U}, {ge::DT_UINT64, 8U}, | ||
| 38 | + {ge::DT_DOUBLE, 8U}, {ge::DT_BF16, 2U}, {ge::DT_BOOL, 1U}}; | ||
| 39 | + const auto iter = type_size_map.find(dtype); | ||
| 40 | + if (iter == type_size_map.end()) { | ||
| 41 | + return false; | ||
| 42 | + } | ||
| 43 | + type_size = iter->second; | ||
| 44 | + return true; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +bool CheckedPgoSizeAdd(uint64_t lhs, uint64_t rhs, uint64_t &result) { | ||
| 48 | + constexpr uint64_t kMaxPgoIoSize = static_cast<uint64_t>(std::numeric_limits<int64_t>::max()); | ||
| 49 | + if (lhs > kMaxPgoIoSize - rhs) { | ||
| 50 | + return false; | ||
| 51 | + } | ||
| 52 | + result = lhs + rhs; | ||
| 53 | + return true; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +bool CheckedPgoSizeMul(uint64_t lhs, uint64_t rhs, uint64_t &result) { | ||
| 57 | + constexpr uint64_t kMaxPgoIoSize = static_cast<uint64_t>(std::numeric_limits<int64_t>::max()); | ||
| 58 | + if (lhs != 0U && rhs > kMaxPgoIoSize / lhs) { | ||
| 59 | + return false; | ||
| 60 | + } | ||
| 61 | + result = lhs * rhs; | ||
| 62 | + return true; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +enum class ConstMemorySizeStatus { kSuccess, kSymbolic, kInvalid }; | ||
| 66 | + | ||
| 67 | +ConstMemorySizeStatus CalculateConstMemorySize(const ascir::TensorAttr &tensor, const af::Expression &element_offset, | ||
| 68 | + uint64_t type_size, uint64_t &memory_size) { | ||
| 69 | + bool all_const = element_offset.IsConstExpr(); | ||
| 70 | + int64_t offset = 0; | ||
| 71 | + if ((all_const && !element_offset.GetConstValue(offset)) || offset < 0) { | ||
| 72 | + return ConstMemorySizeStatus::kInvalid; | ||
| 73 | + } | ||
| 74 | + std::vector<std::pair<int64_t, int64_t>> dims; | ||
| 75 | + bool has_zero_repeat = false; | ||
| 76 | + for (size_t i = 0UL; i < tensor.attr.repeats.size(); ++i) { | ||
| 77 | + int64_t repeat = 0; | ||
| 78 | + int64_t stride = 0; | ||
| 79 | + const bool dim_const = tensor.attr.repeats[i].IsConstExpr() && tensor.attr.strides[i].IsConstExpr(); | ||
| 80 | + if (dim_const && (!tensor.attr.repeats[i].GetConstValue(repeat) || !tensor.attr.strides[i].GetConstValue(stride))) { | ||
| 81 | + return ConstMemorySizeStatus::kInvalid; | ||
| 82 | + } | ||
| 83 | + if (dim_const && (repeat < 0 || stride < 0)) { | ||
| 84 | + return ConstMemorySizeStatus::kInvalid; | ||
| 85 | + } | ||
| 86 | + has_zero_repeat = has_zero_repeat || (dim_const && repeat == 0); | ||
| 87 | + all_const = all_const && dim_const; | ||
| 88 | + dims.emplace_back(repeat, stride); | ||
| 89 | + } | ||
| 90 | + if (has_zero_repeat) { | ||
| 91 | + memory_size = 0U; | ||
| 92 | + return ConstMemorySizeStatus::kSuccess; | ||
| 93 | + } | ||
| 94 | + if (!all_const) { | ||
| 95 | + return ConstMemorySizeStatus::kSymbolic; | ||
| 96 | + } | ||
| 97 | + uint64_t span = static_cast<uint64_t>(offset) + 1U; | ||
| 98 | + for (const auto &[repeat, stride] : dims) { | ||
| 99 | + uint64_t contribution = 0U; | ||
| 100 | + if (!CheckedPgoSizeMul(static_cast<uint64_t>(repeat - 1), static_cast<uint64_t>(stride), contribution) || | ||
| 101 | + !CheckedPgoSizeAdd(span, contribution, span)) { | ||
| 102 | + return ConstMemorySizeStatus::kInvalid; | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + return CheckedPgoSizeMul(span, type_size, memory_size) ? ConstMemorySizeStatus::kSuccess | ||
| 106 | + : ConstMemorySizeStatus::kInvalid; | ||
| 107 | +} | ||
| 108 | +} // namespace | ||
| 109 | + | ||
| 110 | +std::string TilingLib::CalculateTensorMemorySizeStr(const ascir::TensorAttr &tensor, | ||
| 111 | + const af::Expression &element_offset) const { | ||
| 112 | + const auto dtype = tensor.attr.dtype.operator ge::DataType(); | ||
| 113 | + uint64_t type_size = 0U; | ||
| 114 | + if (!GetTensorTypeSize(dtype, type_size)) { | ||
| 115 | + GELOGE(ge::GRAPH_FAILED, "Unsupported data type: %d", static_cast<int32_t>(dtype)); | ||
| 116 | + return "0"; | ||
| 117 | + } | ||
| 118 | + if (tensor.attr.repeats.empty() || tensor.attr.repeats.size() != tensor.attr.strides.size()) { | ||
| 119 | + GELOGE(ge::GRAPH_FAILED, "Invalid repeats or strides when calculating tensor memory size"); | ||
| 120 | + return "0"; | ||
| 121 | + } | ||
| 122 | + uint64_t const_memory_size = 0U; | ||
| 123 | + const auto const_status = CalculateConstMemorySize(tensor, element_offset, type_size, const_memory_size); | ||
| 124 | + if (const_status == ConstMemorySizeStatus::kSuccess) { | ||
| 125 | + return std::to_string(const_memory_size); | ||
| 126 | + } | ||
| 127 | + if (const_status == ConstMemorySizeStatus::kInvalid) { | ||
| 128 | + GELOGE(ge::GRAPH_FAILED, "Tensor memory size has invalid layout or overflow"); | ||
| 129 | + return "0"; | ||
| 130 | + } | ||
| 131 | + af::Expression element_span = af::sym::Add(element_offset, af::ops::One); | ||
| 132 | + for (size_t i = 0UL; i < tensor.attr.repeats.size(); ++i) { | ||
| 133 | + const auto repeat_span = af::sym::Sub(tensor.attr.repeats[i], af::ops::One); | ||
| 134 | + element_span = af::sym::Add(element_span, af::sym::Mul(repeat_span, tensor.attr.strides[i])); | ||
| 135 | + } | ||
| 136 | + const auto type_size_expr = af::Expression::Parse(std::to_string(type_size).c_str()); | ||
| 137 | + af::Expression need_malloc_size = af::sym::Mul(element_span.Simplify(), type_size_expr).Simplify(); | ||
| 138 | + GELOGD("Tensor element span: %s, need malloc size: %s", element_span.Str().get(), need_malloc_size.Str().get()); | ||
| 139 | + return std::string(need_malloc_size.Str().get()); | ||
| 140 | +} | ||
| 141 | + | ||
| 142 | +namespace { | ||
| 143 | +enum class PgoIoKind { kInput, kOutput }; | ||
| 144 | +using PgoVarReplacements = std::vector<std::pair<af::Expression, af::Expression>>; | ||
| 145 | + | ||
| 146 | +PgoVarReplacements BuildPgoVarReplacements(const ascir::ScheduledResult &result, size_t group_id) { | ||
| 147 | + PgoVarReplacements replacements; | ||
| 148 | + const auto dst_group_iter = result.var_relations.find(group_id); | ||
| 149 | + if (dst_group_iter == result.var_relations.end()) { | ||
| 150 | + return replacements; | ||
| 151 | + } | ||
| 152 | + for (const auto &relations_by_src : dst_group_iter->second) { | ||
| 153 | + for (const auto &[dst_var_name, src_expr] : relations_by_src.second) { | ||
| 154 | + replacements.emplace_back(af::Expression::Parse(dst_var_name.c_str()), src_expr); | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + return replacements; | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +af::Expression ApplyPgoVarReplacements(const af::Expression &expr, const PgoVarReplacements &replacements) { | ||
| 161 | + return replacements.empty() ? expr : expr.Replace(replacements).Simplify(); | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +ascir::TensorAttr ApplyPgoVarReplacements(const ascir::TensorAttr &tensor, const PgoVarReplacements &replacements) { | ||
| 165 | + ascir::TensorAttr replaced_tensor = tensor; | ||
| 166 | + for (auto &repeat : replaced_tensor.attr.repeats) { | ||
| 167 | + repeat = ApplyPgoVarReplacements(repeat, replacements); | ||
| 168 | + } | ||
| 169 | + for (auto &stride : replaced_tensor.attr.strides) { | ||
| 170 | + stride = ApplyPgoVarReplacements(stride, replacements); | ||
| 171 | + } | ||
| 172 | + return replaced_tensor; | ||
| 173 | +} | ||
| 174 | + | ||
| 175 | +int64_t GetPgoIoIndex(const af::AscNodePtr &node, int64_t fallback_index) { | ||
| 176 | + int64_t index = fallback_index; | ||
| 177 | + if (node == nullptr || node->attr.ir_attr == nullptr) { | ||
| 178 | + return index; | ||
| 179 | + } | ||
| 180 | + (void)node->attr.ir_attr->GetAttrValue("index", index); | ||
| 181 | + return index; | ||
| 182 | +} | ||
| 183 | + | ||
| 184 | +bool IsPgoIoNode(const af::AscNodePtr &node, int64_t io_index, PgoIoKind kind) { | ||
| 185 | + const bool type_matches = | ||
| 186 | + kind == PgoIoKind::kInput ? IsOps<Data>(node) || IsOps<ScalarData>(node) : IsOps<Output>(node); | ||
| 187 | + return type_matches && GetPgoIoIndex(node, -1) == io_index; | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | +template <typename TensorVisitor> | ||
| 191 | +void VisitPgoNodeTensors(const af::AscNodePtr &node, PgoIoKind kind, TensorVisitor &visitor) { | ||
| 192 | + if (kind == PgoIoKind::kOutput) { | ||
| 193 | + const auto &input_nodes = node->GetInDataNodes(); | ||
| 194 | + const auto *access_node = input_nodes.empty() ? nullptr : static_cast<const af::AscNode *>(input_nodes.at(0).get()); | ||
| 195 | + visitor(node->inputs[0], access_node); | ||
| 196 | + return; | ||
| 197 | + } | ||
| 198 | + for (auto *out_node : node->GetOutNodesPtr()) { | ||
| 199 | + auto *asc_out_node = static_cast<af::AscNode *>(out_node); | ||
| 200 | + if (asc_out_node != nullptr) { | ||
| 201 | + visitor(asc_out_node->outputs[0], asc_out_node); | ||
| 202 | + } | ||
| 203 | + } | ||
| 204 | +} | ||
| 205 | + | ||
| 206 | +template <typename TensorVisitor> | ||
| 207 | +void VisitPgoGraphTensors(const af::AscGraph &graph, int64_t io_index, PgoIoKind kind, | ||
| 208 | + const PgoVarReplacements &replacements, TensorVisitor &visitor) { | ||
| 209 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 210 | + if (IsPgoIoNode(node, io_index, kind)) { | ||
| 211 | + auto replace_and_visit = [&replacements, &visitor](const ascir::TensorAttr &tensor, | ||
| 212 | + const af::AscNode *access_node) { | ||
| 213 | + visitor(ApplyPgoVarReplacements(tensor, replacements), access_node, replacements); | ||
| 214 | + }; | ||
| 215 | + VisitPgoNodeTensors(node, kind, replace_and_visit); | ||
| 216 | + } | ||
| 217 | + } | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +template <typename TensorVisitor> | ||
| 221 | +void VisitPgoCandidateTensors(const ascir::FusedScheduledResult &fused_schedule_result, int64_t io_index, | ||
| 222 | + PgoIoKind kind, TensorVisitor &visitor) { | ||
| 223 | + for (const auto &scheduled_results : fused_schedule_result.node_idx_to_scheduled_results) { | ||
| 224 | + for (const auto &result : scheduled_results) { | ||
| 225 | + for (size_t group_id = 0UL; group_id < result.schedule_groups.size(); ++group_id) { | ||
| 226 | + const auto replacements = BuildPgoVarReplacements(result, group_id); | ||
| 227 | + const auto &schedule_group = result.schedule_groups[group_id]; | ||
| 228 | + for (const auto &impl_graph : schedule_group.impl_graphs) { | ||
| 229 | + VisitPgoGraphTensors(impl_graph, io_index, kind, replacements, visitor); | ||
| 230 | + } | ||
| 231 | + } | ||
| 232 | + } | ||
| 233 | + } | ||
| 234 | +} | ||
| 235 | + | ||
| 236 | +void AppendPgoTensorMalloc(std::stringstream &ss, const std::string &tensor_name, | ||
| 237 | + const std::vector<std::string> &size_expressions) { | ||
| 238 | + if (size_expressions.empty()) { | ||
| 239 | + return; | ||
| 240 | + } | ||
| 241 | + if (std::find(size_expressions.begin(), size_expressions.end(), "") != size_expressions.end()) { | ||
| 242 | + ss << " DLOGE(\"Invalid or symbolic PGO " << tensor_name << " memory size\");" << std::endl; | ||
| 243 | + ss << " return FAILED;" << std::endl; | ||
| 244 | + return; | ||
| 245 | + } | ||
| 246 | + const std::string size_name = tensor_name + "_size"; | ||
| 247 | + ss << " size_t " << size_name << " = " << size_expressions[0] << ";" << std::endl; | ||
| 248 | + for (size_t i = 1UL; i < size_expressions.size(); ++i) { | ||
| 249 | + ss << " " << size_name << " = std::max(" << size_name << ", static_cast<size_t>(" << size_expressions[i] << "));" | ||
| 250 | + << std::endl; | ||
| 251 | + } | ||
| 252 | + ss << " ret = aclrtMalloc(&" << tensor_name << ", " << size_name << ", ACL_MEM_MALLOC_HUGE_FIRST);" << std::endl; | ||
| 253 | + ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 254 | + ss << " DLOGE(\"aclrtMalloc " << tensor_name << " failed. ERROR: %d\", ret);" << std::endl; | ||
| 255 | + ss << " return FAILED;" << std::endl; | ||
| 256 | + ss << " }" << std::endl; | ||
| 257 | +} | ||
| 258 | +} // namespace | ||
| 259 | + | ||
| 260 | +std::vector<std::string> TilingLib::CalculatePgoIoMemorySizeStrs( | ||
| 261 | + const ascir::FusedScheduledResult &fused_schedule_result, int64_t io_index, bool is_input, | ||
| 262 | + const ascir::TensorAttr &fallback_tensor) const { | ||
| 263 | + std::vector<std::string> size_expressions; | ||
| 264 | + std::set<std::string> seen_expressions; | ||
| 265 | + auto append_size = [this, &size_expressions, &seen_expressions](const ascir::TensorAttr &tensor, | ||
| 266 | + const af::AscNode *access_node, | ||
| 267 | + const PgoVarReplacements &replacements) { | ||
| 268 | + af::Expression element_offset = af::ops::Zero; | ||
| 269 | + if (access_node != nullptr && access_node->attr.ir_attr != nullptr) { | ||
| 270 | + (void)access_node->attr.ir_attr->GetAttrValue("offset", element_offset); | ||
| 271 | + } | ||
| 272 | + element_offset = ApplyPgoVarReplacements(element_offset, replacements); | ||
| 273 | + const std::string size_expression = CalculateTensorMemorySizeStr(tensor, element_offset); | ||
| 274 | + if (size_expression == "0" || !af::Expression::Parse(size_expression.c_str()).IsConstExpr()) { | ||
| 275 | + GELOGD("Reject invalid or symbolic PGO candidate memory size: %s", size_expression.c_str()); | ||
| 276 | + if (seen_expressions.emplace("").second) { | ||
| 277 | + size_expressions.emplace_back(""); | ||
| 278 | + } | ||
| 279 | + return; | ||
| 280 | + } | ||
| 281 | + if (seen_expressions.emplace(size_expression).second) { | ||
| 282 | + size_expressions.emplace_back(size_expression); | ||
| 283 | + } | ||
| 284 | + }; | ||
| 285 | + append_size(fallback_tensor, nullptr, {}); | ||
| 286 | + const PgoIoKind kind = is_input ? PgoIoKind::kInput : PgoIoKind::kOutput; | ||
| 287 | + VisitPgoCandidateTensors(fused_schedule_result, io_index, kind, append_size); | ||
| 288 | + return size_expressions; | ||
| 289 | +} | ||
| 290 | + | ||
| 291 | +std::string TilingLib::PGOSearchTensorMallocDef(const ascir::FusedScheduledResult &fused_schedule_result) const { | ||
| 292 | + std::stringstream ss; | ||
| 293 | + int index = 0; | ||
| 294 | + for (auto &input : fused_schedule_result.input_nodes) { | ||
| 295 | + if (input->GetOutNodesPtr().empty()) { | ||
| 296 | + continue; | ||
| 297 | + } | ||
| 298 | + af::Node *out_node = input->GetOutNodesPtr()[0]; | ||
| 299 | + af::AscNode *asc_out_node = static_cast<af::AscNode *>(out_node); | ||
| 300 | + const auto size_expressions = CalculatePgoIoMemorySizeStrs(fused_schedule_result, GetPgoIoIndex(input, index), true, | ||
| 301 | + asc_out_node->outputs[0]); | ||
| 302 | + AppendPgoTensorMalloc(ss, "input" + std::to_string(index), size_expressions); | ||
| 303 | + index++; | ||
| 304 | + } | ||
| 305 | + index = 0; | ||
| 306 | + for (auto &output : fused_schedule_result.output_nodes) { | ||
| 307 | + if (af::ops::IsOps<af::ascir_op::Output>(output)) { | ||
| 308 | + const auto size_expressions = | ||
| 309 | + CalculatePgoIoMemorySizeStrs(fused_schedule_result, GetPgoIoIndex(output, index), false, output->inputs[0]); | ||
| 310 | + AppendPgoTensorMalloc(ss, "output" + std::to_string(index), size_expressions); | ||
| 311 | + index++; | ||
| 312 | + } | ||
| 313 | + } | ||
| 314 | + return ss.str(); | ||
| 315 | +} | ||
| 316 | + | ||
| 317 | +std::string TilingLib::PGOSearchTensorFreeDef(const ascir::FusedScheduledResult &fused_schedule_result) const { | ||
| 318 | + std::stringstream ss; | ||
| 319 | + int index = 0; | ||
| 320 | + for ([[maybe_unused]] auto &input : fused_schedule_result.input_nodes) { | ||
| 321 | + ss << " if (input" << index << " != nullptr) {" << std::endl; | ||
| 322 | + ss << " ret = aclrtFree(input" << index << ");" << std::endl; | ||
| 323 | + ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 324 | + ss << " DLOGW(\"aclrtFree input" << index << " failed. ERROR: %d\", ret);" << std::endl; | ||
| 325 | + ss << " }" << std::endl; | ||
| 326 | + ss << " input" << index << " = nullptr;" << std::endl; | ||
| 327 | + ss << " }" << std::endl; | ||
| 328 | + index++; | ||
| 329 | + } | ||
| 330 | + index = 0; | ||
| 331 | + for (auto &output : fused_schedule_result.output_nodes) { | ||
| 332 | + if (af::ops::IsOps<af::ascir_op::Output>(output)) { | ||
| 333 | + ss << " if (output" << index << " != nullptr) {" << std::endl; | ||
| 334 | + ss << " ret = aclrtFree(output" << index << ");" << std::endl; | ||
| 335 | + ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 336 | + ss << " DLOGW(\"aclrtFree output" << index << " failed. ERROR: %d\", ret);" << std::endl; | ||
| 337 | + ss << " }" << std::endl; | ||
| 338 | + ss << " output" << index << " = nullptr;" << std::endl; | ||
| 339 | + ss << " }" << std::endl; | ||
| 340 | + index++; | ||
| 341 | + } | ||
| 342 | + } | ||
| 343 | + return ss.str(); | ||
| 344 | +} | ||
| 345 | + | ||
| 27 | std::string TilingLib::ExternFunctionDeclare(const ascir::FusedScheduledResult &fused_schedule_result, | 346 | std::string TilingLib::ExternFunctionDeclare(const ascir::FusedScheduledResult &fused_schedule_result, |
| 28 | const std::string tiling) const { | 347 | const std::string tiling) const { |
| 29 | (void)tiling; | 348 | (void)tiling; |
| @@ -62,9 +381,11 @@ void TilingLib::AppendPgoConfigDef(std::stringstream &ss) const { | |||
| 62 | ss << " pgo_ub_threshold_list = {0.2, 0.1, 0, 0.05, 0.1};" << std::endl; | 381 | ss << " pgo_ub_threshold_list = {0.2, 0.1, 0, 0.05, 0.1};" << std::endl; |
| 63 | ss << " pgo_corenum_threshold_list = {0.4, 0.4, 1, 1, 0.8};" << std::endl; | 382 | ss << " pgo_corenum_threshold_list = {0.4, 0.4, 1, 1, 0.8};" << std::endl; |
| 64 | ss << " }" << std::endl; | 383 | ss << " }" << std::endl; |
| 65 | - ss << " ProfilingCallback single_callback;" << std::endl; | 384 | + ss << " ProfilingCallback single_callback = nullptr;" << std::endl; |
| 66 | - ss << " ProfilingBatchCallback batch_callback;" << std::endl; | 385 | + ss << " ProfilingBatchCallback batch_callback = nullptr;" << std::endl; |
| 67 | ss << " PgoTensorArgs *tensor_args = nullptr;" << std::endl; | 386 | ss << " PgoTensorArgs *tensor_args = nullptr;" << std::endl; |
| 387 | + ss << " std::vector<AutofuseTilingDataPerf> *measured_candidates = nullptr;" << std::endl; | ||
| 388 | + ss << " void *stream = nullptr;" << std::endl; | ||
| 68 | ss << " int32_t pgo_algorithm = 1; // 0 for pruning, 1 for core num" << std::endl; | 389 | ss << " int32_t pgo_algorithm = 1; // 0 for pruning, 1 for core num" << std::endl; |
| 69 | ss << " bool need_change_solver_run = false;" << std::endl; | 390 | ss << " bool need_change_solver_run = false;" << std::endl; |
| 70 | ss << " size_t pgo_threshold_index = 0;" << std::endl; | 391 | ss << " size_t pgo_threshold_index = 0;" << std::endl; |
| @@ -220,116 +541,4 @@ uint32_t TilingLib::PGOSearchFuncGetOutputCount(const ascir::FusedScheduledResul | |||
| 220 | return count; | 541 | return count; |
| 221 | } | 542 | } |
| 222 | 543 | ||
| 223 | -std::string TilingLib::CalculateTensorMemorySizeStr(const ascir::TensorAttr &tensor) const { | ||
| 224 | - static const std::unordered_map<ge::DataType, af::Expression> type_size_map = { | ||
| 225 | - {ge::DT_FLOAT, af::Expression::Parse("4")}, // sizeof(float) | ||
| 226 | - {ge::DT_FLOAT16, af::Expression::Parse("2")}, // fp16 is 2 bytes | ||
| 227 | - {ge::DT_INT8, af::Expression::Parse("1")}, // sizeof(int8_t) | ||
| 228 | - {ge::DT_INT16, af::Expression::Parse("2")}, // sizeof(int16_t) | ||
| 229 | - {ge::DT_INT32, af::Expression::Parse("4")}, // sizeof(int32_t) | ||
| 230 | - {ge::DT_INT64, af::Expression::Parse("8")}, // sizeof(int64_t) | ||
| 231 | - {ge::DT_UINT8, af::Expression::Parse("1")}, // sizeof(uint8_t) | ||
| 232 | - {ge::DT_UINT16, af::Expression::Parse("2")}, // sizeof(uint16_t) | ||
| 233 | - {ge::DT_UINT32, af::Expression::Parse("4")}, // sizeof(uint32_t) | ||
| 234 | - {ge::DT_UINT64, af::Expression::Parse("8")}, // sizeof(uint64_t) | ||
| 235 | - {ge::DT_DOUBLE, af::Expression::Parse("8")}, // sizeof(double) | ||
| 236 | - {ge::DT_BF16, af::Expression::Parse("2")}, // bf16 is 2 bytes | ||
| 237 | - {ge::DT_BOOL, af::Expression::Parse("1")} // sizeof(bool) | ||
| 238 | - }; | ||
| 239 | - const auto dtype = tensor.attr.dtype.operator ge::DataType(); | ||
| 240 | - auto it = type_size_map.find(dtype); | ||
| 241 | - if (it == type_size_map.end()) { | ||
| 242 | - GELOGE(ge::GRAPH_FAILED, "Unsupported data type: %d", static_cast<int32_t>(dtype)); | ||
| 243 | - return "0"; | ||
| 244 | - } | ||
| 245 | - af::Expression type_size = it->second; | ||
| 246 | - if (tensor.attr.repeats.empty() || tensor.attr.strides.empty()) { | ||
| 247 | - GELOGE(ge::GRAPH_FAILED, "Empty repeats or strides for tensor when calculating memory size"); | ||
| 248 | - return "0"; | ||
| 249 | - } | ||
| 250 | - | ||
| 251 | - // 跳过brc场景下的0 strides | ||
| 252 | - size_t stride_index = 0UL; | ||
| 253 | - for (; stride_index < tensor.attr.strides.size(); ++stride_index) { | ||
| 254 | - if (tensor.attr.strides[stride_index] != af::ops::Zero) { | ||
| 255 | - break; | ||
| 256 | - } | ||
| 257 | - GELOGD("Tensor stride %zu is zero, try to skip to next non-zero stride.", stride_index); | ||
| 258 | - } | ||
| 259 | - | ||
| 260 | - // 全为brc轴时,元素个数为1,其他情况下为repeats[index] * strides[index] | ||
| 261 | - af::Expression element_size = af::ops::One; | ||
| 262 | - if (stride_index < tensor.attr.repeats.size() && stride_index < tensor.attr.strides.size()) { | ||
| 263 | - element_size = af::sym::Mul(tensor.attr.repeats[stride_index], tensor.attr.strides[stride_index]).Simplify(); | ||
| 264 | - } | ||
| 265 | - af::Expression need_malloc_size = af::sym::Mul(element_size, type_size).Simplify(); | ||
| 266 | - GELOGD("Tensor element size: %s, need malloc size: %s", element_size.Str().get(), need_malloc_size.Str().get()); | ||
| 267 | - return std::string(need_malloc_size.Str().get()); | ||
| 268 | -} | ||
| 269 | - | ||
| 270 | -std::string TilingLib::PGOSearchTensorMallocDef(const ascir::FusedScheduledResult &fused_schedule_result) const { | ||
| 271 | - std::stringstream ss; | ||
| 272 | - int index = 0; | ||
| 273 | - for (auto &input : fused_schedule_result.input_nodes) { | ||
| 274 | - if (input->GetOutNodesPtr().empty()) { | ||
| 275 | - continue; | ||
| 276 | - } | ||
| 277 | - af::Node *out_node = input->GetOutNodesPtr()[0]; | ||
| 278 | - af::AscNode *asc_out_node = static_cast<af::AscNode *>(out_node); | ||
| 279 | - ss << " size_t input" << index << "_size = " << CalculateTensorMemorySizeStr(asc_out_node->outputs[0]) << ";" | ||
| 280 | - << std::endl; | ||
| 281 | - ss << " ret = aclrtMalloc(&input" << index << ", input" << index << "_size, ACL_MEM_MALLOC_HUGE_FIRST);" | ||
| 282 | - << std::endl; | ||
| 283 | - ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 284 | - ss << " DLOGE(\"aclrtMalloc input" << index << " failed. ERROR: %d\", ret);" << std::endl; | ||
| 285 | - ss << " return FAILED;" << std::endl; | ||
| 286 | - ss << " }" << std::endl; | ||
| 287 | - index++; | ||
| 288 | - } | ||
| 289 | - index = 0; | ||
| 290 | - for (auto &output : fused_schedule_result.output_nodes) { | ||
| 291 | - if (af::ops::IsOps<af::ascir_op::Output>(output)) { | ||
| 292 | - ss << " size_t output" << index << "_size = " << CalculateTensorMemorySizeStr(output->inputs[0]) << ";" | ||
| 293 | - << std::endl; | ||
| 294 | - ss << " ret = aclrtMalloc(&output" << index << ", output" << index << "_size, ACL_MEM_MALLOC_HUGE_FIRST);" | ||
| 295 | - << std::endl; | ||
| 296 | - ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 297 | - ss << " DLOGE(\"aclrtMalloc output" << index << " failed. ERROR: %d\", ret);" << std::endl; | ||
| 298 | - ss << " return FAILED;" << std::endl; | ||
| 299 | - ss << " }" << std::endl; | ||
| 300 | - index++; | ||
| 301 | - } | ||
| 302 | - } | ||
| 303 | - return ss.str(); | ||
| 304 | -} | ||
| 305 | - | ||
| 306 | -std::string TilingLib::PGOSearchTensorFreeDef(const ascir::FusedScheduledResult &fused_schedule_result) const { | ||
| 307 | - std::stringstream ss; | ||
| 308 | - int index = 0; | ||
| 309 | - for ([[maybe_unused]] auto &input : fused_schedule_result.input_nodes) { | ||
| 310 | - ss << " if (input" << index << " != nullptr) {" << std::endl; | ||
| 311 | - ss << " ret = aclrtFree(input" << index << ");" << std::endl; | ||
| 312 | - ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 313 | - ss << " DLOGW(\"aclrtFree input" << index << " failed. ERROR: %d\", ret);" << std::endl; | ||
| 314 | - ss << " }" << std::endl; | ||
| 315 | - ss << " input" << index << " = nullptr;" << std::endl; | ||
| 316 | - ss << " }" << std::endl; | ||
| 317 | - index++; | ||
| 318 | - } | ||
| 319 | - index = 0; | ||
| 320 | - for (auto &output : fused_schedule_result.output_nodes) { | ||
| 321 | - if (af::ops::IsOps<af::ascir_op::Output>(output)) { | ||
| 322 | - ss << " if (output" << index << " != nullptr) {" << std::endl; | ||
| 323 | - ss << " ret = aclrtFree(output" << index << ");" << std::endl; | ||
| 324 | - ss << " if (ret != ACL_SUCCESS) {" << std::endl; | ||
| 325 | - ss << " DLOGW(\"aclrtFree output" << index << " failed. ERROR: %d\", ret);" << std::endl; | ||
| 326 | - ss << " }" << std::endl; | ||
| 327 | - ss << " output" << index << " = nullptr;" << std::endl; | ||
| 328 | - ss << " }" << std::endl; | ||
| 329 | - index++; | ||
| 330 | - } | ||
| 331 | - } | ||
| 332 | - return ss.str(); | ||
| 333 | -} | ||
| 334 | - | ||
| 335 | } // namespace codegen | 544 | } // namespace codegen |
| @@ -11,21 +11,42 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | namespace codegen { | 13 | namespace codegen { |
| 14 | +void TilingLib::GenSharedPgoRuntimeLaunch(const ascir::FusedScheduledResult &fused_schedule_result, | ||
| 15 | + const std::string &pgo_dir, std::stringstream &ss, bool direct_link) const { | ||
| 16 | + GenPgoToolFunction(fused_schedule_result, pgo_dir, ss, direct_link); | ||
| 17 | + GenPgoWrapper(fused_schedule_result, ss, direct_link); | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +void TilingLib::GenSharedPgoRuntimeProfiling(const ascir::FusedScheduledResult &fused_schedule_result, | ||
| 21 | + std::stringstream &ss, bool direct_link) const { | ||
| 22 | + GenPgoMsptiProfiling(ss, direct_link); | ||
| 23 | + GenPgoBatchProcess(ss, direct_link); | ||
| 24 | + GenPgoGetProfilingBatch(fused_schedule_result, ss, direct_link); | ||
| 25 | + GenPgoGetProfiling(fused_schedule_result, ss, direct_link); | ||
| 26 | +} | ||
| 14 | 27 | ||
| 15 | std::string TilingLib::GenerateForPgo(const ascir::FusedScheduledResult &fused_schedule_result, | 28 | std::string TilingLib::GenerateForPgo(const ascir::FusedScheduledResult &fused_schedule_result, |
| 16 | const std::string &pgo_dir) const { | 29 | const std::string &pgo_dir) const { |
| 17 | - // 生成PGO的头文件和函数定义 | ||
| 18 | std::stringstream ss; | 30 | std::stringstream ss; |
| 19 | - GenPgoHeaders(ss); | 31 | + GenPgoHeaders(ss, false); |
| 20 | - // 生成PGO需要的工具函数 | 32 | + GenSharedPgoRuntimeLaunch(fused_schedule_result, pgo_dir, ss, false); |
| 21 | - GenPgoToolFunction(fused_schedule_result, pgo_dir, ss); | 33 | + GenSharedPgoRuntimeProfiling(fused_schedule_result, ss, false); |
| 22 | - // 生成PGO需要的wrapper函数 | ||
| 23 | - GenPgoWrapper(fused_schedule_result, ss); | ||
| 24 | - // 生成PGO需要的求解代码 | ||
| 25 | GenPgoProfiling(fused_schedule_result, ss); | 34 | GenPgoProfiling(fused_schedule_result, ss); |
| 26 | - // 生成PGO的main函数 | ||
| 27 | GenPgoMain(fused_schedule_result, ss); | 35 | GenPgoMain(fused_schedule_result, ss); |
| 28 | return ss.str(); | 36 | return ss.str(); |
| 29 | } | 37 | } |
| 30 | 38 | ||
| 39 | +std::string TilingLib::GenInductorPgoRunner(const ascir::FusedScheduledResult &fused_schedule_result) const { | ||
| 40 | + std::stringstream ss; | ||
| 41 | + GenPgoHeaders(ss, true); | ||
| 42 | + GenSharedPgoRuntimeLaunch(fused_schedule_result, "", ss, true); | ||
| 43 | + GenInductorPgoResultTypes(ss); | ||
| 44 | + GenInductorPgoHostLoader(ss); | ||
| 45 | + GenSharedPgoRuntimeProfiling(fused_schedule_result, ss, true); | ||
| 46 | + GenInductorPgoResultProtocol(ss); | ||
| 47 | + GenInductorPgoRuntime(fused_schedule_result, ss); | ||
| 48 | + GenInductorPgoMain(ss); | ||
| 49 | + return ss.str(); | ||
| 50 | +} | ||
| 51 | + | ||
| 31 | } // namespace codegen | 52 | } // namespace codegen |
| @@ -19,10 +19,37 @@ namespace codegen { | |||
| 19 | using namespace ascgen_utils; | 19 | using namespace ascgen_utils; |
| 20 | using namespace ascir; | 20 | using namespace ascir; |
| 21 | 21 | ||
| 22 | +namespace { | ||
| 23 | +std::string GenPgoMeasuredSearchModel() { | ||
| 24 | + return R"( | ||
| 25 | +inline std::string PgoMeasuredCandidateKey(const AutofuseTilingDataPerf &candidate) { | ||
| 26 | + const char *ptr = reinterpret_cast<const char *>(&candidate.tiling_data); | ||
| 27 | + return std::string(ptr, ptr + sizeof(AutofuseTilingData)); | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +inline std::vector<AutofuseTilingDataPerf> NormalizePgoMeasuredCandidates( | ||
| 31 | + std::vector<AutofuseTilingDataPerf> raw_candidates) { | ||
| 32 | + std::vector<AutofuseTilingDataPerf> candidates; | ||
| 33 | + candidates.reserve(raw_candidates.size()); | ||
| 34 | + std::unordered_set<std::string> seen; | ||
| 35 | + for (auto &candidate : raw_candidates) { | ||
| 36 | + const std::string key = PgoMeasuredCandidateKey(candidate); | ||
| 37 | + if (!seen.insert(key).second) { | ||
| 38 | + continue; | ||
| 39 | + } | ||
| 40 | + candidates.push_back(std::move(candidate)); | ||
| 41 | + } | ||
| 42 | + return candidates; | ||
| 43 | +} | ||
| 44 | +)"; | ||
| 45 | +} | ||
| 46 | +} // namespace | ||
| 47 | + | ||
| 22 | std::string TilingLib::GenPgoTilingFunc(const ascir::FusedScheduledResult &fused_schedule_result, | 48 | std::string TilingLib::GenPgoTilingFunc(const ascir::FusedScheduledResult &fused_schedule_result, |
| 23 | const std::string &tiling, codegen::PgoShapeStringStream &pgo_shape_dim, | 49 | const std::string &tiling, codegen::PgoShapeStringStream &pgo_shape_dim, |
| 24 | bool is_inductor_scene, const std::string &core_num) const { | 50 | bool is_inductor_scene, const std::string &core_num) const { |
| 25 | std::stringstream ss; | 51 | std::stringstream ss; |
| 52 | + ss << GenPgoMeasuredSearchModel(); | ||
| 26 | // 生成 AutofuseTilingWithConfig 函数 | 53 | // 生成 AutofuseTilingWithConfig 函数 |
| 27 | ss << GenPgoAutofuseTiling(fused_schedule_result, pgo_shape_dim, tiling, is_inductor_scene); | 54 | ss << GenPgoAutofuseTiling(fused_schedule_result, pgo_shape_dim, tiling, is_inductor_scene); |
| 28 | // 生成 PgoSaveTilingKey 函数 | 55 | // 生成 PgoSaveTilingKey 函数 |
| @@ -97,22 +124,21 @@ std::string TilingLib::GenProfilingAllTilingData(std::string tiling_data_list_na | |||
| 97 | std::stringstream ss; | 124 | std::stringstream ss; |
| 98 | ss << " double out_cost = DBL_MAX;" << std::endl; | 125 | ss << " double out_cost = DBL_MAX;" << std::endl; |
| 99 | ss << " *workspaceSize = 0;" << std::endl; | 126 | ss << " *workspaceSize = 0;" << std::endl; |
| 100 | - ss << " std::unordered_set<std::string> solver_filter;" << std::endl; | 127 | + ss << " std::vector<AutofuseTilingDataPerf> raw_search_candidates;" << std::endl; |
| 101 | ss << " for (const auto &tiling_data_item : " << tiling_data_list_name << ") {" << std::endl; | 128 | ss << " for (const auto &tiling_data_item : " << tiling_data_list_name << ") {" << std::endl; |
| 102 | - ss << " const char *ptr = reinterpret_cast<const char*>(&tiling_data_item);" << std::endl; | ||
| 103 | - ss << " std::string key(ptr, ptr + sizeof(AutofuseTilingData));" << std::endl; | ||
| 104 | - ss << " if (!solver_filter.insert(key).second) {" << std::endl; | ||
| 105 | - ss << " continue;" << std::endl; | ||
| 106 | - ss << " }" << std::endl; | ||
| 107 | ss << " *workspaceSize = std::max(GetWorkspaceSize(tiling_data_item), *workspaceSize);" << std::endl; | 129 | ss << " *workspaceSize = std::max(GetWorkspaceSize(tiling_data_item), *workspaceSize);" << std::endl; |
| 108 | ss << " AutofuseTilingDataPerf tiling_data_perf;" << std::endl; | 130 | ss << " AutofuseTilingDataPerf tiling_data_perf;" << std::endl; |
| 109 | ss << " tiling_data_perf.tiling_data = tiling_data_item;" << std::endl; | 131 | ss << " tiling_data_perf.tiling_data = tiling_data_item;" << std::endl; |
| 110 | ss << " tiling_data_perf.best_perf = DBL_MAX;" << std::endl; | 132 | ss << " tiling_data_perf.best_perf = DBL_MAX;" << std::endl; |
| 111 | - ss << " " << tiling_data_perf_list_name << ".push_back(tiling_data_perf);" << std::endl; | 133 | + ss << " raw_search_candidates.push_back(tiling_data_perf);" << std::endl; |
| 112 | ss << " }" << std::endl; | 134 | ss << " }" << std::endl; |
| 113 | if (!is_inductor_scene) { | 135 | if (!is_inductor_scene) { |
| 114 | ss << " *workspaceSize += 16 * 1024 * 1024;" << std::endl; | 136 | ss << " *workspaceSize += 16 * 1024 * 1024;" << std::endl; |
| 115 | } | 137 | } |
| 138 | + ss << " auto normalized_search_candidates = NormalizePgoMeasuredCandidates(std::move(raw_search_candidates));" | ||
| 139 | + << std::endl; | ||
| 140 | + ss << " " << tiling_data_perf_list_name << ".insert(" << tiling_data_perf_list_name | ||
| 141 | + << ".end(), normalized_search_candidates.begin(), normalized_search_candidates.end());" << std::endl; | ||
| 116 | ss << " PgoConfig::Instance().batch_callback(" << PGOSearchFuncInputOutputCall(fused_schedule_result) | 142 | ss << " PgoConfig::Instance().batch_callback(" << PGOSearchFuncInputOutputCall(fused_schedule_result) |
| 117 | << "stream, *workspaceSize, &" << tiling_data_perf_list_name << ");" << std::endl; | 143 | << "stream, *workspaceSize, &" << tiling_data_perf_list_name << ");" << std::endl; |
| 118 | return ss.str(); | 144 | return ss.str(); |
| @@ -131,6 +157,8 @@ std::string TilingLib::GenPgoTilingSearchByCoreNum(const ascir::FusedScheduledRe | |||
| 131 | ss << "void *stream=nullptr, ProfilingCallback prof_callback=nullptr, ProfilingBatchCallback " | 157 | ss << "void *stream=nullptr, ProfilingCallback prof_callback=nullptr, ProfilingBatchCallback " |
| 132 | "prof_batch_callback=nullptr) {" | 158 | "prof_batch_callback=nullptr) {" |
| 133 | << std::endl; | 159 | << std::endl; |
| 160 | + ss << " (void)prof_callback;" << std::endl; | ||
| 161 | + ss << " (void)prof_batch_callback;" << std::endl; | ||
| 134 | ss << " const ResLimit *limit = (res_limit == nullptr) ? &g_no_limit_res : res_limit;" << std::endl; | 162 | ss << " const ResLimit *limit = (res_limit == nullptr) ? &g_no_limit_res : res_limit;" << std::endl; |
| 135 | ss << pgo_shape_dim.tiling_set_shape_dim.str(); | 163 | ss << pgo_shape_dim.tiling_set_shape_dim.str(); |
| 136 | ss << " double best_perf = DBL_MAX;" << std::endl; | 164 | ss << " double best_perf = DBL_MAX;" << std::endl; |
| @@ -230,6 +258,22 @@ std::string TilingLib::GenGetAutoFuseTilingInput(bool is_inductor_scene) const { | |||
| 230 | return ss.str(); | 258 | return ss.str(); |
| 231 | } | 259 | } |
| 232 | 260 | ||
| 261 | +void TilingLib::GenPgoTilingKeySearch(const ascir::FusedScheduledResult &fused_schedule_result, | ||
| 262 | + std::stringstream &ss) const { | ||
| 263 | + if (ascgen_utils::IsSingleGroup(fused_schedule_result)) { | ||
| 264 | + ss << " // 不使用,仅保持接口一致" << std::endl; | ||
| 265 | + ss << " std::unordered_map<int64_t, uint64_t> workspace_map;" << std::endl; | ||
| 266 | + ss << " if (!optiling::PGOSearchTilingKey(tiling_data_list, *tiling, -1, tiling, " | ||
| 267 | + << PGOSearchFuncInputOutputCall(fused_schedule_result) << "stream, *workspaceSize, best_perf, workspace_map)) {" | ||
| 268 | + << std::endl; | ||
| 269 | + } else { | ||
| 270 | + ss << " if (!optiling::PGOSearchTilingKey(tiling_data_list, *tiling, -1, tiling, " | ||
| 271 | + << PGOSearchFuncInputOutputCall(fused_schedule_result) << "stream, *workspaceSize, best_perf)) {" << std::endl; | ||
| 272 | + } | ||
| 273 | + ss << " return -1;" << std::endl; | ||
| 274 | + ss << " }" << std::endl; | ||
| 275 | +} | ||
| 276 | + | ||
| 233 | std::string TilingLib::GenPgoTilingSearchPGO(const ascir::FusedScheduledResult &fused_schedule_result, | 277 | std::string TilingLib::GenPgoTilingSearchPGO(const ascir::FusedScheduledResult &fused_schedule_result, |
| 234 | codegen::PgoShapeStringStream &pgo_shape_dim, const std::string &tiling, | 278 | codegen::PgoShapeStringStream &pgo_shape_dim, const std::string &tiling, |
| 235 | bool is_inductor_scene, const std::string &core_num) const { | 279 | bool is_inductor_scene, const std::string &core_num) const { |
| @@ -241,6 +285,8 @@ std::string TilingLib::GenPgoTilingSearchPGO(const ascir::FusedScheduledResult & | |||
| 241 | << "void *stream=nullptr, ProfilingCallback prof_callback=nullptr, ProfilingBatchCallback " | 285 | << "void *stream=nullptr, ProfilingCallback prof_callback=nullptr, ProfilingBatchCallback " |
| 242 | << "prof_batch_callback=nullptr) {" << std::endl; | 286 | << "prof_batch_callback=nullptr) {" << std::endl; |
| 243 | 287 | ||
| 288 | + ss << " (void)prof_callback;" << std::endl; | ||
| 289 | + ss << " (void)prof_batch_callback;" << std::endl; | ||
| 244 | ss << " const ResLimit *limit = (res_limit == nullptr) ? &g_no_limit_res : res_limit;" << std::endl; | 290 | ss << " const ResLimit *limit = (res_limit == nullptr) ? &g_no_limit_res : res_limit;" << std::endl; |
| 245 | ss << " std::vector<AutofuseTilingDataPerf> tiling_data_list;" << std::endl; | 291 | ss << " std::vector<AutofuseTilingDataPerf> tiling_data_list;" << std::endl; |
| 246 | ss << pgo_shape_dim.tiling_set_shape_dim.str(); | 292 | ss << pgo_shape_dim.tiling_set_shape_dim.str(); |
| @@ -260,18 +306,7 @@ std::string TilingLib::GenPgoTilingSearchPGO(const ascir::FusedScheduledResult & | |||
| 260 | ss << " tiling_data_list.push_back(tiling_perf);" << std::endl; | 306 | ss << " tiling_data_list.push_back(tiling_perf);" << std::endl; |
| 261 | ss << " OP_LOGD(OP_NAME, \"axesreorder solution base perf is %lf\", best_perf);" << std::endl; | 307 | ss << " OP_LOGD(OP_NAME, \"axesreorder solution base perf is %lf\", best_perf);" << std::endl; |
| 262 | ss << " tiling->set_block_dim(max_block_dim);" << std::endl; | 308 | ss << " tiling->set_block_dim(max_block_dim);" << std::endl; |
| 263 | - if (ascgen_utils::IsSingleGroup(fused_schedule_result)) { | 309 | + GenPgoTilingKeySearch(fused_schedule_result, ss); |
| 264 | - ss << " // 不使用,仅保持接口一致" << std::endl; | ||
| 265 | - ss << " std::unordered_map<int64_t, uint64_t> workspace_map;" << std::endl; | ||
| 266 | - ss << " if (!optiling::PGOSearchTilingKey(tiling_data_list, *tiling, -1, tiling, " | ||
| 267 | - << PGOSearchFuncInputOutputCall(fused_schedule_result) << "stream, *workspaceSize, best_perf, workspace_map)) {" | ||
| 268 | - << std::endl; | ||
| 269 | - } else { | ||
| 270 | - ss << " if (!optiling::PGOSearchTilingKey(tiling_data_list, *tiling, -1, tiling, " | ||
| 271 | - << PGOSearchFuncInputOutputCall(fused_schedule_result) << "stream, *workspaceSize, best_perf)) {" << std::endl; | ||
| 272 | - } | ||
| 273 | - ss << " return -1;" << std::endl; | ||
| 274 | - ss << " }" << std::endl; | ||
| 275 | ss << " if (optiling::IsEqual(best_perf, DBL_MAX)) {" << std::endl; | 310 | ss << " if (optiling::IsEqual(best_perf, DBL_MAX)) {" << std::endl; |
| 276 | ss << " OP_LOGE(OP_NAME, \"pgo solution get perf failed %lf\", best_perf);" << std::endl; | 311 | ss << " OP_LOGE(OP_NAME, \"pgo solution get perf failed %lf\", best_perf);" << std::endl; |
| 277 | ss << " return -1;" << std::endl; | 312 | ss << " return -1;" << std::endl; |
| @@ -308,7 +343,6 @@ std::string TilingLib::GenGetResLimitStru(void) const { | |||
| 308 | bool TilingLib::IsMixKernelTaskType(const ascir::FusedScheduledResult &fused_schedule_result) const { | 343 | bool TilingLib::IsMixKernelTaskType(const ascir::FusedScheduledResult &fused_schedule_result) const { |
| 309 | return fused_schedule_result.workspace_nodes.size() != 0; | 344 | return fused_schedule_result.workspace_nodes.size() != 0; |
| 310 | } | 345 | } |
| 311 | - | ||
| 312 | std::string TilingLib::GenPGOGetTilingKey(const std::string tiling) const { | 346 | std::string TilingLib::GenPGOGetTilingKey(const std::string tiling) const { |
| 313 | std::stringstream ss; | 347 | std::stringstream ss; |
| 314 | ss << "bool PGOGetTilingKey(const char *config_file_path, " << tiling << " &tiling_data) {" << std::endl; | 348 | ss << "bool PGOGetTilingKey(const char *config_file_path, " << tiling << " &tiling_data) {" << std::endl; |
| @@ -416,5 +450,4 @@ std::string TilingLib::GenSavePGOConfigTilingDataFunc() const { | |||
| 416 | 450 | ||
| 417 | return ss.str(); | 451 | return ss.str(); |
| 418 | } | 452 | } |
| 419 | - | ||
| 420 | } // namespace codegen | 453 | } // namespace codegen |
| @@ -10,6 +10,8 @@ | |||
| 10 | # See LICENSE in the root of the software repository for the full text of the License. | 10 | # See LICENSE in the root of the software repository for the full text of the License. |
| 11 | # ----------------------------------------------------------------------------------------------------------- | 11 | # ----------------------------------------------------------------------------------------------------------- |
| 12 | import ctypes | 12 | import ctypes |
| 13 | +import hashlib | ||
| 14 | +import json | ||
| 13 | import os | 15 | import os |
| 14 | import re | 16 | import re |
| 15 | import sys | 17 | import sys |
| @@ -17,8 +19,11 @@ import shutil | |||
| 17 | import argparse | 19 | import argparse |
| 18 | import subprocess | 20 | import subprocess |
| 19 | import platform | 21 | import platform |
| 22 | +import tempfile | ||
| 23 | +import uuid | ||
| 20 | from concurrent.futures import ThreadPoolExecutor, as_completed | 24 | from concurrent.futures import ThreadPoolExecutor, as_completed |
| 21 | import time | 25 | import time |
| 26 | +from dataclasses import dataclass | ||
| 22 | from functools import wraps | 27 | from functools import wraps |
| 23 | from typing import List | 28 | from typing import List |
| 24 | from asc_op_compile_base.common.platform.platform_info import get_soc_spec | 29 | from asc_op_compile_base.common.platform.platform_info import get_soc_spec |
| @@ -30,6 +35,9 @@ HOST_LINK_LIBRARIES = ["tiling_api", "platform", "graph_base", "register"] | |||
| 30 | CV_HOST_LINK_LIBRARIES = HOST_LINK_LIBRARIES + ["nnopbase"] | 35 | CV_HOST_LINK_LIBRARIES = HOST_LINK_LIBRARIES + ["nnopbase"] |
| 31 | INDUCTOR_COMPILE_TRACE_LABEL = "InductorCompile" | 36 | INDUCTOR_COMPILE_TRACE_LABEL = "InductorCompile" |
| 32 | HOST_COMPILE_MAX_WORKERS = 32 | 37 | HOST_COMPILE_MAX_WORKERS = 32 |
| 38 | +PGO_BUNDLE_SCHEMA_VERSION = 1 | ||
| 39 | +PGO_RESULT_PROTOCOL_VERSION = 1 | ||
| 40 | +PGO_KERNEL_FORMAT = "aicore_binary_elf_v1" | ||
| 33 | if not os.path.exists(ASCEND_PATH): | 41 | if not os.path.exists(ASCEND_PATH): |
| 34 | ASCEND_PATH = os.getenv("ASCEND_HOME_PATH", ASCEND_PATH) | 42 | ASCEND_PATH = os.getenv("ASCEND_HOME_PATH", ASCEND_PATH) |
| 35 | 43 | ||
| @@ -38,17 +46,14 @@ class CompileError(Exception): | |||
| 38 | """Compile failed exception.""" | 46 | """Compile failed exception.""" |
| 39 | 47 | ||
| 40 | 48 | ||
| 41 | -def parse_env_flags(env_name): | 49 | +@dataclass(frozen=True) |
| 42 | - result = {} | 50 | +class PgoBundle: |
| 43 | - flags = os.getenv(env_name) | 51 | + tiling_file: str |
| 44 | - if not flags: | 52 | + runner_file: str |
| 45 | - return result | 53 | + kernel_file: str |
| 46 | - params = flags.split(";") | 54 | + output_file: str |
| 47 | - for param in params: | 55 | + generation: str |
| 48 | - if "=" in param: | 56 | + ld_preload: str = "" |
| 49 | - key_part, value_part = param.split("=", 1) | ||
| 50 | - result[key_part.lstrip("-")] = value_part | ||
| 51 | - return result | ||
| 52 | 57 | ||
| 53 | 58 | ||
| 54 | def record_inductor_compile_duration(stage, step, graph_name, start, duration): | 59 | def record_inductor_compile_duration(stage, step, graph_name, start, duration): |
| @@ -127,6 +132,180 @@ def link_shared(target_file, obj_files, link_libraries=None): | |||
| 127 | return target_file | 132 | return target_file |
| 128 | 133 | ||
| 129 | 134 | ||
| 135 | +def link_pgo_executable(target_file, obj_files, mspti_link_flags): | ||
| 136 | + link_command = [f"{ASCEND_PATH}/tools/bisheng_compiler/bin/bisheng", *obj_files] | ||
| 137 | + link_command.extend(["-fPIC", "-o", target_file]) | ||
| 138 | + link_command.extend(["-L", f"{ASCEND_PATH}/lib64"]) | ||
| 139 | + link_command.extend(["-L", f"{ASCEND_PATH}/{machine}-linux/lib64"]) | ||
| 140 | + link_command.extend([f"-l{link_library}" for link_library in HOST_LINK_LIBRARIES]) | ||
| 141 | + link_command.extend( | ||
| 142 | + ["-lascendcl", "-lruntime", "-lunified_dlog", "-lascendalog", "-lc_sec", "-lm"] | ||
| 143 | + ) | ||
| 144 | + link_command.extend( | ||
| 145 | + f"-Wl,-rpath,{option[2:]}" | ||
| 146 | + for option in mspti_link_flags | ||
| 147 | + if option.startswith("-L") and len(option) > 2 | ||
| 148 | + ) | ||
| 149 | + link_command.extend(mspti_link_flags) | ||
| 150 | + link_command.extend(["-ldl", "-lpthread"]) | ||
| 151 | + run_compile_command(link_command, "LinkPgoExecutable") | ||
| 152 | + return target_file | ||
| 153 | + | ||
| 154 | + | ||
| 155 | +def extract_aicore_binary(device_obj_file, output_file): | ||
| 156 | + objcopy = shutil.which("llvm-objcopy") | ||
| 157 | + if objcopy is None: | ||
| 158 | + objcopy = os.path.join( | ||
| 159 | + ASCEND_PATH, "tools", "bisheng_compiler", "bin", "llvm-objcopy" | ||
| 160 | + ) | ||
| 161 | + if not os.path.isfile(objcopy): | ||
| 162 | + raise CompileError("llvm-objcopy is required for Inductor PGO device binary") | ||
| 163 | + run_compile_command( | ||
| 164 | + [objcopy, "--dump-section", f".aicore_binary={output_file}", device_obj_file], | ||
| 165 | + "ExtractPgoDeviceBinary", | ||
| 166 | + ) | ||
| 167 | + if not os.path.isfile(output_file) or os.path.getsize(output_file) == 0: | ||
| 168 | + raise CompileError("extracted Inductor PGO device binary is empty") | ||
| 169 | + return output_file | ||
| 170 | + | ||
| 171 | + | ||
| 172 | +def build_pgo_sidecars(args, temp_dir): | ||
| 173 | + mspti_dir, preload_files, link_flags = args.pgo_mspti_config | ||
| 174 | + args.pgo_mspti_dir = mspti_dir | ||
| 175 | + args.pgo_ld_preload = ":".join(preload_files) | ||
| 176 | + runner_obj = compile_host_obj_file(args, temp_dir, args.pgo_runner_file) | ||
| 177 | + runner_file = os.path.join(temp_dir, "pgo_runner") | ||
| 178 | + link_pgo_executable(runner_file, [runner_obj], link_flags) | ||
| 179 | + args.device_files = args.pgo_device_file | ||
| 180 | + device_obj = compile_device_obj(args, temp_dir) | ||
| 181 | + kernel_file = os.path.join(temp_dir, f"pgo_kernel.{PGO_KERNEL_FORMAT}") | ||
| 182 | + extract_aicore_binary(device_obj, kernel_file) | ||
| 183 | + return runner_file, kernel_file | ||
| 184 | + | ||
| 185 | + | ||
| 186 | +def get_pgo_sidecar_paths(output_file, generation): | ||
| 187 | + generation_dir = f"{os.path.realpath(output_file)}.pgo.{generation}" | ||
| 188 | + output_name = os.path.basename(output_file) | ||
| 189 | + return { | ||
| 190 | + "generation_dir": generation_dir, | ||
| 191 | + "tiling_so": os.path.join(generation_dir, output_name), | ||
| 192 | + "runner": os.path.join(generation_dir, f"{output_name}.pgo_runner"), | ||
| 193 | + "kernel": os.path.join( | ||
| 194 | + generation_dir, f"{output_name}.pgo_kernel.{PGO_KERNEL_FORMAT}" | ||
| 195 | + ), | ||
| 196 | + "manifest": os.path.join(generation_dir, "manifest.json"), | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + | ||
| 200 | +def file_sha256(path): | ||
| 201 | + digest = hashlib.sha256() | ||
| 202 | + with open(path, "rb") as file: | ||
| 203 | + for chunk in iter(lambda: file.read(1024 * 1024), b""): | ||
| 204 | + digest.update(chunk) | ||
| 205 | + return digest.hexdigest() | ||
| 206 | + | ||
| 207 | + | ||
| 208 | +def build_pgo_manifest(bundle): | ||
| 209 | + return { | ||
| 210 | + "bundle_schema_version": PGO_BUNDLE_SCHEMA_VERSION, | ||
| 211 | + "generation": bundle.generation, | ||
| 212 | + "result_protocol_version": PGO_RESULT_PROTOCOL_VERSION, | ||
| 213 | + "ld_preload": bundle.ld_preload, | ||
| 214 | + "artifacts": { | ||
| 215 | + "tiling_so": { | ||
| 216 | + "file": os.path.basename(bundle.output_file), | ||
| 217 | + "sha256": file_sha256(bundle.tiling_file), | ||
| 218 | + }, | ||
| 219 | + "runner": { | ||
| 220 | + "file": os.path.basename(bundle.runner_file), | ||
| 221 | + "sha256": file_sha256(bundle.runner_file), | ||
| 222 | + }, | ||
| 223 | + "kernel": { | ||
| 224 | + "file": os.path.basename(bundle.kernel_file), | ||
| 225 | + "sha256": file_sha256(bundle.kernel_file), | ||
| 226 | + }, | ||
| 227 | + }, | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + | ||
| 231 | +def write_pgo_manifest(path, manifest): | ||
| 232 | + with open(path, "w", encoding="utf-8") as file: | ||
| 233 | + json.dump(manifest, file, sort_keys=True) | ||
| 234 | + file.flush() | ||
| 235 | + os.fsync(file.fileno()) | ||
| 236 | + | ||
| 237 | + | ||
| 238 | +def cleanup_stale_pgo_generations(output_file, current_generation_dir): | ||
| 239 | + output_file = os.path.realpath(output_file) | ||
| 240 | + output_dir = os.path.dirname(output_file) | ||
| 241 | + generation_prefix = os.path.basename(output_file) + ".pgo." | ||
| 242 | + previous_generations = [] | ||
| 243 | + try: | ||
| 244 | + for entry in os.scandir(output_dir): | ||
| 245 | + if not entry.name.startswith(generation_prefix) or not entry.is_dir( | ||
| 246 | + follow_symlinks=False | ||
| 247 | + ): | ||
| 248 | + continue | ||
| 249 | + if os.path.realpath(entry.path) == os.path.realpath(current_generation_dir): | ||
| 250 | + continue | ||
| 251 | + previous_generations.append( | ||
| 252 | + (entry.stat(follow_symlinks=False).st_mtime_ns, entry.name, entry.path) | ||
| 253 | + ) | ||
| 254 | + except OSError: | ||
| 255 | + return | ||
| 256 | + previous_generations.sort(reverse=True) | ||
| 257 | + for _, _, generation_dir in previous_generations[1:]: | ||
| 258 | + shutil.rmtree(generation_dir, ignore_errors=True) | ||
| 259 | + | ||
| 260 | + | ||
| 261 | +def publish_pgo_bundle(bundle): | ||
| 262 | + output_file = os.path.realpath(bundle.output_file) | ||
| 263 | + output_dir = os.path.dirname(output_file) | ||
| 264 | + os.makedirs(output_dir, exist_ok=True) | ||
| 265 | + paths = get_pgo_sidecar_paths(output_file, bundle.generation) | ||
| 266 | + staging_dir = tempfile.mkdtemp( | ||
| 267 | + prefix=f".{os.path.basename(output_file)}.pgo.", dir=output_dir | ||
| 268 | + ) | ||
| 269 | + staged_tiling = os.path.join( | ||
| 270 | + output_dir, f".{os.path.basename(output_file)}.{bundle.generation}.tmp" | ||
| 271 | + ) | ||
| 272 | + generation_published = False | ||
| 273 | + try: | ||
| 274 | + staged_generation_tiling = os.path.join( | ||
| 275 | + staging_dir, os.path.basename(paths["tiling_so"]) | ||
| 276 | + ) | ||
| 277 | + staged_runner = os.path.join(staging_dir, os.path.basename(paths["runner"])) | ||
| 278 | + staged_kernel = os.path.join(staging_dir, os.path.basename(paths["kernel"])) | ||
| 279 | + shutil.copy2(bundle.tiling_file, staged_generation_tiling) | ||
| 280 | + shutil.copy2(bundle.runner_file, staged_runner) | ||
| 281 | + shutil.copy2(bundle.kernel_file, staged_kernel) | ||
| 282 | + shutil.copy2(staged_generation_tiling, staged_tiling) | ||
| 283 | + manifest = build_pgo_manifest( | ||
| 284 | + PgoBundle( | ||
| 285 | + staged_generation_tiling, | ||
| 286 | + staged_runner, | ||
| 287 | + staged_kernel, | ||
| 288 | + output_file, | ||
| 289 | + bundle.generation, | ||
| 290 | + bundle.ld_preload, | ||
| 291 | + ) | ||
| 292 | + ) | ||
| 293 | + write_pgo_manifest(os.path.join(staging_dir, "manifest.json"), manifest) | ||
| 294 | + os.replace(staging_dir, paths["generation_dir"]) | ||
| 295 | + generation_published = True | ||
| 296 | + os.replace(staged_tiling, output_file) | ||
| 297 | + cleanup_stale_pgo_generations(output_file, paths["generation_dir"]) | ||
| 298 | + return paths | ||
| 299 | + except OSError as ex: | ||
| 300 | + if generation_published: | ||
| 301 | + shutil.rmtree(paths["generation_dir"], ignore_errors=True) | ||
| 302 | + raise CompileError(f"publish Inductor PGO bundle failed: {ex}") from ex | ||
| 303 | + finally: | ||
| 304 | + shutil.rmtree(staging_dir, ignore_errors=True) | ||
| 305 | + if os.path.exists(staged_tiling): | ||
| 306 | + os.remove(staged_tiling) | ||
| 307 | + | ||
| 308 | + | ||
| 130 | def iter_compile_source_files(args: argparse.Namespace): | 309 | def iter_compile_source_files(args: argparse.Namespace): |
| 131 | for source_files in ( | 310 | for source_files in ( |
| 132 | getattr(args, "host_files", None), | 311 | getattr(args, "host_files", None), |
| @@ -201,7 +380,7 @@ def build_host_include_options(temp_dir): | |||
| 201 | 380 | ||
| 202 | def build_host_base_options(args: argparse.Namespace, temp_dir): | 381 | def build_host_base_options(args: argparse.Namespace, temp_dir): |
| 203 | soc_version = get_soc_type(args) | 382 | soc_version = get_soc_type(args) |
| 204 | - return [ | 383 | + options = [ |
| 205 | f"{ASCEND_PATH}/tools/bisheng_compiler/bin/bisheng", | 384 | f"{ASCEND_PATH}/tools/bisheng_compiler/bin/bisheng", |
| 206 | "-D", | 385 | "-D", |
| 207 | "kernel_EXPORTS", | 386 | "kernel_EXPORTS", |
| @@ -214,6 +393,13 @@ def build_host_base_options(args: argparse.Namespace, temp_dir): | |||
| 214 | "-Wfloat-equal", | 393 | "-Wfloat-equal", |
| 215 | "-fvisibility=default", | 394 | "-fvisibility=default", |
| 216 | ] | 395 | ] |
| 396 | + mspti_dir = getattr(args, "pgo_mspti_dir", None) | ||
| 397 | + if mspti_dir: | ||
| 398 | + options.extend(["-I", os.path.join(mspti_dir, "include")]) | ||
| 399 | + pgo_generation = getattr(args, "pgo_generation", None) | ||
| 400 | + if pgo_generation: | ||
| 401 | + options.extend(["-D", f'AUTOFUSE_PGO_GENERATION="{pgo_generation}"']) | ||
| 402 | + return options | ||
| 217 | 403 | ||
| 218 | 404 | ||
| 219 | def build_host_output_options(source_file, obj_file): | 405 | def build_host_output_options(source_file, obj_file): |
| @@ -618,6 +804,8 @@ def link_host_target(args, temp_dir): | |||
| 618 | link_libraries = ( | 804 | link_libraries = ( |
| 619 | CV_HOST_LINK_LIBRARIES if is_cv_fusion_compile(args) else HOST_LINK_LIBRARIES | 805 | CV_HOST_LINK_LIBRARIES if is_cv_fusion_compile(args) else HOST_LINK_LIBRARIES |
| 620 | ) | 806 | ) |
| 807 | + if getattr(args, "pgo_runner_file", None) is not None: | ||
| 808 | + link_libraries = link_libraries + ["ascendcl", "runtime"] | ||
| 621 | with InductorCompileDuration(args, "LinkHostSo"): | 809 | with InductorCompileDuration(args, "LinkHostSo"): |
| 622 | link_shared(so_file, host_obj_paths, link_libraries=link_libraries) | 810 | link_shared(so_file, host_obj_paths, link_libraries=link_libraries) |
| 623 | return so_file | 811 | return so_file |
| @@ -661,21 +849,53 @@ def copy_so_to_output(so_file, args, src_directory): | |||
| 661 | os.chdir(src_directory) | 849 | os.chdir(src_directory) |
| 662 | 850 | ||
| 663 | 851 | ||
| 852 | +def build_host_output(args): | ||
| 853 | + should_build_sidecars = ( | ||
| 854 | + getattr(args, "pgo_runner_file", None) is not None | ||
| 855 | + and getattr(args, "pgo_mspti_config", None) is not None | ||
| 856 | + ) | ||
| 857 | + if should_build_sidecars: | ||
| 858 | + args.pgo_generation = uuid.uuid4().hex | ||
| 859 | + so_file = link_host_target(args, args.temp_dir) | ||
| 860 | + if not should_build_sidecars: | ||
| 861 | + return so_file | ||
| 862 | + try: | ||
| 863 | + runner_file, kernel_file = build_pgo_sidecars(args, args.temp_dir) | ||
| 864 | + publish_pgo_bundle( | ||
| 865 | + PgoBundle( | ||
| 866 | + so_file, | ||
| 867 | + runner_file, | ||
| 868 | + kernel_file, | ||
| 869 | + args.output_file, | ||
| 870 | + args.pgo_generation, | ||
| 871 | + getattr(args, "pgo_ld_preload", ""), | ||
| 872 | + ) | ||
| 873 | + ) | ||
| 874 | + return None | ||
| 875 | + except CompileError as ex: | ||
| 876 | + print(f"[PGO] Inductor PGO sidecar build failed, skip PGO: {ex}") | ||
| 877 | + return so_file | ||
| 878 | + | ||
| 879 | + | ||
| 664 | def main(args): | 880 | def main(args): |
| 665 | print("compile args:", args) | 881 | print("compile args:", args) |
| 666 | src_directory = os.getcwd() | 882 | src_directory = os.getcwd() |
| 667 | os.chdir(args.temp_dir) | 883 | os.chdir(args.temp_dir) |
| 668 | print("change work dir:", os.getcwd()) | 884 | print("change work dir:", os.getcwd()) |
| 885 | + try: | ||
| 886 | + if args.stage == "host": | ||
| 887 | + so_file = build_host_output(args) | ||
| 888 | + if so_file is None: | ||
| 889 | + return | ||
| 890 | + elif args.stage == "device": | ||
| 891 | + so_file = link_kernel_target(args, None, args.temp_dir) | ||
| 892 | + else: # all | ||
| 893 | + host_obj_paths = compile_host_objs(args, args.temp_dir) | ||
| 894 | + so_file = link_kernel_target(args, host_obj_paths, args.temp_dir) | ||
| 669 | 895 | ||
| 670 | - if args.stage == "host": | 896 | + copy_so_to_output(so_file, args, src_directory) |
| 671 | - so_file = link_host_target(args, args.temp_dir) | 897 | + finally: |
| 672 | - elif args.stage == "device": | 898 | + os.chdir(src_directory) |
| 673 | - so_file = link_kernel_target(args, None, args.temp_dir) | ||
| 674 | - else: # all | ||
| 675 | - host_obj_paths = compile_host_objs(args, args.temp_dir) | ||
| 676 | - so_file = link_kernel_target(args, host_obj_paths, args.temp_dir) | ||
| 677 | - | ||
| 678 | - copy_so_to_output(so_file, args, src_directory) | ||
| 679 | 899 | ||
| 680 | 900 | ||
| 681 | def main_with_except(argv: List[str]): | 901 | def main_with_except(argv: List[str]): |