// 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 #include #include namespace paddle { namespace framework { namespace details { static void AppendSkipDeletionVars(const std::vector &append_vars, std::vector *all_vars) { for (auto &var : append_vars) { all_vars->emplace_back(var); } } static void AppendSafeEagerDeletionSkipVars( const framework::ProgramDesc &program, std::vector *skip_vars) { const framework::BlockDesc &block = program.Block(0); const std::vector &all_ops = block.AllOps(); std::unordered_set grad_op_output; std::unordered_set grad_op_input; for (const framework::OpDesc *op : all_ops) { int op_role = BOOST_GET_CONST( int, op->GetAttr(framework::OpProtoAndCheckerMaker::OpRoleAttrName())); if ((op_role & static_cast(framework::OpRole::kBackward)) == 0) { continue; } for (const std::string &in_arg_name : op->InputArgumentNames()) { grad_op_input.emplace(in_arg_name); } for (const std::string &out_arg_name : op->OutputArgumentNames()) { grad_op_output.emplace(out_arg_name); } } // For the grad op input variables, if it is not output of grad_op, it may // be output of forward op and we should set the variables as skip_var to // prevent it being deleted when grad op is called multiple times. for (const std::string &var_name : grad_op_input) { if (grad_op_output.find(var_name) == grad_op_output.end()) { skip_vars->emplace_back(var_name); } } } } // namespace details // 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 ExecutorInfoCache &ExecutorInfoCache::Instance() { static ExecutorInfoCache g_exe_cache_info_map; return g_exe_cache_info_map; } std::shared_ptr GetExecutorInfoFromCache( const framework::Executor &exe, const framework::ExecutionContext &ctx, const std::vector> &ctx_output_names, bool is_grad) { auto *program = ctx.Attr("global_block")->Program(); auto &cached_exe_info = framework::ExecutorInfoCache::Instance(); auto cache_key = framework::ExecutorInfoCache::KeyInfo(program, is_grad); if (!cached_exe_info.Has(cache_key)) { VLOG(1) << "create exe_info for program: " << program << " is_grad: " << is_grad; // skip delete vars std::vector skip_vars; for (auto &output_names : ctx_output_names) { details::AppendSkipDeletionVars(output_names, &skip_vars); } if (is_grad) { details::AppendSafeEagerDeletionSkipVars(*program, &skip_vars); } VLOG(2) << "Prepare to skip " << skip_vars.size() << " var(s): " << string::join_strings(skip_vars, ' '); std::shared_ptr exe_ctx = std::move(exe.Prepare(*program, /*block_id=*/0, skip_vars)); cached_exe_info.Insert(cache_key, exe_ctx); return exe_ctx; } else { VLOG(1) << "get exe_info from cache by program: " << program << " is_grad: " << is_grad; return cached_exe_info.Get(cache_key); } } } // namespace framework } // namespace paddle