// Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "paddle/fluid/framework/executor_cache.h" #include "paddle/common/flags.h" #include "paddle/common/macros.h" #include "paddle/fluid/framework/new_executor/interpretercore.h" #include "paddle/fluid/framework/op_info.h" #include "paddle/fluid/ir_adaptor/translator/translate.h" #include "paddle/fluid/pir/transforms/general/inplace_pass.h" #include "paddle/fluid/pir/transforms/general/remove_shadow_feed_pass.h" #include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h" #include "paddle/pir/include/core/program.h" #include "paddle/pir/include/core/value.h" #include "paddle/pir/include/pass/pass.h" #include "paddle/pir/include/pass/pass_manager.h" #include "paddle/pir/include/pass/pass_registry.h" DECLARE_FILE_SYMBOLS(print_statistics); COMMON_DECLARE_bool(pir_apply_inplace_pass); COMMON_DECLARE_bool(print_ir); COMMON_DECLARE_string(enable_custom_engine); namespace paddle::framework { class ProgramDesc; } // namespace paddle::framework namespace paddle::framework::details { void AppendSkipDeletionVars(const std::vector &append_vars, std::set *all_vars) { for (auto &var : append_vars) { all_vars->insert(var); } } std::set ParseSafeEagerDeletionSkipVarsSet( const ProgramDesc &backward_program, bool skip_no_need_buffer) { std::set skip_eager_delete_vars; auto backward_ops = backward_program.Block(0).AllOps(); auto &op_info_map = OpInfoMap::Instance(); std::unordered_set op_outputs; std::unordered_set op_inputs; std::unordered_set no_need_buffer_ins; for (auto op : backward_ops) { VLOG(4) << "parse op type: " << op->Type(); if (op->Type() == "share_buffer") { VLOG(1) << "skip share_buffer op"; continue; } // NOTE: skip NoNeedBufferVars of grad_op and GC its memory in advance. auto &op_info = op_info_map.Get(op->Type()); auto &inferer = op_info.NoNeedBufferVarsInferer(); no_need_buffer_ins.clear(); // TODO(Aurelius84): Need remove skip_no_need_buffer after cinn fix this // problem. if (inferer != nullptr && !skip_no_need_buffer) { no_need_buffer_ins = inferer(op->Inputs(), op->Outputs(), op->GetAttrMap()); } for (auto &in_names : op->Inputs()) { if (no_need_buffer_ins.count(in_names.first) == 0) { for (auto &in_name : in_names.second) { op_inputs.emplace(in_name); } } else { VLOG(2) << op->Type() << " has no_need_buffer_in: " << in_names.first << " , skip it."; } } for (const std::string &out_arg_name : op->OutputArgumentNames()) { op_outputs.emplace(out_arg_name); } } for (const std::string &var_name : op_inputs) { VLOG(4) << "parse op.input: " << var_name; if (op_outputs.find(var_name) == op_outputs.end()) { VLOG(1) << "skip eager var: " << var_name; skip_eager_delete_vars.insert(var_name); } } VLOG(1) << "Found skip_eager_delete_vars: " << skip_eager_delete_vars.size(); return skip_eager_delete_vars; } } // namespace paddle::framework::details namespace paddle::framework { // C++11 removes the need for manual locking. Concurrent execution shall wait if // a static local variable is already being initialized. // https://stackoverflow.com/questions/11711920/how-to-implement-multithread-safe-singleton-in-c11-without-using-mutex int64_t hash_with_seed(int64_t value, int64_t seed) { return value + 0x9e3779b9 + (value << 6) + (seed >> 2); } InterpreterCoreInfoCache &InterpreterCoreInfoCache::Instance() { static InterpreterCoreInfoCache g_info_cache; return g_info_cache; } std::shared_ptr CreateProgramInterpreterCoreInfoToCache( const ProgramDesc &program_desc, const Place &place, framework::Scope *scope, const InterpreterCoreInfoCacheKey &key) { auto &cache = framework::InterpreterCoreInfoCache::Instance(); if (cache.Size() > 256000u /* max_cached_size*/) { PADDLE_THROW(common::errors::Fatal( "The cached info size has exceeded max_cached_size: 256000, " "which will cause error. ")); } interpreter::ExecutionConfig execution_config; execution_config.create_local_scope = false; execution_config.used_for_jit = true; std::shared_ptr core = nullptr; core.reset(new InterpreterCore( place, program_desc.Block(0), scope, execution_config)); auto &cached_value = cache.GetMutable(key.with_pir_mode(false)); cached_value.core_ = core; return core; } std::shared_ptr CreatePirInterpreterCoreInfoToCache( std::unique_ptr ir_program, const Place &place, framework::Scope *scope, const InterpreterCoreInfoCacheKey &key, bool used_for_sot) { auto &cache = framework::InterpreterCoreInfoCache::Instance(); if (cache.Size() > 256000u /* max_cached_size*/) { PADDLE_THROW(common::errors::Fatal( "The cached info size has exceeded max_cached_size: 256000, " "which will cause error. ")); } interpreter::ExecutionConfig execution_config; execution_config.create_local_scope = false; execution_config.used_for_jit = true; execution_config.used_for_sot = used_for_sot; std::shared_ptr core = nullptr; core.reset(new InterpreterCore( place, {}, ir_program->block(), scope, execution_config)); auto &cached_value = cache.GetMutable(key.with_pir_mode(true)); cached_value.core_ = core; cached_value.ir_prog_ = std::move(ir_program); return core; } bool TensorSortHelper(const paddle::Tensor &t1, const paddle::Tensor &t2) { return t1.name() < t2.name(); } std::unique_ptr ApplyIrPass( pir::Program *program, Place place, const std::set &no_need_buffer_names) { #if defined(PADDLE_WITH_CUSTOM_DEVICE) if (!FLAGS_enable_custom_engine.empty()) { std::string custom_engine_translate_pass = FLAGS_enable_custom_engine; std::istringstream ss(custom_engine_translate_pass); std::string pass; std::vector passes; while (std::getline(ss, pass, ',')) { passes.push_back(pass); VLOG(4) << "Add CustomEngine pass : " << pass; } pir::PassManager pass_pm(pir::IrContext::Instance(), 3); for (std::string custom_pass : passes) { pass_pm.AddPass(pir::PassRegistry::Instance().Get(custom_pass)); pass_pm.Run(program); } } #endif auto ir_res = pir::PdOpLowerToKernelPass(program, place); if (FLAGS_pir_apply_inplace_pass) { pir::PassManager pm(pir::IrContext::Instance(), 3); pm.AddPass(pir::CreateInplacePass(no_need_buffer_names)); pm.Run(ir_res.get()); if (FLAGS_print_ir) { std::cout << "IR After inplace -------------------" << std::endl; std::cout << *ir_res << std::endl; } } return ir_res; } std::unique_ptr ApplyRemoveShadowFeedPass( std::unique_ptr program, const pir::Block *block, const Place &place, const paddle::framework::Scope *scope) { pir::PassManager pm(pir::IrContext::Instance(), 3); auto pass = pir::CreateRemoveShadowFeedPass(); pass->SetNotOwned("top_block", block); pass->SetNotOwned(pir::Pass::kPlaceAttr, &place); pass->SetNotOwned(pir::Pass::kParamScopeAttr, scope); pm.AddPass(std::move(pass)); pm.Run(program.get()); if (FLAGS_print_ir) { std::cout << "IR After RemoveShadowFeedPass -------------------" << std::endl; std::cout << *program << std::endl; } return program; } std::unique_ptr ConstructForwardIrProgram( const paddle::framework::BlockDesc *forward_global_block, const paddle::framework::BlockDesc *backward_global_block, const std::vector &output_names, const std::vector &x, const std::vector &x_names, const std::vector ¶ms, const Place &place) { std::set set_output_names; auto local_program = paddle::framework::ProgramDesc(*(forward_global_block->Program())); for (auto op_desc : local_program.Block(0).AllOps()) { for (const auto &n : op_desc->Outputs()) { const auto &input_var_names = n.second; for (const auto &var_name : input_var_names) { set_output_names.insert(var_name); } } } // add data op to program auto *block = local_program.MutableBlock(0); for (size_t i = 0; i < x.size(); ++i) { auto &name = x_names[i]; auto &in_t = x[i]; if (block->FindVarRecursive(name) == nullptr) { continue; } auto p = in_t.place().GetType(); auto op_desc = block->PrependOp(); op_desc->SetType("data"); op_desc->SetAttr("shape", std::vector()); // TODO(phlrain) : using tensor dtype op_desc->SetAttr("dtype", 0); op_desc->SetAttr("place", static_cast(p)); if (p == phi::AllocationType::CUSTOM) { op_desc->SetAttr("place_device_id", in_t.place().GetDeviceId()); op_desc->SetAttr("place_device_type", in_t.place().GetDeviceType()); } op_desc->SetAttr("name", name); op_desc->SetOutput("out", {name}); } std::set input_param_names; auto sorted_params = params; std::sort(sorted_params.begin(), sorted_params.end(), TensorSortHelper); for (auto ¶m : sorted_params) { auto &name = param.name(); auto p = param.place().GetType(); auto op_desc = local_program.MutableBlock(0)->PrependOp(); op_desc->SetType("data"); op_desc->SetAttr("shape", std::vector()); // TODO(phlrain) : using tensor dtype op_desc->SetAttr("dtype", 0); op_desc->SetAttr("place", static_cast(p)); if (p == phi::AllocationType::CUSTOM) { op_desc->SetAttr("place_device_id", param.place().GetDeviceId()); op_desc->SetAttr("place_device_type", param.place().GetDeviceType()); } op_desc->SetAttr("name", name); op_desc->SetOutput("out", {name}); input_param_names.insert(name); } std::set set_parameter_names; for (auto &t : output_names) { set_parameter_names.insert(t); } if (backward_global_block != nullptr) { for (auto op_desc : backward_global_block->Program()->Block(0).AllOps()) { for (const auto &n : op_desc->Inputs()) { const auto &input_var_names = n.second; for (const auto &var_name : input_var_names) { set_parameter_names.insert(var_name); } } } } for (auto &name : set_parameter_names) { if (!set_output_names.count(name)) { continue; } if (input_param_names.count(name)) { continue; } auto op_desc = local_program.MutableBlock(0)->AppendOp(); op_desc->SetType("shadow_output"); op_desc->SetAttr("name", name); op_desc->SetInput("x", {name}); op_desc->SetOutput("out", {"@EMPTY@"}); } auto program = TranslateLegacyProgramToProgram(local_program); return ApplyIrPass(program.get(), place, {}); } std::unique_ptr ConstructBackwardIrProgram( const paddle::framework::BlockDesc *backward_global_block, const std::vector &out_grad, const std::vector &x_grad, const std::vector ¶ms_grad, const paddle::framework::Scope *scope, const Place &place) { auto local_program = paddle::framework::ProgramDesc(*(backward_global_block->Program())); // get feed with data std::set set_parameter_names; for (auto op_desc : backward_global_block->Program()->Block(0).AllOps()) { for (const auto &n : op_desc->Inputs()) { const auto &input_var_names = n.second; for (const auto &var_name : input_var_names) { set_parameter_names.insert(var_name); } } } for (auto &var_name : set_parameter_names) { if (scope->FindVar(var_name)) { auto tensor = scope->FindVar(var_name)->Get(); phi::AllocationType p = place.GetType(); if (tensor.has_allocation()) { p = tensor.place().GetType(); } if (var_name == "@EMPTY@") { continue; } auto op_desc = local_program.MutableBlock(0)->PrependOp(); op_desc->SetType("data"); op_desc->SetAttr("shape", std::vector()); // TODO(phlrain) : using tensor dtype op_desc->SetAttr("dtype", 0); op_desc->SetAttr("place", static_cast(p)); if (p == phi::AllocationType::CUSTOM) { op_desc->SetAttr("place_device_id", tensor.place().GetDeviceId()); op_desc->SetAttr("place_device_type", tensor.place().GetDeviceType()); } op_desc->SetAttr("name", var_name); op_desc->SetOutput("out", {var_name}); } } std::vector param_grad_names; for (auto &p_g : params_grad) { param_grad_names.push_back(p_g->name()); } for (auto &t : x_grad) { param_grad_names.push_back(t->name()); } std::sort(param_grad_names.begin(), param_grad_names.end()); for (auto &name : param_grad_names) { if (name == "@EMPTY@") { continue; } auto op_desc = local_program.MutableBlock(0)->AppendOp(); op_desc->SetType("shadow_output"); op_desc->SetAttr("name", name); op_desc->SetInput("x", {name}); op_desc->SetOutput("out", {"@EMPTY@"}); } auto program = TranslateLegacyProgramToProgram(local_program); auto res = pir::PdOpLowerToKernelPass(program.get(), place); if (FLAGS_pir_apply_inplace_pass) { pir::PassManager pm(pir::IrContext::Instance(), 3); pm.AddPass(pir::CreateInplacePass()); if (VLOG_IS_ON(6)) { pm.EnableIRPrinting(); pm.EnablePrintStatistics(); } pm.Run(res.get()); if (FLAGS_print_ir) { std::cout << "IR After inplace -------------------" << std::endl; std::cout << *res << std::endl; } } return res; } } // namespace paddle::framework