// Copyright (c) 2021 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. // #pragma once #include #include #include "paddle/fluid/eager/grad_node_info.h" #include "paddle/fluid/eager/hooks.h" #include "paddle/fluid/eager/type_defs.h" #include "paddle/fluid/imperative/tracer.h" #include "paddle/phi/api/ext/op_meta_info.h" #include "paddle/utils/small_vector.h" #include "paddle/utils/test_macros.h" namespace egr { class UniqueNameGenerator { public: explicit UniqueNameGenerator(std::string prefix = "") : prefix_(prefix) {} std::string Generate(std::string key = "eager_tmp") { return prefix_ + key + "_" + std::to_string(id_++); } private: std::atomic id_{0}; std::string prefix_; }; // Global // TODO(jiabin): Now we are using imperative tracer, move it here when we // deprecate imperative. class GradNodeBase; class Controller { public: TEST_API static Controller& Instance(); phi::Place GetExpectedPlace() const { return tracer_->ExpectedPlace(); } TEST_API void SetExpectedPlace(const phi::Place& place); void SetAMPLevel(paddle::imperative::AmpLevel level) { tracer_->SetAmpLevel(level); } paddle::imperative::AmpLevel GetAMPLevel() const { return tracer_->GetAmpLevel(); } TEST_API void SetUsePromote(bool use_promote); TEST_API bool GetUsePromote() const; TEST_API bool UseLayoutAutoTune(); void DisableLayoutAutoTune() { tracer_->DisableLayoutAutoTune(); } void EnableLayoutAutoTune() { tracer_->EnableLayoutAutoTune(); } void SetPythonStack(std::string stack_str) { tracer_->SetPythonStack(stack_str); } std::string GetPythonStack() { return tracer_->GetPythonStack(); } bool HasGrad() const { return tracer_->HasGrad(); } void SetHasGrad(bool has_grad) { tracer_->SetHasGrad(has_grad); } std::string GenerateUniqueName(std::string key = "eager_in_tmp") { return tracer_->GenerateUniqueName(key); } const std::shared_ptr& GetCurrentTracer() { return tracer_; } void SetCurrentTracer( const std::shared_ptr& tracer) { tracer_ = tracer; VLOG(6) << "Set current tracer for Controller: " << tracer_; } const std::shared_ptr& GetCurrentAmpAttrs() { return paddle::imperative::GetCurrentAmpAttrs(); } const std::unordered_map>& GetOpMetaInfoMap() { return op_meta_info_map_; } void MergeOpMetaInfoMap( const std::unordered_map>& map) { for (const auto& [key, value] : map) { if (op_meta_info_map_.count(key)) { VLOG(3) << "Replacing existing OpMetaInfo for op: " << key; } VLOG(3) << "Merging OpMetaInfo for op: " << key; op_meta_info_map_[key] = value; } } std::unordered_map>>>& GetCustomEdgesSlotMap() { return custom_edges_slot_map_; } // For Cpp Hook void RegisterBackwardFinalHook(const std::function& call_back) { VLOG(6) << "RegisterBackwardFinalHook"; final_backward_hooks_.emplace_back( std::make_shared(std::move(call_back))); VLOG(6) << "Size: " << final_backward_hooks_.size(); } // For Python hook void RegisterBackwardFinalHook(const std::shared_ptr& call_back) { final_backward_hooks_.emplace_back(call_back); } const std::vector>& FinalBackwardHooks() const { return final_backward_hooks_; } void ClearFinalBackwardHooks() { final_backward_hooks_.clear(); } void ClearForceSequentialNodes() { force_sequential_nodes_.clear(); } void PushBackForceSequentialNodes(GradNodeBase* node) { force_sequential_nodes_.push_back(node); } void EraseForceSequentialNodes(GradNodeBase* node) { for (auto iter = force_sequential_nodes_.begin(); iter != force_sequential_nodes_.end(); ++iter) { if (*iter == node) { force_sequential_nodes_.erase(iter); return; } } } std::list GetForceSequentialNodes() { return force_sequential_nodes_; } TEST_API void SetIsInBackward(bool is_in_backward); TEST_API bool GetIsInBackward() const; private: Controller() = default; static Controller* controller_; static thread_local std::shared_ptr tracer_; std::unordered_map> op_meta_info_map_; /* op_type : {{{grad_outputs}, {grad_inputs}, {input}, {output}, {attrs}}, * {{grad_outputs}, {grad_inputs}, {input}, {output}, {attrs}}}*/ std::unordered_map>>> custom_edges_slot_map_; std::vector> final_backward_hooks_; std::list force_sequential_nodes_; bool is_in_backward_{false}; DISABLE_COPY_AND_ASSIGN(Controller); }; class EagerBackwardStateGuard { public: EagerBackwardStateGuard() { Controller::Instance().SetIsInBackward(true); } ~EagerBackwardStateGuard() { Controller::Instance().SetIsInBackward(false); } }; class EagerBackwardSubGraphNodeRecorder { public: /** * @brief Get the singleton instance of EagerBackwardSubGraphNodeRecorder * @return Reference to the singleton instance * * Uses static local variable for thread-safe singleton initialization * (C++11 guarantee). The instance is created on first call and destroyed * automatically at program termination. */ static EagerBackwardSubGraphNodeRecorder& Instance() { static EagerBackwardSubGraphNodeRecorder instance; return instance; } public: void AddGradNode(const GradNodeBase* node) { if (need_capture_viz_subgraph_) viz_nodes_.insert(node); if (need_capture_vlog_subgraph_) { vlog_nodes_.insert(node); // record the node's vlog level node_to_vlog_level_[node] = subgraph_bwd_vlog_level_; } } bool NeedCaptureSubGraph() { return need_capture_viz_subgraph_ || need_capture_vlog_subgraph_; } void StartCaptureSubGraphForViz() { need_capture_viz_subgraph_ = true; } void StopCaptureSubGraphForViz() { need_capture_viz_subgraph_ = false; } void StartCaptureSubGraphForVlog() { need_capture_vlog_subgraph_ = true; } void StopCaptureSubGraphForVlog() { need_capture_vlog_subgraph_ = false; } void SetDumpDirPath(const std::string& path) { dump_dir_path_ = path; } const std::string& GetDumpDirPath() { return dump_dir_path_; } void SetNeedDumpGradTensors(bool need_dump) { need_dump_grad_tensors_ = need_dump; } bool GetNeedDumpGradTensors() { return need_dump_grad_tensors_; } int GetSubGraphBwdVlogLevel(const GradNodeBase* node) { auto it = node_to_vlog_level_.find(node); if (it != node_to_vlog_level_.end()) { return it->second; } return 0; } void SetSubGraphBwdVlogLevel(int level) { subgraph_bwd_vlog_level_ = level; } // Is Gradnode within the scope of backward vlog guard bool IsGradNodeInVlogGuard(GradNodeBase* node) { return vlog_nodes_.count(node); } bool IsGradNodeInVizGuard(const GradNodeBase* node) { return viz_nodes_.count(node); } bool NeedDumpBwdSubGraph() { return !viz_nodes_.empty() && !dump_dir_path_.empty(); } bool NeedBwdVlogGuard() { return !vlog_nodes_.empty(); } private: std::unordered_set viz_nodes_; std::unordered_set vlog_nodes_; std::unordered_map node_to_vlog_level_; std::string dump_dir_path_; bool need_capture_vlog_subgraph_ = false; bool need_capture_viz_subgraph_ = false; bool need_dump_grad_tensors_ = false; int subgraph_bwd_vlog_level_ = 0; }; /** * @class LogIndent * @brief Singleton class for managing log indentation levels globally * * This class implements the singleton pattern to provide a centralized way * to manage indentation levels for formatted log output. It ensures only * one instance exists throughout the application lifecycle. */ class LogIndent { public: /** * @brief Get the singleton instance of LogIndent * @return Reference to the singleton instance * * Uses static local variable for thread-safe singleton initialization * (C++11 guarantee). The instance is created on first call and destroyed * automatically at program termination. */ static LogIndent& Instance() { static LogIndent instance; return instance; } /** * @brief Increase the current indentation level by 1 * * Call this method when entering a nested scope to increase * log indentation for better visual hierarchy. */ void IncreaseIndentLevel() { FLAGS_indentlevel = FLAGS_indentlevel + 1; } /** * @brief Decrease the current indentation level by 1 * * Reduces the indentation level, but never goes below 0. * Call this when leaving a nested scope. */ void DecreaseIndentLevel() { if (FLAGS_indentlevel > 0) { FLAGS_indentlevel = FLAGS_indentlevel - 1; } } LogIndent(const LogIndent&) = delete; LogIndent& operator=(const LogIndent&) = delete; private: LogIndent() = default; ~LogIndent() = default; }; } // namespace egr