Files
paddlepaddle--paddle/paddle/fluid/sub_graph/sub_graph_checker.cc
T
2026-07-13 12:40:42 +08:00

414 lines
14 KiB
C++

// 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/sub_graph/sub_graph_checker.h"
#include <chrono>
#include <ctime>
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/cinn/hlir/dialect/operator/ir/manual_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/add_broadcast_to_elementwise_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/cinn_group_cluster_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/lowering_pass/lower_cinn_fusion_op_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/pd_to_cinn_pass.h"
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_attribute.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_type.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/dialect/operator/utils/utils.h"
#include "paddle/fluid/pir/transforms/build_cinn_pass.h"
#include "paddle/pir/include/core/operation_utils.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
namespace paddle {
namespace test {
bool AllClose(const phi::DenseTensor& a,
const phi::DenseTensor& b,
const float rtol = 1e-5,
const float atol = 1e-8) {
if (a.dims() != b.dims() || a.dtype() != b.dtype()) {
return false;
}
if (a.dtype() == phi::DataType::FLOAT32) {
auto pa = a.data<float>();
auto pb = b.data<float>();
for (size_t i = 0; i < a.numel(); ++i) {
if (std::abs(pa[i] - pb[i]) > (atol + rtol * std::abs(pb[i]))) {
LOG(WARNING) << "element pos " << i << "\t" << pa[i] << "\t" << pb[i]
<< std::endl;
return false;
}
}
} else {
PADDLE_THROW(common::errors::Unimplemented(
"ONLY support float32, but received %s", a.dtype()));
}
return true;
}
std::vector<pir::Value> GetBlockInput(pir::Block* block) {
std::vector<pir::Value> inputs;
std::unordered_set<::pir::Value> inner_output;
for (auto& op : *block) {
for (size_t i = 0; i < op.num_results(); ++i) {
inner_output.insert(op.result(i));
}
if (op.isa<paddle::dialect::DataOp>()) {
inputs.push_back(op.result(0));
}
}
std::unordered_set<::pir::Value> value_set;
for (auto& op : *block) {
for (size_t i = 0; i < op.num_operands(); ++i) {
if (!op.operand(i) || !(op.operand_source(i))) {
continue;
}
auto value = op.operand_source(i);
if (!inner_output.count(value) && !value_set.count(value)) {
inputs.push_back(value);
value_set.insert(value);
}
}
}
return inputs;
}
SubGraphChecker::SubGraphChecker(std::shared_ptr<pir::Program> phi_program,
std::shared_ptr<pir::Program> prim_program)
: phi_program_(phi_program), prim_program_(prim_program) {}
bool SubGraphChecker::CheckResult() {
auto phi_res = RunPhiResult();
auto cinn_res = RunCinnResult();
bool check_right = true;
for (size_t i = 0; i < phi_res.size(); ++i) {
auto res = AllClose(phi_res[i], cinn_res[i]);
if (!res) {
check_right = false;
break;
}
VLOG(3) << "compare index " << i << "\t" << res << std::endl;
}
if (check_right) {
LOG(INFO) << "Result check Success" << std::endl;
} else {
LOG(INFO) << "Result check Failed" << std::endl;
}
return check_right;
}
std::vector<phi::DenseTensor> SubGraphChecker::RunPhiResult() {
phi_input_values_ = GetBlockInput(phi_program_->block());
InitInputs(phi_input_values_, phi_program_->block(), &inner_scope_);
AppendFetchOp(phi_program_->block(), &phi_fetch_names_, kOutputPrefix);
phi::Place place = phi::GPUPlace(0);
phi_kernel_program_ = pir::PdOpLowerToKernelPass(phi_program_.get(), place);
paddle::framework::interpreter::ExecutionConfig exec_config;
exec_config.create_local_scope = false;
for (size_t i = 0; i < phi_input_values_.size(); ++i) {
std::string name = kInputPrefix + std::to_string(i);
exec_config.skip_gc_vars.insert(name);
}
std::vector<std::string> fetch_var_names;
for (auto name : phi_fetch_names_) {
fetch_var_names.push_back(name + kFetchSuffix);
}
paddle::framework::InterpreterCore exec(place,
fetch_var_names,
phi_kernel_program_->block(),
&inner_scope_,
exec_config);
exec.Run({}, true);
std::vector<phi::DenseTensor> vec_res;
for (auto& name : fetch_var_names) {
vec_res.push_back(inner_scope_.FindVar(name)->Get<phi::DenseTensor>());
}
return vec_res;
}
std::vector<phi::DenseTensor> SubGraphChecker::RunCinnResult() {
cinn_input_values_ = GetBlockInput(prim_program_->block());
::pir::IrContext* ctx = ::pir::IrContext::Instance();
AppendFetchOp(prim_program_->block(), &cinn_fetch_names_, kOutputPrefix);
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreatePdOpToCinnOpPass());
pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
pm.AddPass(cinn::dialect::ir::CreateLowerCinnFusionOpPass());
pm.Run(prim_program_.get());
phi::Place place = phi::GPUPlace(0);
auto kernel_program = pir::PdOpLowerToKernelPass(prim_program_.get(), place);
std::vector<std::string> fetch_var_names;
for (auto name : cinn_fetch_names_) {
fetch_var_names.push_back(name + kFetchSuffix);
}
paddle::framework::interpreter::ExecutionConfig exec_config;
exec_config.create_local_scope = false;
for (size_t i = 0; i < phi_input_values_.size(); ++i) {
std::string name = kInputPrefix + std::to_string(i);
exec_config.skip_gc_vars.insert(name);
}
paddle::framework::InterpreterCore executor(place,
fetch_var_names,
kernel_program->block(),
&inner_scope_,
exec_config);
executor.Run({}, true);
std::vector<phi::DenseTensor> vec_res;
for (auto& name : fetch_var_names) {
vec_res.push_back(inner_scope_.FindVar(name)->Get<phi::DenseTensor>());
}
return vec_res;
}
std::vector<double> SubGraphChecker::CheckSpeed() {
auto time_phi = RunPhiSpeed();
auto time_cinn = RunCinnSpeed();
LOG(INFO) << "time cost: Phi: " << time_phi << "\tCINN : " << time_cinn
<< std::endl;
std::vector<double> speed_data{time_phi, time_cinn};
return speed_data;
}
double SubGraphChecker::RunPhiSpeed() {
RemoveFetchOp(phi_program_->block());
phi::Place place = phi::GPUPlace(0);
phi_kernel_program_ = pir::PdOpLowerToKernelPass(phi_program_.get(), place);
paddle::framework::interpreter::ExecutionConfig exec_config;
exec_config.create_local_scope = false;
for (size_t i = 0; i < phi_input_values_.size(); ++i) {
std::string name = kInputPrefix + std::to_string(i);
exec_config.skip_gc_vars.insert(name);
}
std::vector<std::string> fetch_var_names;
for (auto name : phi_fetch_names_) {
fetch_var_names.push_back(name + kFetchSuffix);
}
paddle::framework::InterpreterCore exec(place,
fetch_var_names,
phi_kernel_program_->block(),
&inner_scope_,
exec_config);
// warm up
for (size_t i = 0; i < 10; ++i) {
exec.Run({}, true);
}
auto start = std::chrono::system_clock::now();
for (size_t i = 0; i < 10000; ++i) {
exec.Run({}, true);
}
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
return elapsed_seconds.count();
}
double SubGraphChecker::RunCinnSpeed() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
AppendFetchOp(prim_program_->block(), &cinn_fetch_names_, kOutputPrefix);
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreatePdOpToCinnOpPass());
pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
pm.AddPass(cinn::dialect::ir::CreateLowerCinnFusionOpPass());
pm.Run(prim_program_.get());
phi::Place place = phi::GPUPlace(0);
RemoveFetchOp(prim_program_->block());
auto kernel_program = pir::PdOpLowerToKernelPass(prim_program_.get(), place);
std::vector<std::string> fetch_var_names;
for (auto name : cinn_fetch_names_) {
fetch_var_names.push_back(name + kFetchSuffix);
}
paddle::framework::interpreter::ExecutionConfig exec_config;
exec_config.create_local_scope = false;
for (size_t i = 0; i < phi_input_values_.size(); ++i) {
std::string name = kInputPrefix + std::to_string(i);
exec_config.skip_gc_vars.insert(name);
}
paddle::framework::InterpreterCore executor(place,
fetch_var_names,
kernel_program->block(),
&inner_scope_,
exec_config);
for (size_t i = 0; i < 100; ++i) {
executor.Run({}, true);
}
auto start = std::chrono::system_clock::now();
for (size_t i = 0; i < 10000; ++i) {
executor.Run({}, true);
}
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
return elapsed_seconds.count();
}
void SubGraphChecker::RemoveFetchOp(pir::Block* block) {
for (auto it = block->begin(); it != block->end();) {
if (it->isa<paddle::dialect::FetchOp>()) {
it = block->erase(it);
} else {
it++;
}
}
}
void SubGraphChecker::InitInputs(const std::vector<pir::Value>& input_values,
pir::Block* block,
paddle::framework::Scope* scope) {
// build a program, init data and set parameter to scope
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
phi::DenseTensor* out_tensor;
for (size_t i = 0; i < input_values.size(); ++i) {
auto tensor_type =
input_values[i].type().dyn_cast<paddle::dialect::DenseTensorType>();
auto shape = common::vectorize<int64_t>(tensor_type.dims());
auto random =
builder
.Build<paddle::dialect::UniformOp>(
shape,
paddle::dialect::TransToPhiDataType(tensor_type.dtype()),
-0.2,
0.2,
0,
phi::GPUPlace())
.result(0);
auto name = kInputPrefix + std::to_string(i);
builder.Build<pir::SetParameterOp>(random, name);
auto param = scope->Var(name);
out_tensor = param->GetMutable<phi::DenseTensor>();
}
if (input_values.size() > 0) {
phi::Place place = phi::GPUPlace(0);
auto kernel_program = pir::PdOpLowerToKernelPass(program.get(), place);
paddle::framework::interpreter::ExecutionConfig exec_config;
exec_config.create_local_scope = false;
paddle::framework::InterpreterCore executor(
place, {}, kernel_program->block(), scope, exec_config);
executor.Run({}, true);
}
}
void SubGraphChecker::AppendGetParameter(
const std::vector<pir::Value>& input_values, pir::Block* block) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
::pir::Builder builder = ::pir::Builder(ctx, block);
builder.SetInsertionPointToStart(block);
for (size_t i = 0; i < input_values.size(); ++i) {
auto get_param =
builder
.Build<pir::ParameterOp>(kInputPrefix + std::to_string(i),
input_values[i].type())
.result(0);
for (auto it = input_values[i].use_begin();
it != input_values[i].use_end();) {
(it++)->set_source(get_param);
}
}
}
void SubGraphChecker::AppendFetchOp(pir::Block* block,
std::vector<std::string>* fetch_names,
const std::string& prefix) {
for (auto& op : *block) {
if (op.isa<paddle::dialect::FetchOp>()) {
fetch_names->push_back(
op.attribute("name").dyn_cast<pir::StrAttribute>().AsString());
}
}
if (fetch_names->size() > 0) {
return;
}
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
::pir::Builder builder = ::pir::Builder(ctx, block);
for (size_t i = 0; i < block->back().num_results(); ++i) {
auto name = prefix + std::to_string(i);
builder.Build<paddle::dialect::FetchOp>(block->back().result(i), name, i);
fetch_names->push_back(name);
}
}
} // namespace test
} // namespace paddle