Files
2026-07-13 12:40:42 +08:00

2145 lines
77 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2023 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/new_executor/pir_interpreter.h"
#include <chrono>
#include <unordered_set>
#include "paddle/common/flags.h"
#include "paddle/fluid/framework/details/nan_inf_utils.h"
#include "paddle/fluid/framework/new_executor/interpreter/interpreter_util.h"
#include "paddle/fluid/framework/new_executor/interpreter/static_build.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/platform/profiler/supplement_tracing.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_context.h"
#include "paddle/phi/core/os_info.h"
#include "paddle/phi/core/platform/device/gpu/gpu_info.h"
#include "paddle/phi/core/platform/profiler/event_tracing.h"
#include "paddle/phi/core/sparse_coo_tensor.h"
#include "paddle/phi/core/sparse_csr_tensor.h"
#ifdef PADDLE_WITH_DNNL
#include "paddle/fluid/framework/new_executor/instruction/onednn/onednn_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/onednn/onednn_legacy_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/onednn/onednn_mixed_instruction.h"
#include "paddle/fluid/platform/onednn_helper.h"
#endif
#include "paddle/phi/backends/device_manager.h"
#include "paddle/phi/core/platform/cuda_graph_with_memory_pool.h"
#ifdef PADDLE_WITH_CINN
#include "paddle/fluid/framework/new_executor/instruction/cinn_jit_instruction.h"
#endif
#ifdef PADDLE_WITH_CUSTOM_DEVICE
#include "paddle/fluid/framework/new_executor/instruction/custom_engine_instruction.h"
#endif
#include "paddle/fluid/framework/new_executor/garbage_collector/async_fast_garbage_collector.h"
#include "paddle/fluid/framework/new_executor/instruction/builtin_combine_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/assert_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/has_elements_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/if_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/pylayer_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/select_input_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/select_output_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/tuple_pop_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/tuple_push_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/while_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/control_flow/yield_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/cuda_graph_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/custom_kernel_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/instruction_util.h"
#include "paddle/fluid/framework/new_executor/instruction/legacy_kernel_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/phi_kernel_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/python_function_instruction.h"
#include "paddle/fluid/framework/new_executor/instruction/tensorrt_engine_instruction.h"
#include "paddle/fluid/framework/new_executor/pir_adaptor/pir_adaptor_util.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_attribute.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_dialect.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_op.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/control_flow_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/manual_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/manual_pylayer_op.h"
#include "paddle/fluid/pir/dialect/operator/ir/tensorrt_op.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
#include "paddle/fluid/platform/device/gpu/nccl_helper.h"
#include "paddle/phi/core/distributed/comm_context_manager.h"
#include "paddle/phi/core/distributed/nccl_comm_context.h"
#endif
#include "paddle/fluid/framework/new_executor/collect_shape_manager.h"
#include "paddle/fluid/framework/new_executor/nan_inf_utils.h"
COMMON_DECLARE_bool(enable_pir_in_executor);
COMMON_DECLARE_bool(enable_pir_in_executor_trace_run);
COMMON_DECLARE_bool(enable_collect_shape);
COMMON_DECLARE_int32(low_precision_op_list);
COMMON_DECLARE_bool(pir_interpreter_record_stream_for_gc_cache);
COMMON_DECLARE_bool(async_fast_eager_deletion_mode);
#define CREATE_INSTR(instr_name) \
vec_instruction_base_.emplace_back(std::make_unique<instr_name>( \
op_idx++, place_, &op, value_exe_info_.get()));
namespace paddle::framework {
void RecordLowPrecisionOp(const InstructionBase* instr_node) {
if (FLAGS_low_precision_op_list) {
std::string op_name = instr_node->Name();
pir::Operation* op = instr_node->Operation();
if (op->HasAttribute("kernel_key")) {
phi::KernelKey kernel_key =
op->attribute("kernel_key")
.dyn_cast<paddle::dialect::KernelAttribute>()
.data();
phi::KernelFactory::Instance().AddToLowPrecisionKernelList(
op_name, kernel_key.dtype());
}
}
}
bool UseTraceRun(const ExecutionConfig& execution_config,
size_t onednn_op_num,
size_t sync_op_num) {
return FLAGS_enable_pir_in_executor_trace_run || onednn_op_num ||
execution_config.used_for_inference || execution_config.used_for_sot ||
((execution_config.used_for_jit || execution_config.used_for_cinn) &&
(sync_op_num == 0));
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
const int64_t PirInterpreter::cuda_graph_capture_pool_id_ =
phi::backends::gpu::CUDAGraph::UniqueMemoryPoolID();
#endif
PirInterpreter::PirInterpreter(const Place& place,
const std::vector<std::string>& fetch_var_names,
const pir::Block* ir_block,
framework::Scope* scope,
const ExecutionConfig& execution_config)
: is_build_(false),
static_build_(false),
is_shared_results_build_(false),
place_(place),
unfinished_op_number_(0),
execution_config_(execution_config),
force_events_to_wait_(nullptr),
var_scope_(scope),
scope_(scope),
local_scope_(nullptr),
main_thread_blocker_(),
async_work_queue_(),
exception_holder_(),
exception_notifier_(nullptr),
completion_notifier_(nullptr),
gc_(nullptr),
async_gc_{nullptr},
last_live_ops_(),
dependency_count_(nullptr),
deps_(),
refs_(),
sync_op_num_(-1),
nccl_op_num_(-1),
onednn_op_num_(-1),
trace_execute_order_(),
pir_output_hookfuncs_(),
pir_input_hookfuncs_(),
ir_instruction_scheduling_priority_less(),
ir_block_(ir_block),
sub_blocks_(),
vec_instruction_base_(),
value_exe_info_(nullptr),
var_ref_count_(),
ir_dependency_builder_(),
ir_stream_analyzer_(place),
fetch_var_names_(fetch_var_names),
parameter_var_names_(),
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
calculate_stream_timer_(
std::make_unique<phi::CalculateStreamTimer>(place)),
#endif
last_calculate_instr_id_(0),
enable_job_schedule_profiler_(false) {
VLOG(2) << "PirInterpreter(): " << this << " on " << place_;
exception_notifier_ = main_thread_blocker_.RegisterEvent(kExceptionCaught);
completion_notifier_ = main_thread_blocker_.RegisterEvent(kTaskCompletion);
dependency_count_ = std::make_shared<std::vector<size_t>>();
if (!FLAGS_new_executor_use_local_scope) {
execution_config_.create_local_scope = false;
}
if (execution_config_.create_local_scope) {
auto local_scope = &scope_->NewScope();
local_scope_ = local_scope;
VLOG(6) << "pir interpretercore scope: " << scope_ << "\t"
<< "; local scope: " << local_scope_;
}
// TODO(zhangbo): delete var_scope
var_scope_.SetLocalScope(local_scope_);
execution_config_.AnalyzeThreadPoolConfig(place, 1);
execution_config_.Log(/*log_level=*/8);
ir_instruction_scheduling_priority_less = [this](size_t lhs, size_t rhs) {
SchedulingPriority lhs_scheduling_priority =
vec_instruction_base_[lhs]->GetSchedulingPriority();
SchedulingPriority rhs_scheduling_priority =
vec_instruction_base_[rhs]->GetSchedulingPriority();
if (lhs_scheduling_priority == rhs_scheduling_priority) {
return lhs > rhs;
}
return lhs_scheduling_priority > rhs_scheduling_priority;
};
value_exe_info_ = std::make_shared<ValueExecutionInfo>(InnerScope());
std::stringstream ss;
ss << this
<< std::chrono::high_resolution_clock::now().time_since_epoch().count();
BuildScope(*ir_block_, ss.str(), execution_config_, value_exe_info_.get());
}
PirInterpreter::PirInterpreter(
const Place& place,
const std::vector<std::string>& fetch_var_names,
const pir::Block* ir_block,
framework::Scope* scope,
std::shared_ptr<ValueExecutionInfo> value_exe_info,
const ExecutionConfig& execution_config)
: is_build_(false),
static_build_(false),
is_shared_results_build_(false),
place_(place),
unfinished_op_number_(0),
execution_config_(execution_config),
force_events_to_wait_(nullptr),
var_scope_(scope),
scope_(scope),
local_scope_(nullptr),
main_thread_blocker_(),
async_work_queue_(),
exception_holder_(),
exception_notifier_(nullptr),
completion_notifier_(nullptr),
gc_(nullptr),
async_gc_{nullptr},
last_live_ops_(),
dependency_count_(nullptr),
deps_(),
refs_(),
sync_op_num_(-1),
nccl_op_num_(-1),
onednn_op_num_(-1),
trace_execute_order_(),
pir_output_hookfuncs_(),
pir_input_hookfuncs_(),
ir_instruction_scheduling_priority_less(),
ir_block_(ir_block),
sub_blocks_(),
vec_instruction_base_(),
value_exe_info_(value_exe_info),
var_ref_count_(),
ir_dependency_builder_(),
ir_stream_analyzer_(place),
fetch_var_names_(fetch_var_names),
parameter_var_names_(),
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
calculate_stream_timer_(nullptr),
#endif
last_calculate_instr_id_(0),
enable_job_schedule_profiler_(false) {
VLOG(2) << "PirInterpreter(): " << this << " on " << place_;
exception_notifier_ = main_thread_blocker_.RegisterEvent(kExceptionCaught);
completion_notifier_ = main_thread_blocker_.RegisterEvent(kTaskCompletion);
dependency_count_ = std::make_shared<std::vector<size_t>>();
if (!FLAGS_new_executor_use_local_scope) {
execution_config_.create_local_scope = false;
}
if (execution_config_.create_local_scope) {
auto local_scope = &scope_->NewScope();
local_scope_ = local_scope;
VLOG(6) << "pir interpretercore scope: " << scope_ << "\t"
<< "; local scope: " << local_scope_;
}
// TODO(zhangbo): delete var_scope
var_scope_.SetLocalScope(local_scope_);
execution_config_.AnalyzeThreadPoolConfig(place, 1);
execution_config_.Log(/*log_level=*/8);
ir_instruction_scheduling_priority_less = [this](size_t lhs, size_t rhs) {
SchedulingPriority lhs_scheduling_priority =
vec_instruction_base_[lhs]->GetSchedulingPriority();
SchedulingPriority rhs_scheduling_priority =
vec_instruction_base_[rhs]->GetSchedulingPriority();
if (lhs_scheduling_priority == rhs_scheduling_priority) {
return lhs > rhs;
}
return lhs_scheduling_priority > rhs_scheduling_priority;
};
std::stringstream ss;
ss << this
<< std::chrono::high_resolution_clock::now().time_since_epoch().count();
BuildScope(*ir_block_, ss.str(), execution_config_, value_exe_info_.get());
}
PirInterpreter::~PirInterpreter() {
// cancel gc's thread
gc_.reset(nullptr);
async_gc_.reset(nullptr);
async_work_queue_.reset();
VLOG(4) << "~PirInterpreter(): " << this << " on " << place_;
#ifdef PADDLE_WITH_DNNL
// Clear one-dnn cache,
// this is needed to have one-dnn unit tests working
platform::ClearONEDNNCache(place_, this);
#endif
}
std::shared_ptr<ProgramDesc> PirInterpreter::GetMutableCopyProgram() {
PADDLE_THROW(common::errors::Unimplemented(
"GetMutableCopyProgram is not implemented in PirInterpreter."));
}
void PirInterpreter::SetSkipGcVars(const std::set<std::string>& skip_gc_vars) {
PADDLE_ENFORCE_EQ(
execution_config_.skip_gc_vars.empty(),
true,
common::errors::PreconditionNotMet(
"execution_config_.skip_gc_vars can only be initialized once, now "
"execution_config_.skip_gc_vars is "
"not empty, do not call SetSkipGcVars method repeatedly."));
execution_config_.skip_gc_vars = skip_gc_vars;
}
void PirInterpreter::SetJitInputVars(
const std::set<std::string>& jit_input_vars) {
PADDLE_ENFORCE_EQ(
execution_config_.jit_input_vars.empty(),
true,
common::errors::PreconditionNotMet(
"execution_config_.jit_input_vars can only be initialized once, now "
"execution_config_.jit_input_vars is "
"not empty, do not call SetJitInputVars method repeatedly."));
execution_config_.jit_input_vars = jit_input_vars;
}
const std::set<std::string>& PirInterpreter::JitInputVars() const {
return execution_config_.jit_input_vars;
}
const VariableScope* PirInterpreter::GetVariableScope() const {
return &var_scope_;
}
void PirInterpreter::reset_scope(Scope* new_scope) {
var_scope_.SetScope(new_scope);
scope_ = new_scope;
for (size_t i = 0; i < value_exe_info_->GetVarList().size(); i++) {
const auto& var_name = value_exe_info_->GetNameById(static_cast<int>(i));
value_exe_info_->ResetVarList(static_cast<int>(i),
new_scope->FindVar(var_name));
}
// The index should be assured valid, cause the InterpreterCore may not be
// fully built, but was still cached and used. For example, see unit test
// `test_assert.py`, it may exit before `PirInterpreter::Convert`,
// but still was cached and used by later tests.
for (size_t i = 0;
i < std::min(refs_.size(), value_exe_info_->GetVarList().size());
i++) {
refs_[i]->ResetVariable(value_exe_info_->GetVarList()[i]);
}
}
const Scope* PirInterpreter::local_scope() const { return local_scope_; }
void PirInterpreter::ShareWorkQueueFrom(InterpreterBaseImpl* src) {
async_work_queue_ = reinterpret_cast<PirInterpreter*>(src)->GetWorkQueue();
VLOG(8) << "Share AsyncWorkQueue from InterpreterCore(" << src
<< ") to InterpreterCore(" << this << ")";
}
void PirInterpreter::ShareBuildResultsFrom(const InterpreterBaseImpl& src) {
const PirInterpreter& impl = dynamic_cast<const PirInterpreter&>(src);
if (is_shared_results_build_ || !impl.IsSharedResultsBuild()) {
return;
}
// share op dependency
ir_dependency_builder_.ShareDependencyFrom(impl.GetPirDependencyBuilder());
dependency_count_ = impl.GetDependencyCount();
// share event analysis
ir_stream_analyzer_.ShareEventInfoFrom(impl.GetPirStreamAnalyzer());
is_shared_results_build_ = true;
VLOG(8) << "Share Build Results from InterpreterCore(" << &impl
<< ") to InterpreterCore(" << this << ")";
}
std::tuple<double, double> PirInterpreter::InterpreterRunTime() {
double start_time = 0, end_time = 0;
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
start_time = calculate_stream_timer_->StartTime();
end_time = calculate_stream_timer_->EndTime();
#endif
return std::make_tuple(start_time, end_time);
}
const interpreter::PirDependencyBuilder&
PirInterpreter::GetPirDependencyBuilder() const {
return ir_dependency_builder_;
}
std::shared_ptr<std::vector<size_t>> PirInterpreter::GetDependencyCount()
const {
return dependency_count_;
}
const interpreter::PirStreamAnalyzer& PirInterpreter::GetPirStreamAnalyzer()
const {
return ir_stream_analyzer_;
}
bool PirInterpreter::IsSharedResultsBuild() const {
return is_shared_results_build_;
}
std::shared_ptr<interpreter::AsyncWorkQueue> PirInterpreter::GetWorkQueue() {
if (async_work_queue_ == nullptr) {
async_work_queue_ = std::make_shared<interpreter::AsyncWorkQueue>(
execution_config_.host_num_threads,
execution_config_.device_num_threads,
nullptr);
}
return async_work_queue_;
}
void PirInterpreter::PrepareForCUDAGraphCapture() {
if (!FLAGS_new_executor_use_cuda_graph) return;
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
PADDLE_ENFORCE_EQ(
platform::IsCUDAGraphCapturing(),
false,
common::errors::PermissionDenied("CUDA Graph is not allowed to capture "
"before prepare."));
PADDLE_ENFORCE_EQ(phi::is_gpu_place(place_),
true,
common::errors::InvalidArgument(
"CUDA Graph is only supported on NVIDIA GPU device."));
// If set true, will call `cudaStreamSynchronize(nccl_stream)`after allreduce.
// which may cause error in cuda graph. This behavior is consistent with PE.
PADDLE_ENFORCE_EQ(FLAGS_sync_nccl_allreduce,
false,
common::errors::InvalidArgument(
"FLAGS_sync_nccl_allreduce must be False to support "
"CUDA Graph capturing."));
#else
PADDLE_THROW(common::errors::Unimplemented(
"CUDA Graph is only supported on NVIDIA GPU device."));
#endif
}
void PirInterpreter::CheckCUDAGraphBeforeRun(
const std::vector<std::string>& feed_names) {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (platform::IsCUDAGraphCapturing()) {
PADDLE_ENFORCE_EQ(
feed_names.empty(),
true,
common::errors::InvalidArgument(
"Feeding data is not permitted when capturing CUDA Graph."));
PADDLE_ENFORCE_EQ(
FLAGS_new_executor_use_cuda_graph,
true,
common::errors::InvalidArgument(
"You must turn on FLAGS_new_executor_use_cuda_graph to True "
"to enable CUDA Graph capturing."));
PADDLE_ENFORCE_EQ(
place_,
platform::CUDAGraphCapturingPlace(),
common::errors::InvalidArgument("The place to capture CUDAGraph is "
"not the same as the place to run."));
}
#endif
}
void PirInterpreter::ClearDenseTensorArrayInLocalScope() {
auto vars = local_scope_->LocalVars();
for (auto var : vars) {
if (var->IsType<phi::TensorArray>()) {
auto* dense_tensor_arr = var->GetMutable<phi::TensorArray>();
dense_tensor_arr->clear();
}
}
}
std::string PirInterpreter::GetDepsString() const {
std::stringstream ss;
auto downstream_map = ir_dependency_builder_.OpDownstreamMap();
ss << "Note: when static_dep is 1, it is ok that the dynamic_dep will not "
"be decreased to 0."
<< std::endl;
ss << "unfinished_op_number_:" << unfinished_op_number_ << std::endl;
for (size_t i = 0; i < deps_.size(); ++i) {
ss << "op:" << i << ", type: " << vec_instruction_base_[i]->Name()
<< ", static_dep:" << deps_[i]->StaticDep()
<< ", dynamic_dep:" << deps_[i]->DynamicDep() << ", downstream op: ";
for (auto id : downstream_map[i]) {
ss << id << ", ";
}
ss << std::endl;
}
return ss.str();
}
bool PirInterpreter::HasLocalScope() const { return local_scope_ != nullptr; }
Scope* PirInterpreter::InnerScope() const {
return local_scope_ != nullptr ? local_scope_ : scope_;
}
std::string PirInterpreter::GetNameByValue(pir::Value value) const {
return value_exe_info_->GetVarName(value);
}
void PirInterpreter::UpdateSyncOpNum() {
int64_t sync_op_num = 0;
for (auto& ins : vec_instruction_base_) {
if (ins->KernelType() == OpFuncType::kCpuSync ||
ins->KernelType() == OpFuncType::kGpuSync) {
sync_op_num = sync_op_num + 1;
}
}
sync_op_num_ = sync_op_num;
VLOG(4) << "Update sync op num, sync op num is: " << sync_op_num_;
}
void PirInterpreter::UpdateNcclOpNum() {
static std::set<std::string> nccl_op_set = {
"pd_op.c_softmax_with_cross_entropy",
"pd_op.c_softmax_with_multi_label_cross_entropy",
"pd_op.c_allreduce_sum",
"pd_op.c_scatter",
"pd_op.partial_send",
"pd_op.partial_recv",
"pd_op.partial_allgather",
"pd_op.recv_v2",
"pd_op.send_v2",
"pd_op.mp_allreduce_sum",
"pd_op.barrier",
"pd_op.all_to_all",
"pd_op.global_gather",
"pd_op.distributed_fused_lamb",
"pd_op.margin_cross_entropy",
"pd_op.sync_batch_norm",
"pd_op.class_center_sample",
"pd_op.all_to_all",
"pd_op.dist_concat",
"pd_op.all_gather",
"pd_op.broadcast",
"pd_op.p_recv",
"pd_op.p_send",
"pd_op.reduce_scatter",
"pd_op.all_reduce",
"pd_op.reduce",
"pd_op.c_softmax_with_cross_entropy_grad",
"pd_op.c_softmax_with_multi_label_cross_entropy_grad",
"pd_op.c_allreduce_sum_grad",
"pd_op.c_scatter_grad",
"pd_op.partial_send_grad",
"pd_op.partial_recv_grad",
"pd_op.partial_allgather_grad",
"pd_op.recv_v2_grad",
"pd_op.send_v2_grad",
"pd_op.mp_allreduce_sum_grad",
"pd_op.barrier_grad",
"pd_op.alltoall_grad",
"pd_op.global_gather_grad",
"pd_op.c_concat_grad",
"pd_op.distributed_fused_lamb_grad",
"pd_op.margin_cross_entropy_grad",
"pd_op.sync_batch_norm_grad",
"pd_op.class_center_sample_grad",
"pd_op.all_to_all_grad",
"pd_op.dist_concat_grad",
"pd_op.all_gather_grad",
"pd_op.broadcast_grad",
"pd_op.p_recv_grad",
"pd_op.p_send_grad",
"pd_op.reduce_scatter_grad",
"pd_op.all_reduce_grad",
"pd_op.reduce_grad",
"pd_op.c_softmax_with_cross_entropy_",
"pd_op.c_softmax_with_multi_label_cross_entropy_",
"pd_op.c_allreduce_sum_",
"pd_op.c_scatter_",
"pd_op.partial_send_",
"pd_op.partial_recv_",
"pd_op.partial_allgather_",
"pd_op.recv_v2_",
"pd_op.send_v2_",
"pd_op.mp_allreduce_sum_",
"pd_op.barrier_",
"pd_op.alltoall_",
"pd_op.global_gather_",
"pd_op.distributed_fused_lamb_",
"pd_op.margin_cross_entropy_",
"pd_op.sync_batch_norm_",
"pd_op.class_center_sample_",
"pd_op.all_to_all_",
"pd_op.dist_concat_",
"pd_op.all_gather_",
"pd_op.broadcast_",
"pd_op.p_recv_",
"pd_op.p_send_",
"pd_op.reduce_scatter_",
"pd_op.all_reduce_",
"pd_op.reduce_",
"pd_op.c_softmax_with_cross_entropy_grad_",
"pd_op.c_softmax_with_multi_label_cross_entropy_grad_",
"pd_op.c_allreduce_sum_grad_",
"pd_op.c_scatter_grad_",
"pd_op.partial_send_grad_",
"pd_op.partial_recv_grad_",
"pd_op.partial_allgather_grad_",
"pd_op.recv_v2_grad_",
"pd_op.send_v2_grad_",
"pd_op.mp_allreduce_sum_grad_",
"pd_op.barrier_grad_",
"pd_op.alltoall_grad_",
"pd_op.global_gather_grad_",
"pd_op.distributed_fused_lamb_grad_",
"pd_op.margin_cross_entropy_grad_",
"pd_op.sync_batch_norm_grad_",
"pd_op.class_center_sample_grad_",
"pd_op.all_to_all_grad_",
"pd_op.dist_concat_grad_",
"pd_op.all_gather_grad_",
"pd_op.broadcast_grad_",
"pd_op.p_recv_grad_",
"pd_op.p_send_grad_",
"pd_op.reduce_scatter_grad_",
"pd_op.all_reduce_grad_",
"pd_op.reduce_grad_"};
int64_t nccl_op_num = 0;
for (auto& ins : vec_instruction_base_) {
if (nccl_op_set.count(ins->Name())) {
nccl_op_num = nccl_op_num + 1;
} else if (ins->Operation()->HasAttribute("ring_id")) {
nccl_op_num = nccl_op_num + 1;
}
}
nccl_op_num_ = nccl_op_num;
VLOG(4) << "Update nccl op num, nccl op num is: " << nccl_op_num;
}
void PirInterpreter::UpdateOneDNNOpNum() {
int64_t onednn_op_num = 0;
#ifdef PADDLE_WITH_DNNL
for (auto& ins : vec_instruction_base_) {
if (dynamic_cast<OneDNNPhiKernelInstruction*>(ins.get()) != nullptr ||
dynamic_cast<OneDNNLegacyKernelInstruction*>(ins.get()) != nullptr ||
dynamic_cast<OneDNNMixedPhiKernelInstruction*>(ins.get()) != nullptr) {
onednn_op_num = onednn_op_num + 1;
}
}
#endif
onednn_op_num_ = onednn_op_num;
VLOG(4) << "Update onednn op num, onednn op num is: " << onednn_op_num;
}
// Note(zhangbo):
// When there is a KQueueSync type OP in the model, breadth traversal is better
// than depth traversal. For example: OP(O) ->(direct_run)-> OP(A)
// ->(sync_run)-> OP(B) OP(O) ->(direct_run)-> OP(C) ->(direct_run)-> OP(D) If B
// is run before C, B may always block to wait for A to finish executing, but in
// fact, C can be executed first during this time.
void PirInterpreter::AnalyseExecuteOrderForTrace(
std::map<size_t, std::set<size_t>> op_downstream_map,
InstructionSchedulingPriorityLess compare) {
VLOG(4) << "Analyze the execution order of Trace scheduling mode.";
interpreter::ResetAtomicGuard guard(&deps_, &refs_);
auto IsReady = [this](size_t next_id) {
VLOG(4) << "op_id: " << next_id
<< ", remain deps: " << deps_[next_id]->DynamicDep();
return deps_[next_id]->CheckAndDecrease();
};
std::vector<size_t> trace_order;
SchedulingQueue ready_ops(compare);
std::stringstream ss;
if (VLOG_IS_ON(2)) {
ss << "\nLeaf nodes: ";
}
for (size_t instr_id = 0; instr_id < dependency_count_->size(); ++instr_id) {
if ((*dependency_count_)[instr_id] == 0) {
ready_ops.push(instr_id);
if (VLOG_IS_ON(2)) {
ss << instr_id << "[" << vec_instruction_base_[instr_id]->Name()
<< "]->";
}
}
}
while (!ready_ops.empty()) {
size_t now_id = ready_ops.top();
ready_ops.pop();
trace_order.push_back(now_id);
if (VLOG_IS_ON(2)) {
ss << "\n" << now_id << " downstreams: ";
}
auto next_op_set = op_downstream_map[now_id];
for (size_t next_op_id : next_op_set) {
if (IsReady(next_op_id)) {
ready_ops.push(next_op_id);
if (VLOG_IS_ON(2)) {
ss << next_op_id << "[" << vec_instruction_base_[next_op_id]->Name()
<< "]->";
}
}
}
}
PADDLE_ENFORCE_EQ(
trace_order.size(),
dependency_count_->size(),
common::errors::PreconditionNotMet(
"trace_order size should be equal to dependency_count_."));
trace_execute_order_ = trace_order;
if (VLOG_IS_ON(2)) {
std::cout << "======================== pir interpreter trace order "
"========================"
<< std::endl;
std::cout << ss.str() << std::endl;
}
}
void PirInterpreter::AnalyzeForceSyncOps() {
for (auto& ins : vec_instruction_base_) {
ins->SetSyncAfterLaunch(FLAGS_benchmark);
// Analyze force sync op set by FLAGS_force_sync_op
int op_id = ins->Id();
std::string op_name = ins->Name();
std::string unused_prefix = "pd_op.";
auto pos = op_name.find(unused_prefix);
if (pos != std::string::npos) {
op_name.erase(pos, unused_prefix.size());
}
for (auto& pair : execution_config_.force_sync_ops) {
int sync_op_id = pair.first;
std::string sync_op_name = pair.second;
if ((sync_op_id == op_id || sync_op_id == -1) &&
(sync_op_name == op_name || sync_op_name == "")) {
VLOG(8) << "Force sync op: "
<< "sync_op_id=" << sync_op_id << ", op_id=" << op_id
<< ", sync_op_name=" << sync_op_name << ", op_name=" << op_name;
ins->SetSyncAfterLaunch(true);
}
}
}
}
void PirInterpreter::BuildInstruction() {
VLOG(6) << "Build Instructions for pir ... ";
vec_instruction_base_.clear();
size_t op_idx = 0;
for (auto& op : *ir_block_) {
VLOG(6) << "Build Instruction for op: " << op_idx;
if (op.dialect()->name() == "builtin") {
if (op.isa<pir::CombineOp>()) {
vec_instruction_base_.emplace_back(
std::make_unique<BuiltinCombineInstruction>(
op_idx++, place_, &op, value_exe_info_.get()));
} else if (interpreter::GetSpecialOpNames().count(op.name())) {
VLOG(6) << "skip process builtin dialect op: " << op.name();
continue;
}
} else if (op.dialect()->name() == "cf") {
if (op.isa<pir::TuplePushOp>()) {
CREATE_INSTR(TuplePushInstruction);
} else if (op.isa<pir::TuplePopOp>()) {
CREATE_INSTR(TuplePopInstruction);
} else if (op.isa<pir::YieldOp>()) {
CREATE_INSTR(YieldInstruction);
} else {
VLOG(6) << "skip process cf dialect op: " << op.name();
continue;
}
} else if (op.dialect()->name() == "pd_op") {
if (op.isa<paddle::dialect::IfOp>()) { // NOLINT
std::unique_ptr<IfInstruction> if_instr_ptr =
std::make_unique<IfInstruction>(op_idx++,
place_,
&op,
value_exe_info_.get(),
execution_config_);
if_instr_ptr->SetOutputHooks(pir_output_hookfuncs_);
if_instr_ptr->SetInputHooks(pir_input_hookfuncs_);
vec_instruction_base_.emplace_back(std::move(if_instr_ptr));
sub_blocks_.insert(
{&op.dyn_cast<paddle::dialect::IfOp>().true_block(),
dynamic_cast<IfInstruction*>(vec_instruction_base_.back().get())
->TrueBranchInterpreter()});
sub_blocks_.insert(
{&op.dyn_cast<paddle::dialect::IfOp>().false_block(),
dynamic_cast<IfInstruction*>(vec_instruction_base_.back().get())
->FalseBranchInterpreter()});
} else if (op.isa<paddle::dialect::PyLayerOp>()) {
vec_instruction_base_.emplace_back(std::make_unique<PyLayerInstruction>(
op_idx++, place_, &op, value_exe_info_.get(), execution_config_));
sub_blocks_.insert(
{&op.dyn_cast<paddle::dialect::PyLayerOp>().forward_block(),
dynamic_cast<PyLayerInstruction*>(
vec_instruction_base_.back().get())
->ForwardInterpreter()});
} else if (op.isa<paddle::dialect::WhileOp>()) {
std::unique_ptr<WhileInstruction> while_instr_ptr =
std::make_unique<WhileInstruction>(op_idx++,
place_,
&op,
value_exe_info_.get(),
execution_config_);
while_instr_ptr->SetOutputHooks(pir_output_hookfuncs_);
while_instr_ptr->SetInputHooks(pir_input_hookfuncs_);
while_instr_ptr->CheckGCEarly([this](InstructionBase* instr) {
std::unordered_map<pir::Value, std::vector<int>> inputs;
GetInputIds(instr->Operation(), *this->value_exe_info_, &inputs);
auto HasUserInLoopBody = [instr](pir::Value value) {
for (auto it = value.use_begin(); it != value.use_end(); ++it) {
auto user_parent_op = it->owner()->GetParentOp();
while (user_parent_op) {
if (user_parent_op == instr->Operation()) {
return true;
}
user_parent_op = user_parent_op->GetParentOp();
}
}
return false;
};
for (const auto& kv : inputs) {
if (kv.first ==
instr->Operation()->operand_source(0 /*cond var*/)) {
// CheckGCEarly should not gc cond var
continue;
}
if (kv.first.isa<pir::BlockArgument>()) {
continue;
}
if (HasUserInLoopBody(kv.first)) {
continue;
}
auto var_id = this->value_exe_info_->GetVarId(kv.first);
bool is_ready = this->refs_[var_id]->DynamicRef() == 1;
if (is_ready) {
VLOG(4) << "early gc: " << this->GetNameByValue(kv.first);
this->refs_[var_id]->CheckAndDecrease();
this->gc_->Add(this->refs_[var_id]->Var(), instr);
}
}
});
vec_instruction_base_.emplace_back(std::move(while_instr_ptr));
sub_blocks_.insert(
{&op.dyn_cast<paddle::dialect::WhileOp>().body(),
dynamic_cast<WhileInstruction*>(vec_instruction_base_.back().get())
->BodyInterpreter()});
} else if (op.isa<paddle::dialect::HasElementsOp>()) {
CREATE_INSTR(HasElementsInstruction);
} else if (op.isa<paddle::dialect::AssertOp>()) {
CREATE_INSTR(AssertInstruction);
} else if (op.isa<paddle::dialect::SelectInputOp>()) {
CREATE_INSTR(SelectInputInstruction);
} else if (op.isa<paddle::dialect::SelectOutputOp>()) {
CREATE_INSTR(SelectOutputInstruction);
#ifdef PADDLE_WITH_CUDA
} else if (op.isa<paddle::dialect::CudaGraphOp>()) {
auto cuda_graph_instr_ptr =
std::make_unique<CudaGraphInstruction>(op_idx++,
place_,
&op,
&cuda_graph_state_,
cuda_graph_capture_pool_id_,
value_exe_info_.get(),
execution_config_);
cuda_graph_instr_ptr->SetOutputHooks(pir_output_hookfuncs_);
cuda_graph_instr_ptr->SetInputHooks(pir_input_hookfuncs_);
vec_instruction_base_.emplace_back(std::move(cuda_graph_instr_ptr));
sub_blocks_.insert({op.dyn_cast<paddle::dialect::CudaGraphOp>().block(),
dynamic_cast<CudaGraphInstruction*>(
vec_instruction_base_.back().get())
->interpreter()});
#endif
} else if (op.isa<paddle::dialect::TensorRTEngineOp>()) {
#ifdef PADDLE_WITH_TENSORRT
CREATE_INSTR(TensorRTEngineInstruction);
#else
PADDLE_THROW(common::errors::PreconditionNotMet(
"Program has TensorRTEngineOp and must compile Paddle use "
"-DWITH_TENSORRT=ON"));
#endif
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Now only support pd_kernel and cinn dialect."));
}
} else if (op.dialect()->name() == "pd_kernel") {
auto op_name = op.attributes()
.at("op_name")
.dyn_cast<pir::StrAttribute>()
.AsString();
if (interpreter::GetSpecialOpNames().count(op_name)) {
VLOG(6) << "skip process " << op_name;
continue;
}
VLOG(6) << "process " << op_name;
if (op_name == "pd_op.share_var") continue;
if (op.isa<paddle::dialect::LegacyKernelOp>()) { // NOLINT
CREATE_INSTR(LegacyKernelInstruction);
} else {
CREATE_INSTR(PhiKernelInstruction);
}
#ifdef PADDLE_WITH_DNNL
} else if (op.dialect()->name() == "onednn_kernel") {
auto op_name = op.attributes()
.at("op_name")
.dyn_cast<pir::StrAttribute>()
.AsString();
VLOG(6) << "process " << op_name;
if (op.isa<paddle::dialect::OneDNNPhiKernelOp>()) {
CREATE_INSTR(OneDNNPhiKernelInstruction);
} else if (op.isa<paddle::dialect::OneDNNMixedPhiKernelOp>()) {
CREATE_INSTR(OneDNNMixedPhiKernelInstruction);
} else {
CREATE_INSTR(OneDNNLegacyKernelInstruction);
}
#endif
#ifdef PADDLE_WITH_CINN
} else if (op.dialect()->name() == "cinn_runtime") {
CREATE_INSTR(CinnJitInstruction);
#endif
} else if (op.dialect()->name() == "custom_kernel") {
vec_instruction_base_.emplace_back(
std::make_unique<CustomKernelInstruction>(
op_idx++, place_, &op, *(value_exe_info_.get())));
} else if (op.dialect()->name() == "py_func") {
vec_instruction_base_.emplace_back(
std::make_unique<PythonFunctionInstruction>(
op_idx++, place_, &op, *(value_exe_info_.get())));
} else if (paddle::dialect::IsCustomEngineOp(&op)) {
#ifdef PADDLE_WITH_CUSTOM_DEVICE
vec_instruction_base_.emplace_back(
std::make_unique<CustomEngineInstruction>(
op_idx++, place_, &op, value_exe_info_.get(), execution_config_));
#else
PADDLE_THROW(common::errors::PreconditionNotMet(
"Program has CustomEngineOp and must compile Paddle use "
"-DWITH_CUSTOM_DEVICE=ON"));
#endif
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Now only support pd_kernel, onednn_kernel, custom_kernel, trt_op, "
"custom_engine_op "
"and cinn dialect."));
}
}
}
std::string PirInterpreter::DebugInstructions() {
// log format: var[101] = pd_op.relu(var[100]) or for inplace op var[100] =
// pd_op.relu_(var[100])
std::stringstream ss;
ss << "{outputs}"
<< " = "
<< " instruction_name[idx] "
<< "({inputs})"
<< "\n";
uint64_t instr_idx = 0;
for (auto& instr : vec_instruction_base_) {
ss << instr_idx++ << ": ";
std::stringstream ss_outputs;
for (auto& output : instr->Outputs()) {
ss_outputs << "( ";
for (auto id : output.second) {
ss_outputs << id << " ";
}
ss_outputs << ") ";
}
ss << ss_outputs.str();
ss << " = " << instr->Name();
std::stringstream ss_inputs;
for (auto& input : instr->Inputs()) {
ss_inputs << " ( ";
for (auto id : input.second) {
ss_inputs << id << " ";
}
ss_inputs << ") ";
}
ss << ss_inputs.str() << "\n";
}
ss << "---------------------------var_id -> var_name -> "
"variable*---------------------------\n";
for (size_t var_id = 0; var_id < value_exe_info_->GetVarList().size();
var_id++) {
auto* var = value_exe_info_->GetVarList()[var_id];
auto var_name = value_exe_info_->GetVarName(var);
ss << var_id << " -> " << var_name << " -> " << var << "\n";
}
return ss.str();
}
std::string PirInterpreter::DebugDependency() {
std::map<size_t, std::set<size_t>> op_downstream_map =
ir_dependency_builder_.OpDownstreamMap();
std::stringstream ss;
ss << "id -> down_stream_id\n";
for (auto const& pair : op_downstream_map) {
ss << pair.first << " -> ";
std::copy(pair.second.begin(),
pair.second.end(),
std::ostream_iterator<size_t>(ss, " "));
ss << std::endl;
}
return ss.str();
}
std::string PirInterpreter::DebugValueInfo() {
std::stringstream os;
os << "value info of interpretercore " << this << "\n"
<< "value -> var_name -> id -> variable*"
<< "\n";
interpreter::PrintValuesAndVariables(*ir_block_,
value_exe_info_->GetValue2VarName(),
value_exe_info_->GetVar2VarName());
for (auto kv : value_exe_info_->GetValue2VarName()) {
PADDLE_ENFORCE((bool)kv.first,
common::errors::PreconditionNotMet(
"var(%s) should not be nullptr", kv.second));
PADDLE_ENFORCE(value_exe_info_->HasVar(kv.second),
common::errors::PreconditionNotMet(
"var(%s) should exist in var_name_2_id_", kv.second));
auto* var = InnerScope()->FindVar(kv.second);
PADDLE_ENFORCE(
var != nullptr,
common::errors::PreconditionNotMet(
"var(%s) should exist in scope (%p)", kv.second, InnerScope()));
os << kv.first.impl() << " -> " << kv.second << " -> "
<< value_exe_info_->GetVarId(kv.first) << " -> " << var << "\n";
}
return os.str();
}
std::vector<std::string> PirInterpreter::DebugInfo() {
// print block
std::stringstream block_stream;
block_stream << "======================== The network executed by pir "
"interpreter ========================\n";
pir::IrPrinter printer(block_stream);
printer.PrintBlock(*ir_block_);
std::string block_info = block_stream.str();
// print instruction
std::stringstream instr_stream;
instr_stream << "======================== The instruction executed by pir "
"interpreter ========================\n";
instr_stream << DebugInstructions() << "\n";
std::string instr_info = instr_stream.str();
// print dependency
std::stringstream depend_stream;
depend_stream << "======================= The dependency of all instruction "
"========================\n";
depend_stream << DebugDependency() << "\n";
std::string depend_info = depend_stream.str();
return {block_info, instr_info, depend_info};
}
void PirInterpreter::BuildInstructionDependences() {
// analysis the dependences between instructions, add next_instr_list to each
// instr, and set the dependency_count_
size_t instr_num = vec_instruction_base_.size();
dependency_count_ = GetDependencyCount();
if (!is_shared_results_build_) {
dependency_count_->assign(instr_num, 0);
}
std::vector<paddle::framework::InstructionBase*> instructions_ptr;
for (auto& instr : vec_instruction_base_) {
instructions_ptr.push_back(instr.get());
}
auto downstream_map = ir_dependency_builder_.Build(instructions_ptr);
for (size_t instr_id = 0; instr_id < instr_num; ++instr_id) {
InstructionBase* cur_instr = vec_instruction_base_[instr_id].get();
const std::set<size_t>& next_instr_ids = downstream_map[instr_id];
if (FLAGS_new_executor_serial_run) {
for (size_t next_instr_id : next_instr_ids) {
cur_instr->AddNextInstrInSameThread(next_instr_id);
}
} else {
if (cur_instr->KernelType() == OpFuncType::kGpuAsync) {
for (size_t next_instr_id : next_instr_ids) {
if (vec_instruction_base_[next_instr_id]->KernelType() ==
OpFuncType::kGpuAsync) {
cur_instr->AddNextInstrInSameThread(next_instr_id);
} else {
cur_instr->AddNextInstrInDifferentThread(next_instr_id);
}
}
} else {
bool has_instr_in_same_thread = false;
for (size_t next_instr_id : next_instr_ids) {
if (!has_instr_in_same_thread &&
vec_instruction_base_[next_instr_id]->KernelType() !=
OpFuncType::kGpuAsync) {
cur_instr->AddNextInstrInSameThread(next_instr_id);
has_instr_in_same_thread = true;
} else {
cur_instr->AddNextInstrInDifferentThread(next_instr_id);
}
}
}
}
if (!is_shared_results_build_) {
for (size_t next_instr_id : next_instr_ids) {
++(*dependency_count_)[next_instr_id];
}
}
}
}
void PirInterpreter::RecordMemcpyD2H(InstructionBase* instr_node) {
// NOTE(zhiqiu): hot fix for jit input var
if (instr_node->Name() == "pd_op.memcpy_d2h") {
phi::DeviceContextPool& pool = phi::DeviceContextPool::Instance();
auto* default_dev_ctx = pool.Get(place_);
for (auto& event : instr_node->EventsToWait()) {
phi::RecordEvent record(
"RecordStreamEvent", phi::TracerEventType::UserDefined, 10);
VLOG(3) << "Record event on default stream in jit_input_var at op: "
<< instr_node->Name();
event.event_->Record(default_dev_ctx);
}
}
}
void PirInterpreter::RecordStreamForGC(InstructionBase* instr) {
#if !defined(PADDLE_WITH_CUDA) && !defined(PADDLE_WITH_HIP)
PADDLE_THROW(common::errors::Unimplemented(
"RecordStreamForGC is only implemented when compiled with GPU."));
#else
if (FLAGS_pir_interpreter_record_stream_for_gc_cache &&
instr->SkipRecordStreamForGC()) {
return;
}
if (!IsInterpretercoreFastGCEnabled() ||
instr->KernelType() != OpFuncType::kGpuAsync) {
instr->SetSkipRecordStreamForGC(true);
return;
}
if (instr->DeviceContext().GetPlace().GetType() ==
phi::AllocationType::CUSTOM) {
instr->SetSkipRecordStreamForGC(true);
return;
}
phi::RecordEvent record(
"RecordStreamForGC", phi::TracerEventType::UserDefined, 10);
bool skip_record_stream = true;
gpuStream_t stream =
reinterpret_cast<const phi::GPUContext&>(instr->DeviceContext()).stream();
// TODO(lizhiyu): Only analyse the 'send_v2' for GPT pp strategy right now.
// To support all the operators for communicating in the future.
#if defined(PADDLE_WITH_NCCL) || defined(PADDLE_WITH_RCCL)
if (instr->Name() == "pd_op.send_v2") {
pir::Operation* op = instr->Operation();
if (op->HasAttribute("use_calc_stream") &&
op->attribute<pir::BoolAttribute>("use_calc_stream").data() == false) {
int ring_id = op->attribute<pir::Int32Attribute>("ring_id").data();
const auto& comm_context_manager =
phi::distributed::CommContextManager::GetInstance();
stream = static_cast<phi::distributed::NCCLCommContext*>(
comm_context_manager.Get(std::to_string(ring_id)))
->GetStream();
}
}
#endif
auto TensorRecordStream = [&stream,
&skip_record_stream](DenseTensor& tensor) {
auto allocation = tensor.Holder();
if (allocation == nullptr) {
return;
}
const Place& place = allocation->place();
if (phi::is_gpu_place(place)) {
if (memory::RecordStream(allocation, stream)) {
skip_record_stream = false;
}
} else if (phi::is_cuda_pinned_place(place)) {
// TODO(Ruibiao): Here should do something to make sure that the tensor
// is not freed until the H2D copies done. However, simply launch a
// CUDA runtime callback to the H2D stream may lead a high performance
// overhead. As all the cases we meet in H2D are copies from CPUPlace at
// present, we just log a WARNING here. A better design is required.
LOG(WARNING) << "Copy data from a CUDAPinned tensor in an asynchronous "
"manner may lead a data inconsistent";
} else {
// memory copies involve CPUPlace are always synchronous, so just do
// nothing here
}
};
/* NOTE(Ruibiao)Cross-stream tensor synchronization is required only when
* all the following conditions are satisfied:
* 1. The tensor will be GC after running the instruction, i.e., in
* instr.GCCheckVars.
* 2. The stream which initializes this tensor is different from the stream
* which the instruction run in.
* 3. The tensor is the instruction's input, cause we assume that
* instruction will initialize all output tensors with its running stream.
* 4. In the OP function of this instruction, the tensor is an input of a
* async CUDA kernel.
*
* Here we only process the first condition, because:
* 1. Since the RecordStream function will directly return when the recorded
* stream is equal to the owning stream, recording a stream same as which
* initialized this tensor has less time overhead. Conversely, it may take
* more time if we try to extract those cross-stream input vars from
* instr.GCCheckVars.
* 2. Now the instruction has no idea of which vars involving async running
* in OP function, and thus we can not recognize condition 4. It should be
* supported later.
*/
for (int var_id : instr->GCCheckVars()) {
VLOG(4) << "GC sync " << value_exe_info_->GetNameById(var_id);
// persistable var will be ignore while GC
if (parameter_var_names_.count(value_exe_info_->GetNameById(var_id))) {
VLOG(4) << value_exe_info_->GetNameById(var_id)
<< " is a parameter, skip gc";
continue;
}
paddle::framework::Variable* var = value_exe_info_->GetVarList()[var_id];
if (var == nullptr) {
continue;
}
if (var->IsType<DenseTensor>()) {
TensorRecordStream(*(var->GetMutable<DenseTensor>()));
} else if (
var->IsType<
operators::reader::
OrderedMultiDeviceDenseTensorBlockingQueueHolder>()) { // NOLINT
// do nothing
} else if (var->IsType<phi::SelectedRows>()) {
TensorRecordStream(
*(var->GetMutable<phi::SelectedRows>()->mutable_value()));
} else if (var->IsType<phi::TensorArray>()) {
auto* tensor_arr = var->GetMutable<phi::TensorArray>();
for (auto& tensor : *tensor_arr) {
TensorRecordStream(tensor);
}
} else if (var->IsType<phi::SparseCooTensor>()) {
TensorRecordStream(
*(var->GetMutable<phi::SparseCooTensor>()->mutable_indices()));
TensorRecordStream(
*(var->GetMutable<phi::SparseCooTensor>()->mutable_values()));
} else if (var->IsType<phi::SparseCsrTensor>()) {
TensorRecordStream(
*(var->GetMutable<phi::SparseCsrTensor>()->mutable_cols()));
TensorRecordStream(
*(var->GetMutable<phi::SparseCsrTensor>()->mutable_crows()));
TensorRecordStream(
*(var->GetMutable<phi::SparseCsrTensor>()->mutable_values()));
} else if (var->IsType<std::vector<Scope*>>()) {
// do nothing
} else {
PADDLE_THROW(common::errors::Unimplemented(
"The variable(%s) is not supported in eager deletion.",
framework::ToTypeName(var->Type())));
}
}
if (skip_record_stream) {
instr->SetSkipRecordStreamForGC(true);
}
#endif
}
void PirInterpreter::CheckGC(InstructionBase* instr) {
phi::RecordEvent record("CheckGC", phi::TracerEventType::UserDefined, 10);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
RecordStreamForGC(instr);
#endif
std::vector<Variable*> gc_vars;
for (auto var_id : instr->GCCheckVars()) {
VLOG(4) << "GC:" << value_exe_info_->GetNameById(static_cast<int>(var_id))
<< ", id:" << var_id << ", ref:" << refs_[var_id]->DynamicRef();
bool is_ready = refs_[var_id]->CheckAndDecrease();
// ignore all persistable var while GCphi
if (parameter_var_names_.count(
value_exe_info_->GetNameById(static_cast<int>(var_id)))) {
VLOG(4) << value_exe_info_->GetNameById(static_cast<int>(var_id))
<< " is a parameter, skip gc";
continue;
}
if (is_ready) {
VLOG(6) << "Async delete variable with name : "
<< value_exe_info_->GetNameById(static_cast<int>(var_id));
if (use_trace_run_ && FLAGS_async_fast_eager_deletion_mode) {
gc_vars.push_back(refs_[var_id]->Var());
} else {
gc_->Add(refs_[var_id]->Var(), instr);
}
}
}
if (use_trace_run_ && FLAGS_async_fast_eager_deletion_mode) {
async_gc_->Add(gc_vars);
}
for (auto var : instr->EagerGCVars()) {
gc_->Add(var, instr);
}
instr->ClearEagerGCVars();
}
void PirInterpreter::CalculateLastLiveOps() {
VLOG(4) << "PirInterpreter(): " << this << " start CalculateLastLiveOps";
// calculate last_live_ops_
for (size_t op_idx = 0; op_idx < vec_instruction_base_.size(); ++op_idx) {
InstructionBase* instr = vec_instruction_base_[op_idx].get();
std::set<size_t> gc_check_vars;
const std::unordered_map<pir::Value, std::vector<int>>& ins =
instr->Inputs();
const std::unordered_map<pir::Value, std::vector<int>>& outs =
instr->Outputs();
std::unordered_multimap<pir::Value, std::vector<int>> ins_and_outs{
ins.begin(), ins.end()};
if (instr->Name() != "pd_op.fetch") {
ins_and_outs.insert(outs.begin(), outs.end());
}
VLOG(4) << "get gc check vars for: " << instr->Name();
for (auto& item : ins_and_outs) {
for (auto var_id : item.second) {
// skip no_need_buffer input vars
if ((ins.count(item.first) &&
instr->NoNeedBuffer().count(item.first)) ||
instr->Name() == "builtin_combine_instruction" ||
instr->Name() == "pd_op.shadow_feed_tensors") {
continue;
}
gc_check_vars.insert(var_id);
}
}
for (auto var_id : gc_check_vars) {
Scope* inner_scope = InnerScope();
paddle::framework::Variable* var = inner_scope->FindVar(
value_exe_info_->GetNameById(static_cast<int>(var_id)));
PADDLE_ENFORCE_NOT_NULL(
var,
common::errors::NotFound(
"Var(id=%d,%s) should not be nullptr.",
static_cast<int>(var_id),
value_exe_info_->GetNameById(static_cast<int>(var_id))));
if (var->IsType<DenseTensor>() || var->IsType<phi::SelectedRows>() ||
var->IsType<phi::TensorArray>() ||
var->IsType<phi::SparseCooTensor>() ||
var->IsType<phi::SparseCsrTensor>()) {
last_live_ops_[var_id].insert(op_idx);
} else {
VLOG(4) << "not clear "
<< value_exe_info_->GetNameById(static_cast<int>(var_id))
<< " after " << instr->Name() << " because its type is "
<< framework::ToTypeName(var->Type());
}
}
VLOG(4) << "update last_live_ops for: " << instr->Name();
}
// clear the last_live_ops list for all vars in skip_gc_vars
for (const std::string& skip_gc_var : execution_config_.skip_gc_vars) {
int var_id = value_exe_info_->GetIdByName(skip_gc_var);
if (var_id != -1) {
last_live_ops_[var_id].clear();
VLOG(8) << "Skip gc for var: " << skip_gc_var;
}
}
VLOG(4) << "clear the last_live_ops list for all vars in skip_gc_vars";
// shrink, find the downstream op that has no other op in the
// downstream list happens before it
// For example,
// b = op1(a)
// c = op2(a, b)
// in this case, a is the input of op1 and op2, we only need to check
// a after op2, because op2 always uses a after op1.
var_ref_count_.resize(value_exe_info_->GetVarList().size());
VLOG(4) << "last_live_ops_.size() : " << last_live_ops_.size();
for (auto kv : last_live_ops_) {
for (auto val : kv.second) {
VLOG(4) << "var: " << kv.first << " -> op: " << val;
}
}
VLOG(4) << "var_ref_count_.size() : " << var_ref_count_.size();
for (size_t i = 0; i < last_live_ops_.size(); ++i) {
std::set<size_t> minimum_last_live_ops;
for (size_t item : last_live_ops_[i]) {
bool not_before_any = true;
// find the op that is not executed before any
for (size_t other_item : last_live_ops_[i]) {
if (ir_dependency_builder_.OpHappensBefore(item, other_item)) {
VLOG(6) << "happens_before: " << item << "->" << other_item
<< ", so skip " << item;
not_before_any = false;
break;
}
}
if (not_before_any) {
VLOG(6) << "last live op of var " << i << " "
<< value_exe_info_->GetNameById(static_cast<int>(i)) << " : "
<< item << " " << vec_instruction_base_[item]->Name();
minimum_last_live_ops.insert(item);
vec_instruction_base_[item]->AddGCCheckVar(i);
}
}
last_live_ops_[i] = minimum_last_live_ops;
var_ref_count_[i] = static_cast<int>(last_live_ops_[i].size());
}
VLOG(4) << "shrink the last_live_ops list for all vars in skip_gc_vars";
for (auto& dep : *dependency_count_) {
deps_.emplace_back(std::make_shared<interpreter::OpDepInfo>(dep));
}
for (size_t i = 0; i < value_exe_info_->GetVarList().size(); ++i) {
refs_.emplace_back(std::make_shared<interpreter::VarRefInfo>(
var_ref_count_[i], value_exe_info_->GetVarList()[i]));
}
VLOG(4) << "done CalculateLastLiveOps";
}
void PirInterpreter::ConstructEventForJitInput() {
for (size_t i = 0; i < dependency_count_->size(); ++i) {
if ((*dependency_count_)[i] == 0) {
InstructionBase* inst = vec_instruction_base_[i].get();
if (inst->Name() == "pd_op.memcpy_d2h" && phi::is_gpu_place(place_)) {
for (auto& item : inst->Inputs()) {
for (auto var_id : item.second) {
auto name = value_exe_info_->GetNameById(var_id);
if (JitInputVars().count(name)) {
auto device_event = std::make_shared<platform::DeviceEvent>(
place_, platform::GenerateDeviceEventFlag());
VLOG(4) << "Add input event for input: " << name << " of "
<< inst->Name();
inst->AddEventToWait(
i, device_event, ir_stream_analyzer_.GetWaiterType(inst));
}
}
}
}
}
}
}
paddle::framework::FetchList PirInterpreter::Run(
const std::vector<std::string>& feed_names,
const std::vector<DenseTensor>& feed_tensors,
bool need_fetch,
bool enable_job_schedule_profiler,
bool switch_stream) {
enable_job_schedule_profiler_ = enable_job_schedule_profiler;
auto FeedInput = [&] {
VLOG(4) << "Feed inputs";
for (size_t i = 0; i < feed_names.size(); ++i) {
auto* feed_var = InnerScope()->FindVar(feed_names[i]);
PADDLE_ENFORCE_NOT_NULL(
feed_var,
common::errors::NotFound("Variable %s should not be nullptr.",
feed_names[i]));
auto feed_tensor = feed_var->GetMutable<DenseTensor>();
feed_tensor->ShareDataWith(feed_tensors[i]);
feed_tensor->set_lod(feed_tensors[i].lod());
}
};
SetDeviceId(place_);
#ifdef PADDLE_WITH_DNNL
platform::AttachPointerHashToONEDNNKey(this, place_);
platform::RegisterModelLayout(ir_block_, place_);
#endif
FeedInput();
if (!is_build_ || switch_stream) {
LOG_FIRST_N(INFO, 1) << "New Executor is Running ...";
VLOG(4) << DebugValueInfo();
SolvePersistableVarNames();
if (VLOG_IS_ON(6)) {
std::stringstream ss;
for (auto parameter : parameter_var_names_) {
ss << parameter << ", ";
}
VLOG(6) << "Parameter value include: " << ss.str();
}
BuildInstruction();
VLOG(4) << "Done BuildInstruction";
PreAnalysis();
VLOG(4) << "Done PreAnalysis";
if (use_trace_run_) {
LOG_FIRST_N(INFO, 1) << "pir interpreter is running by trace mode ...";
TraceRunImpl();
} else {
LOG_FIRST_N(INFO, 1)
<< "pir interpreter is running by multi-thread mode ...";
MultiThreadRunImpl();
}
is_build_ = true;
is_shared_results_build_ = true;
} else {
if (use_trace_run_) {
TraceRunImpl();
} else {
MultiThreadRunImpl();
}
}
if (HasLocalScope()) {
ClearDenseTensorArrayInLocalScope();
}
// return Fetch Tensors
Scope* inner_scope = InnerScope();
framework::FetchList fetch_res;
if (need_fetch) {
for (auto& var_name : fetch_var_names_) {
auto* var = inner_scope->FindVar(var_name);
VLOG(4) << "fetch " << var_name << "[" << var << "]";
fetch_res.push_back(var->Get<DenseTensor>());
}
}
VLOG(4) << "get fetch list size: " << fetch_res.size();
return fetch_res;
}
FetchList PirInterpreter::Run(const std::vector<std::string>& feed_names,
bool need_fetch,
bool enable_job_schedule_profiler,
bool enable_op_profiling,
bool switch_stream) {
enable_job_schedule_profiler_ = enable_job_schedule_profiler;
if (enable_op_profiling) {
PADDLE_THROW(common::errors::Unimplemented(
"Currently PIR does not support op runtime profiling feature."));
}
SetDeviceId(place_);
#ifdef PADDLE_WITH_DNNL
platform::AttachPointerHashToONEDNNKey(this, place_);
platform::RegisterModelLayout(ir_block_, place_);
#endif
if (!is_build_ || switch_stream) {
LOG_FIRST_N(INFO, 1) << "New Executor is Running ...";
VLOG(4) << DebugValueInfo();
SolvePersistableVarNames();
if (VLOG_IS_ON(6)) {
std::stringstream ss;
for (auto parameter : parameter_var_names_) {
ss << parameter << ", ";
}
VLOG(6) << "Parameter value include: " << ss.str();
}
BuildInstruction();
VLOG(4) << "Done BuildInstruction";
PreAnalysis();
VLOG(4) << "Done PreAnalysis";
// Run
if (use_trace_run_) {
LOG_FIRST_N(INFO, 1) << "pir interpreter is running by trace mode ...";
TraceRunImpl();
} else {
LOG_FIRST_N(INFO, 1)
<< "pir interpreter is running by multi-thread mode ...";
MultiThreadRunImpl();
}
is_build_ = true;
is_shared_results_build_ = true;
} else {
if (use_trace_run_) {
TraceRunImpl();
} else {
MultiThreadRunImpl();
}
}
if (HasLocalScope()) {
ClearDenseTensorArrayInLocalScope();
}
framework::FetchList fetch_res;
if (need_fetch) {
// return Fetch Tensors
Scope* inner_scope = InnerScope();
for (auto& var_name : fetch_var_names_) {
auto* var = inner_scope->FindVar(var_name);
VLOG(4) << "fetch " << var_name << "[" << var << "]";
fetch_res.push_back(var->Get<DenseTensor>());
}
VLOG(4) << "get fetch list size: " << fetch_res.size();
}
return fetch_res;
}
void PirInterpreter::TraceRunImpl() {
// lazy initialization of gc, do not create gc is the program only run once
if (!gc_) {
gc_ = CreateInterpreterCoreGarbageCollector(place_, vec_instruction_base_);
}
if (FLAGS_async_fast_eager_deletion_mode) {
if (!async_gc_) {
async_gc_ = std::make_unique<InterpreterCoreAsyncFastGarbageCollector>(
vec_instruction_base_.size());
} else {
async_gc_->Reset(vec_instruction_base_.size());
}
}
interpreter::ResetAtomicGuard guard(&deps_, &refs_);
VLOG(4) << "Tracing Instruction List";
TraceRunInstructionList(vec_instruction_base_);
VLOG(4) << "Done TraceRunInstructionList";
#ifdef PADDLE_WITH_CUSTOM_DEVICE
if (phi::is_custom_place(place_)) {
phi::DeviceContextPool::Instance().Get(place_)->Wait();
}
#endif
}
void PirInterpreter::MultiThreadRunImpl() {
// lazy initialization of gc, do not create gc is the program only run once
if (!gc_) {
gc_ = CreateInterpreterCoreGarbageCollector(place_, vec_instruction_base_);
}
interpreter::ResetAtomicGuard guard(&deps_, &refs_);
VLOG(4) << "Multi Thread Run Instruction List";
async_work_queue_ = GetWorkQueue();
MultiThreadRunInstructionList(vec_instruction_base_);
VLOG(4) << "Done MultiThreadRunInstructionList";
#ifdef PADDLE_WITH_CUSTOM_DEVICE
if (phi::is_custom_place(place_)) {
phi::DeviceContextPool::Instance().Get(place_)->Wait();
}
#endif
}
void PirInterpreter::TraceRunInstructionList(
const std::vector<std::unique_ptr<InstructionBase>>& vec_instr) {
unfinished_op_number_ = vec_instr.size();
if (unfinished_op_number_ == 0) {
VLOG(4) << "No op to run, return";
return;
}
exception_holder_.Clear();
if (enable_job_schedule_profiler_) {
for (int i = trace_execute_order_.size() - 1; i >= 0; --i) {
auto instr_id = trace_execute_order_[i];
auto* instr_node = vec_instruction_base_.at(instr_id).get();
std::string op_name = instr_node->Name();
pir::Operation* op = instr_node->Operation();
if (op_name != "pd_op.feed" && !op->HasAttribute("ring_id")) {
VLOG(3) << "Last calculated op type: " << op_name;
last_calculate_instr_id_ = instr_node->Id();
break;
}
}
}
for (size_t i = 0; i < dependency_count_->size(); ++i) {
if ((*dependency_count_)[i] == 0) {
// NOTE(zhiqiu): hot fix for jit input var
RecordMemcpyD2H(vec_instr.at(i).get());
}
}
for (size_t idx = 0; idx < trace_execute_order_.size(); idx++) {
auto instr_id = trace_execute_order_[idx];
InstructionBase* instr_node = vec_instruction_base_.at(instr_id).get();
VLOG(6) << "Run InstructionBase " << instr_node->Name() << "[" << instr_id
<< "], op id: " << instr_node->Operation()->id();
RunInstructionBase(instr_node);
if (UNLIKELY(exception_holder_.IsCaught())) {
VLOG(4) << "Exception caught";
break;
}
}
if (UNLIKELY(exception_holder_.IsCaught())) {
VLOG(1) << "Exception caught " << exception_holder_.Type();
PADDLE_ENFORCE_EQ(
main_thread_blocker_.Clear(),
0,
common::errors::PreconditionNotMet(
"main_thread_blocker_.Clear() return -1, clear failed"));
VLOG(4) << "clear ok";
exception_holder_.ReThrow();
}
VLOG(4) << "Done TraceRunInstructionList";
}
void PirInterpreter::MultiThreadRunInstructionList(
const std::vector<std::unique_ptr<InstructionBase>>& vec_instr) {
unfinished_op_number_ = vec_instr.size();
if (unfinished_op_number_ == 0) {
VLOG(4) << "No op to run, return";
return;
}
exception_holder_.Clear();
if (enable_job_schedule_profiler_) {
for (int i = vec_instr.size() - 1; i >= 0; --i) {
auto* instr_node = vec_instr.at(i).get();
std::string op_name = instr_node->Name();
pir::Operation* op = instr_node->Operation();
if (op_name != "pd_op.feed" && !op->HasAttribute("ring_id")) {
VLOG(3) << "Last calculated op type: " << op_name;
last_calculate_instr_id_ = vec_instr.at(i)->Id();
break;
}
}
}
for (size_t i = 0; i < dependency_count_->size(); ++i) {
if ((*dependency_count_)[i] == 0) {
// NOTE(zhiqiu): hot fix for jit input var
RecordMemcpyD2H(vec_instr.at(i).get());
if (FLAGS_new_executor_serial_run) {
RunInstructionBaseAsync(i);
} else {
async_work_queue_->AddTask(vec_instr.at(i)->KernelType(),
[this, i] { RunInstructionBaseAsync(i); });
}
}
}
// For debug hang in main_thread_blocker_.WaitEvent(),
// launch async task to log deps every
// FLAGS_executor_log_deps_every_microseconds, then cancel the std::async when
// main_thread_blocker_.WaitEvent() executed. Why not use std::async instead
// of workqueue? To make sure that the logging thread itself will not affect
// the workqueue
// used in interpretercore.
std::future<int> logged_times;
std::atomic_bool cancel_log = ATOMIC_VAR_INIT(false);
if (FLAGS_executor_log_deps_every_microseconds) {
logged_times = std::async(
std::launch::async,
[this](const std::atomic_bool& cancel) {
int times = 0;
while (!cancel) {
std::this_thread::sleep_for(std::chrono::microseconds(
FLAGS_executor_log_deps_every_microseconds));
// check again, since cancel may be changed during sleep
if (cancel) {
break;
}
VLOG(0) << "deps:\n" << GetDepsString();
times++;
}
return times;
},
std::ref(cancel_log));
}
auto event_name = main_thread_blocker_.WaitEvent();
VLOG(1) << "main_thread_blocker_(" << &main_thread_blocker_
<< ") got event_name: " << event_name;
cancel_log = true;
if (logged_times.valid()) {
VLOG(1) << "Logged deps for " << logged_times.get() << " times";
}
if (UNLIKELY(exception_holder_.IsCaught())) {
VLOG(1) << "Exception caught " << exception_holder_.Type();
// Graceful exit when the executor encountered a fatal error.
// EOF is not a fatal error.
if (exception_holder_.Type() != "EOF") {
async_work_queue_->Cancel();
async_work_queue_.reset();
}
VLOG(4) << "Cancel ok";
PADDLE_ENFORCE_EQ(
main_thread_blocker_.Clear(),
0,
common::errors::PreconditionNotMet(
"main_thread_blocker_.Clear() return -1, clear failed"));
VLOG(4) << "clear ok";
exception_holder_.ReThrow();
}
}
void PirInterpreter::RunInstructionBaseAsync(size_t instr_id) {
// NOTE(Ruibiao): Due to the uncertain order in multi-threading asynchronous
// scheduling, the priority order involved cross-thread scheduling is not
// guaranteed. Only Ops scheduled by the same AddTask call have the guarantee
// of priority order.
SchedulingQueue ready_ops(ir_instruction_scheduling_priority_less);
ready_ops.push(instr_id);
while (!ready_ops.empty()) {
instr_id = ready_ops.top();
ready_ops.pop();
auto* instr_node = vec_instruction_base_.at(instr_id).get();
RunInstructionBase(instr_node);
if (UNLIKELY(exception_holder_.IsCaught())) {
VLOG(4) << "Exception caught";
if (exception_notifier_ != nullptr) {
exception_notifier_->NotifyEvent();
}
return;
}
VLOG(4) << "unfinished_op_number_: " << unfinished_op_number_;
if (UNLIKELY(unfinished_op_number_.fetch_sub(
1, std::memory_order_relaxed) == 1)) {
if (completion_notifier_ != nullptr) {
completion_notifier_->NotifyEvent();
}
}
RunNextInstructions(instr_node, &ready_ops);
}
}
void PirInterpreter::RunNextInstructions(InstructionBase* instr,
SchedulingQueue* reserved_next_ops) {
phi::RecordEvent record(
"RunNextInstructions", phi::TracerEventType::UserDefined, 10);
auto IsReady = [this](size_t next_id) {
VLOG(4) << "op_id: " << next_id
<< ", remain deps: " << deps_[next_id]->DynamicDep();
return deps_[next_id]->CheckAndDecrease();
};
for (size_t next_instr_id : instr->NextInstrsInDifferenceThread()) {
if (IsReady(next_instr_id)) {
async_work_queue_->AddTask(
vec_instruction_base_[next_instr_id]->KernelType(),
[this, next_instr_id]() { RunInstructionBaseAsync(next_instr_id); });
}
}
for (size_t next_instr_id : instr->NextInstrsInSameThread()) {
if (IsReady(next_instr_id)) {
reserved_next_ops->push(next_instr_id);
}
}
}
void PirInterpreter::RunInstructionBase(InstructionBase* instr_node) {
phi::RecordEvent instruction_event(
instr_node->Name(), phi::TracerEventType::Operator, 1);
auto cur_place = instr_node->DeviceContext().GetPlace();
SetDeviceId(cur_place);
try {
instr_node->WaitEvent(cur_place);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (enable_job_schedule_profiler_) {
std::string op_name = instr_node->Name();
pir::Operation* op = instr_node->Operation();
if (!calculate_stream_timer_->IsStarted() && op_name != "pd_op.feed" &&
!op->HasAttribute("ring_id") && op_name != "pd_op.shadow_feed" &&
op_name != "pd_op.full" && op_name != "pd_op.full_int_array") {
VLOG(3) << "Start calculated stream timer from op: " << op_name;
calculate_stream_timer_->Start();
}
}
#endif
RecordLowPrecisionOp(instr_node);
VLOG(2) << "\nbegin: " << __func__ << " OP id:" << instr_node->Id()
<< " name:" << instr_node->Name() << " type:"
<< (instr_node->KernelType() == OpFuncType::kCpuSync
? "kCpuSync"
: (instr_node->KernelType() == OpFuncType::kGpuSync
? "kGpuSync"
: "kGpuAsync"))
<< " runs on " << phi::GetCurrentThreadName() << "\n"
<< "Before: " << cur_place << " "
<< instr_node->DebugStringEx(scope_, value_exe_info_.get());
if (execution_config_.used_for_inference) {
for (auto& hook : pir_input_hookfuncs_) {
hook(instr_node, value_exe_info_.get(), scope_);
}
}
if (FLAGS_enable_collect_shape) {
CollectShapeManager::Instance().CollectShapeInfo(
instr_node, value_exe_info_.get(), scope_);
}
if (!instr_node->IsArtificial()) {
{
phi::RecordEvent record(
"InstrRun", phi::TracerEventType::UserDefined, 10);
instr_node->Run();
}
if (instr_node->IsSyncAfterLaunch()) {
instr_node->DeviceContext().Wait();
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
PADDLE_ENFORCE_GPU_SUCCESS(platform::GpuGetLastError());
VLOG(4) << "Operator(" << instr_node->Name() // NOLINT
<< "): context wait and get last error";
#endif
}
if (FLAGS_check_nan_inf) {
CheckTensorHasNanOrInf(instr_node, scope_, value_exe_info_.get());
}
VLOG(2) << "\ndone: " << __func__ << " OP id:" << instr_node->Id()
<< " name:" << instr_node->Name() << " type:"
<< (instr_node->KernelType() == OpFuncType::kCpuSync
? "kCpuSync"
: (instr_node->KernelType() == OpFuncType::kGpuSync
? "kGpuSync"
: "kGpuAsync"))
<< " runs on " << phi::GetCurrentThreadName() << "\n"
<< "After: " << cur_place << " "
<< instr_node->DebugStringEx(scope_, value_exe_info_.get());
CheckGC(instr_node);
VLOG(4) << "done CheckGC";
memory::LogDeviceMemoryStats(cur_place, instr_node->Name());
}
if (execution_config_.used_for_inference) {
for (auto& hook : pir_output_hookfuncs_) {
hook(instr_node, value_exe_info_.get(), scope_);
}
}
VLOG(5) << "after run kernel";
instr_node->RecordEvent(cur_place);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
if (enable_job_schedule_profiler_) {
if (instr_node->Id() == last_calculate_instr_id_ &&
calculate_stream_timer_->IsStarted()) {
VLOG(3) << "Stop calculated stream timer from op: "
<< instr_node->Name();
calculate_stream_timer_->Stop();
}
}
#endif
} catch (platform::EnforceNotMet& ex) {
auto* op = instr_node->Operation();
const std::vector<std::string> op_callstack_attr =
interpreter::GetInstructionCallStack(op->name(), op->attributes());
framework::InsertCallStackInfo(op->name(), op_callstack_attr, &ex);
if (op->HasAttribute("origin_id")) {
LOG(WARNING)
<< "Instruction OP id: " << instr_node->Id() << ", Ir OP id: "
<< op->attribute("origin_id").dyn_cast<pir::Int64Attribute>().data()
<< ", " << instr_node->Name() << " raises an EnforceNotMet exception "
<< common::demangle(typeid(ex).name());
} else {
LOG(WARNING) << "Instruction OP id: " << instr_node->Id()
<< ", Ir OP id is null"
<< ", " << instr_node->Name()
<< " raises an EnforceNotMet exception "
<< common::demangle(typeid(ex).name());
}
exception_holder_.Catch(std::make_exception_ptr(std::move(ex)));
} catch (platform::EOFException&) {
exception_holder_.Catch(std::current_exception());
} catch (std::exception& ex) {
LOG(WARNING) << instr_node->Name() << " raises an exception "
<< common::demangle(typeid(ex).name());
exception_holder_.Catch(std::current_exception());
} catch (...) {
LOG(WARNING) << instr_node->Name() << " raises an unknown exception";
exception_holder_.Catch(std::current_exception());
}
}
void PirInterpreter::PreAnalysis() {
BuildInstructionDependences();
VLOG(4) << "Done BuildInstructionDependences";
ir_stream_analyzer_.SetForceEventsToWaitInfo(force_events_to_wait_);
ir_stream_analyzer_.ConstructEvents(vec_instruction_base_);
VLOG(4) << "Done ConstructEvents";
// add event for the input var of jit program, since there are async copied
// from gpu_pinned place to gpu place on compute stream.
ConstructEventForJitInput();
VLOG(4) << "AddEventToWait for JitInputVars";
CalculateLastLiveOps();
VLOG(4) << "Done CalculateLastLiveOps";
if (VLOG_IS_ON(2)) {
std::vector<std::string> instr_debug_info = DebugInfo();
for (auto& item : instr_debug_info) {
std::cout << item << std::endl;
}
}
AnalyseExecuteOrderForTrace(ir_dependency_builder_.OpDownstreamMap(),
ir_instruction_scheduling_priority_less);
VLOG(4) << "Done AnalyseExecuteOrderForTrace";
AnalyzeForceSyncOps();
VLOG(4) << "Done AnalyzeForceSyncOps";
UpdateSyncOpNum();
VLOG(4) << "Done UpdateSyncOpNum";
UpdateNcclOpNum();
VLOG(4) << "Done UpdateNcclOpNum";
UpdateOneDNNOpNum();
VLOG(4) << "Done UpdateOneDNNOpNum";
use_trace_run_ = UseTraceRun(execution_config_, onednn_op_num_, sync_op_num_);
}
pir::Value PirInterpreter::GetValueByName(const std::string& var_name) {
for (auto kv : value_exe_info_->GetValue2VarName()) {
if (kv.second == var_name) {
return kv.first;
}
}
return nullptr;
}
void PirInterpreter::SolvePersistableVarNames() {
VLOG(6) << "SolvePersistableVarNames";
for (auto kv : value_exe_info_->GetValue2VarName()) {
pir::Value value = kv.first;
const std::string& var_name = kv.second;
auto bool_attr = value.attribute<pir::BoolAttribute>(kAttrIsPersistable);
if (bool_attr && bool_attr.data()) {
parameter_var_names_.insert(var_name);
}
}
}
Variable* PirInterpreter::DebugVar(const std::string& name) const {
Scope* scope = HasLocalScope() ? local_scope_ : scope_;
auto* var = scope->FindVar(name);
if (var != nullptr) {
return var;
}
for (auto kv : sub_blocks_) {
var = kv.second->DebugVar(name);
if (var != nullptr) {
return var;
}
}
return var;
}
void PirInterpreter::Build(
const std::vector<std::string>& feed_names,
std::vector<paddle::framework::OpFuncNode>* op_func_nodes,
bool switch_stream) {
PADDLE_THROW(common::errors::Unimplemented(
"Build is not implemented in PirInterpreter."));
}
void PirInterpreter::SetCopyProgram(std::shared_ptr<ProgramDesc> prog) {
PADDLE_THROW(common::errors::Unimplemented(
"SetCopyProgram is not implemented in PirInterpreter."));
}
} // namespace paddle::framework