// 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 #include #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(); auto pb = b.data(); 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 GetBlockInput(pir::Block* block) { std::vector 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()) { 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 phi_program, std::shared_ptr 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 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 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 vec_res; for (auto& name : fetch_var_names) { vec_res.push_back(inner_scope_.FindVar(name)->Get()); } return vec_res; } std::vector 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(); ctx->GetOrRegisterDialect(); 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 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 vec_res; for (auto& name : fetch_var_names) { vec_res.push_back(inner_scope_.FindVar(name)->Get()); } return vec_res; } std::vector SubGraphChecker::CheckSpeed() { auto time_phi = RunPhiSpeed(); auto time_cinn = RunCinnSpeed(); LOG(INFO) << "time cost: Phi: " << time_phi << "\tCINN : " << time_cinn << std::endl; std::vector 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 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 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(); ctx->GetOrRegisterDialect(); 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 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 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()) { it = block->erase(it); } else { it++; } } } void SubGraphChecker::InitInputs(const std::vector& 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(); 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(); auto shape = common::vectorize(tensor_type.dims()); auto random = builder .Build( 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(random, name); auto param = scope->Var(name); out_tensor = param->GetMutable(); } 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& input_values, pir::Block* block) { ::pir::IrContext* ctx = ::pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); ::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(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* fetch_names, const std::string& prefix) { for (auto& op : *block) { if (op.isa()) { fetch_names->push_back( op.attribute("name").dyn_cast().AsString()); } } if (fetch_names->size() > 0) { return; } ::pir::IrContext* ctx = ::pir::IrContext::Instance(); ctx->GetOrRegisterDialect(); ::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(block->back().result(i), name, i); fetch_names->push_back(name); } } } // namespace test } // namespace paddle