chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
add_subdirectory(adt)
if(WITH_TESTING AND WITH_CINN)
paddle_test(test_dialect_convert SRCS dialect_convert_test.cc)
paddle_test(test_add_broadcast_to_elementwise SRCS
add_broadcast_to_elementwise_test.cc)
paddle_test(test_sub_graph_extract SRCS sub_graph_extract_test.cc DEPS
pir_transforms)
paddle_test(test_ir_op_cluster SRCS ir_op_cluster_test.cc DEPS pir_transforms
cinn_transforms)
paddle_test(test_pir_all_path SRCS pir_all_path_test.cc DEPS cinn_transforms)
paddle_test(test_symbolic_lower SRCS symbolic_lower_test.cc)
paddle_test(test_pir_build_cinn_pass SRCS build_cinn_pass_test.cc)
paddle_test(test_compilation_task SRCS compilation_task_test.cc)
paddle_test(test_generate_shape_util_test SRCS generate_shape_util_test.cc
DEPS cinn_op_dialect)
paddle_test(merge_parallel_matmul_pass_test SRCS
merge_parallel_matmul_pass_test.cc)
paddle_test(test_tile_config_searcher SRCS tile_config_searcher_test.cc DEPS
schedule_config_search)
paddle_test(
test_tile_config_searcher_pure_spatial SRCS
tile_config_searcher_pure_spatial_test.cc DEPS schedule_config_search)
paddle_test(test_tile_config_performance SRCS tile_config_performance_test.cc
DEPS schedule_config_search)
paddle_test(test_file_tile_config SRCS file_tile_config_test.cc)
paddle_test(replace_cross_block_reduction_test SRCS
replace_cross_block_reduction_test.cc)
paddle_test(ir_simplify_test SRCS ir_simplify_test.cc)
paddle_test(eliminate_common_factor_of_local_index_test SRCS
eliminate_common_factor_of_local_index_test.cc)
paddle_test(ir_simplify_select_test SRCS ir_simplify_select_test.cc)
paddle_test(ir_simplify_bound_test SRCS ir_simplify_bound_test.cc)
# DO NOT forget add test name here, otherwise it will not be executed in
# CINN CI.
set(cinn_unit_tests
test_dialect_convert
test_add_broadcast_to_elementwise
test_sub_graph_extract
test_pir_all_path
test_pir_build_cinn_pass
test_compilation_task
test_generate_shape_util_test
merge_parallel_matmul_pass_test
test_tile_config_searcher
test_tile_config_searcher_pure_spatial
test_file_tile_config
replace_cross_block_reduction_test)
foreach(test_name ${cinn_unit_tests})
get_property(
env
TEST ${test_name}
PROPERTY ENVIRONMENT)
set_property(TEST ${test_name} PROPERTY ENVIRONMENT ${env})
set_tests_properties(${test_name} PROPERTIES LABELS "RUN_TYPE=CINN")
endforeach()
endif()
@@ -0,0 +1,251 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.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/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
std::vector<pir::Type> CreateDenseTensorTypes(const phi::DDim &dims) {
pir::IrContext *ctx = ::pir::IrContext::Instance();
pir::Type fp32_dtype = ::pir::Float32Type::get(ctx);
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {};
size_t offset = 0;
std::vector<::pir::Type> op_output_types = {::pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset)};
return op_output_types;
}
void BuildProgram(pir::Builder &builder) { // NOLINT
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({4, 3, 16})));
builder.SetInsertionPointToBlockEnd(group_op.block());
paddle::dialect::FullOp full_input_x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_input_y = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{16}, 1.5, phi::DataType::FLOAT32, phi::CPUPlace());
auto add_op = builder.Build<paddle::dialect::AddOp>(full_input_x.result(0),
full_input_y.result(0));
auto relu_op = builder.Build<paddle::dialect::ReluOp>(add_op.result(0));
builder.Build<pir::YieldOp>(std::vector<pir::Value>{relu_op.out()});
}
void BuildProgramBoth(pir::Builder &builder) { // NOLINT
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({10, 10})));
builder.SetInsertionPointToBlockEnd(group_op.block());
paddle::dialect::FullOp full_input_x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{10, 1},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_input_y =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1, 10},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
auto add_op = builder.Build<paddle::dialect::AddOp>(full_input_x.result(0),
full_input_y.result(0));
auto relu_op = builder.Build<paddle::dialect::ReluOp>(add_op.result(0));
builder.Build<pir::YieldOp>(std::vector<pir::Value>{relu_op.out()});
}
void BuildProgramSubBoth(pir::Builder &builder) { // NOLINT
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({10, 10})));
builder.SetInsertionPointToBlockEnd(group_op.block());
paddle::dialect::FullOp full_input_x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{10, 1},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
paddle::dialect::FullOp full_input_y =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1, 10},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
auto sub_op = builder.Build<paddle::dialect::SubtractOp>(
full_input_x.result(0), full_input_y.result(0));
auto relu_op = builder.Build<paddle::dialect::ReluOp>(sub_op.result(0));
builder.Build<pir::YieldOp>(std::vector<pir::Value>{relu_op.out()});
}
TEST(PatternRewrite, broadcast_elementwise) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
pm.Run(&program);
auto it = program.block()
->begin()
->dyn_cast<cinn::dialect::GroupOp>()
.block()
->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::AddOp>(),
true,
common::errors::PreconditionNotMet(
"Expected AddOp but found different operation type: " +
std::string(it->name())));
}
TEST(PatternRewrite, broadcast_elementwise_both) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgramBoth(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
pm.Run(&program);
auto it = program.block()
->begin()
->dyn_cast<cinn::dialect::GroupOp>()
.block()
->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::AddOp>(),
true,
common::errors::PreconditionNotMet(
"Expected AddOp but found different operation type: " +
std::string(it->name())));
}
TEST(PatternRewrite, broadcast_elementwise_sub_both) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgramSubBoth(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
pm.Run(&program);
auto it = program.block()
->begin()
->dyn_cast<cinn::dialect::GroupOp>()
.block()
->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::PreconditionNotMet(
"Expected FullOp but found different operation type: " +
std::string(it->name())));
it++;
PADDLE_ENFORCE_EQ(
it->isa<paddle::dialect::SubtractOp>(),
true,
common::errors::PreconditionNotMet(
"Expected SubtractOp but found different operation type: " +
std::string(it->name())));
}
+8
View File
@@ -0,0 +1,8 @@
if(WITH_TESTING AND WITH_CINN)
paddle_test(map_expr_test SRCS map_expr_test.cc)
set_tests_properties(map_expr_test PROPERTIES LABELS "RUN_TYPE=CINN")
paddle_test(test_data_dependency_graph SRCS data_dependency_graph_test.cc)
paddle_test(test_index_expr SRCS index_expr_test.cc)
paddle_test(test_iter_simplify SRCS iter_simplify_test.cc)
paddle_test(merge_block_utils_test SRCS merge_block_utils_test.cc)
endif()
@@ -0,0 +1,124 @@
// Copyright (c) 2024 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sstream>
#include "paddle/cinn/ir/ir_analyzer/data_dependency_graph.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/stmt.h"
namespace cinn {
namespace ir {
namespace {
using ir::analyzer::DataDependencyGraph;
using ir::analyzer::DepKind;
class TestDataDependencyGraph : public ::testing::Test {
public:
void SetUp() override {
const std::vector<ir::Expr> shape = {};
tensor_a =
ir::_Tensor_::Make(common::UniqName("A"), common::Bool(), shape, shape);
tensor_b =
ir::_Tensor_::Make(common::UniqName("B"), common::Bool(), shape, shape);
tensor_c =
ir::_Tensor_::Make(common::UniqName("C"), common::Bool(), shape, shape);
tensor_d =
ir::_Tensor_::Make(common::UniqName("D"), common::Bool(), shape, shape);
tensor_a->WithBuffer("global", "_" + tensor_a->name + "_temp_buffer");
tensor_b->WithBuffer("global", "_" + tensor_b->name + "_temp_buffer");
tensor_c->WithBuffer("global", "_" + tensor_c->name + "_temp_buffer");
tensor_d->WithBuffer("global", "_" + tensor_d->name + "_temp_buffer");
var_x = ir::Var(ir::Expr(1), ir::Expr(INT32_MAX), "x");
load_a = ir::Load::Make(ir::Expr(tensor_a), {});
load_b = ir::Load::Make(ir::Expr(tensor_b), {});
load_c = ir::Load::Make(ir::Expr(tensor_c), {});
};
ir::Var var_x;
ir::Tensor tensor_a, tensor_b, tensor_c, tensor_d;
ir::Expr load_a, load_b, load_c;
};
TEST_F(TestDataDependencyGraph, TensorDep) {
// A[0] = B[0]
auto store_a_b = ir::stmt::Store(ir::Expr(tensor_a), load_b, {});
// B[0] = C[0]
auto store_b_c = ir::stmt::Store(ir::Expr(tensor_b), load_c, {});
// C[0] = A[0]
auto store_c_a = ir::stmt::Store(ir::Expr(tensor_c), load_a, {});
const std::vector<ir::stmt::StmtRef> stmts = {
store_a_b, store_b_c, store_c_a};
const auto &dep_graph = DataDependencyGraph(stmts);
dep_graph.Print();
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[1]), DepKind::DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[2]), DepKind::DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[1], stmts[2]), DepKind::DEP);
}
TEST_F(TestDataDependencyGraph, VarDep) {
// x = A[0]
auto let_x_a = ir::stmt::Let(ir::Expr(var_x), load_a);
// B[0] = x
auto store_b_x = ir::stmt::Store(ir::Expr(tensor_b), ir::Expr(var_x), {});
const std::vector<ir::stmt::StmtRef> stmts = {let_x_a, store_b_x};
const auto &dep_graph = DataDependencyGraph(stmts);
dep_graph.Print();
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[1]), DepKind::DEP);
}
TEST_F(TestDataDependencyGraph, TensorNoDep) {
// A[0] = B[0]
auto store_a_b = ir::stmt::Store(ir::Expr(tensor_a), load_b, {});
// C[0] = B[0]
auto store_c_b = ir::stmt::Store(ir::Expr(tensor_c), load_b, {});
// D[0] = B[0]
auto store_d_b = ir::stmt::Store(ir::Expr(tensor_d), load_b, {});
const std::vector<ir::stmt::StmtRef> stmts = {
store_a_b, store_c_b, store_d_b};
const auto &dep_graph = DataDependencyGraph(stmts);
dep_graph.Print();
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[1]), DepKind::NO_DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[0], stmts[2]), DepKind::NO_DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[1], stmts[2]), DepKind::NO_DEP);
}
TEST_F(TestDataDependencyGraph, VarNoDep) {
// x = A[0]
auto let_x_a = ir::stmt::Let(ir::Expr(var_x), load_a);
// B[0] = x
auto store_b_x = ir::stmt::Store(ir::Expr(tensor_b), ir::Expr(var_x), {});
// C[0] = x
auto store_c_x = ir::stmt::Store(ir::Expr(tensor_c), ir::Expr(var_x), {});
// D[0] = x
auto store_d_x = ir::stmt::Store(ir::Expr(tensor_d), ir::Expr(var_x), {});
const std::vector<ir::stmt::StmtRef> stmts = {
let_x_a, store_b_x, store_c_x, store_d_x};
const auto &dep_graph = DataDependencyGraph(stmts);
dep_graph.Print();
EXPECT_EQ(dep_graph.HasDependency(stmts[1], stmts[2]), DepKind::NO_DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[1], stmts[3]), DepKind::NO_DEP);
EXPECT_EQ(dep_graph.HasDependency(stmts[2], stmts[3]), DepKind::NO_DEP);
}
} // namespace
} // namespace ir
} // namespace cinn
+688
View File
@@ -0,0 +1,688 @@
// Copyright (c) 2024 CINN 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 <glog/logging.h>
#include <gtest/gtest.h>
#include "paddle/cinn/common/integer_set.h"
#include "paddle/cinn/common/simplify_special_pattern.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_base.h"
#include "paddle/cinn/ir/ir_mutator.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/optim/simplify_util.h"
namespace cinn {
namespace common {
using optim::ChangeSeqOfDivMod;
using optim::CheckPattern;
using optim::ConstructIndexExprByNodeType;
using optim::MatchPattern;
using optim::ParseExpressionFromString;
class TestIndexExpr : public ::testing::Test {
public:
void SetUp() override {
S4 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S4")
.set_index(true);
S5 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S5")
.set_index(true);
S6 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S6")
.set_index(true);
S7 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S7")
.set_index(true);
S8 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S8")
.set_index(true);
S9 = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "S9")
.set_index(true);
f = ir::Var(ir::Expr(static_cast<int64_t>(1)), ir::Expr(INT32_MAX), "f");
};
ir::Var S4, S5, S6, S7, S8, S9, f;
};
TEST_F(TestIndexExpr, IndexExpr_0) {
ir::IndexExpr a(14);
ir::IndexExpr b(7);
Expr d(6);
ir::Expr c0 = a + b;
ir::Expr c1 = a - b;
ir::Expr c2 = a * b;
ir::Expr c3 = a / b;
ir::Expr c4 = a % b;
ir::Expr c5 = a / d.as_index();
ir::Expr c6 = a % d.as_index();
EXPECT_EQ(c0, Expr(21));
EXPECT_EQ(c1, Expr(7));
EXPECT_EQ(c2, Expr(98));
EXPECT_EQ(c3, Expr(2));
EXPECT_EQ(c4, Expr(0));
EXPECT_EQ(c5, Expr(2));
EXPECT_EQ(c6, Expr(2));
}
TEST_F(TestIndexExpr, IndexExpr_1) {
auto test = S6 * S7;
ir::IndexExpr e1 = (S5 * ((S4 * (S5 * (S6 * S7))) / S5));
ir::IndexExpr e2 = (S4 * (S5 * (S6 * S7))) / S5;
ir::IndexExpr e3 = (S4 * S5) / S5;
ir::IndexExpr e4 = (S4 * (S5 * (S6 * S7)) + S5) / S5;
ir::IndexExpr e5 = (S4 * (S5 * (S6 * S7)) + 2 * S5) / S5;
ir::IndexExpr e6 = (S4 * (S5 * (S6 * S7)) + S5 / S6) / S5;
ir::IndexExpr e7 = (S4 * (S5 * (S6 * S7)) + 2 * S5 / S6) / S5;
EXPECT_EQ(e1.Normalize(), ir::IndexExpr((S6 * S7) * S4 * S5));
EXPECT_EQ(e2.Normalize(), ir::IndexExpr((S6 * S7) * S4));
EXPECT_EQ(e3.Normalize(), ir::IndexExpr(S4));
EXPECT_EQ(e4.Normalize(), ir::IndexExpr(((S6 * S7) * S4) + 1));
EXPECT_EQ(e5.Normalize(), ir::IndexExpr(((S6 * S7) * S4) + 2));
EXPECT_EQ(e6.Normalize(), ir::IndexExpr(((S6 * S7) * S4) + (1 / S6)));
EXPECT_EQ(e7.Normalize(), ir::IndexExpr(((S6 * S7) * S4) + (2 / S6)));
}
TEST_F(TestIndexExpr, IndexExpr_2) {
ir::Expr q1 = S4;
ir::Expr q2 = S4;
ir::Expr q3 = S4 + S5;
ir::Expr q4 = S5 + S4;
ir::Expr q5 = S4 * 2 + S5 / 4;
ir::Expr q6 = S5 / 4 + S4 * 2;
ir::Expr q7 = S4 + S5 + S6;
ir::Expr q8 = S5 + (S4 + S6);
ir::Expr q9 = S4 + (S5 + S7 / 4 + S6 * 2);
ir::Expr q10 = S5 + (S4 + S6 * 2 + S7 / 4);
ir::Expr q11 = (S7 + S5) + (S4 + S6);
ir::Expr q12 = (S4 + S5) + (S6 + S7);
ir::Expr q13 = (S4 + S5) * 3 + (S6 / 2 + S7) * 2;
ir::Expr q14 = (S6 / 2 + S7) * 2 + (S4 + S5) * 3;
ir::Expr q15 = (S4 + S5 * 2) * 3 + (S6 / 2 + S7) * 2;
ir::Expr q16 = (S6 / 2 + S7) * 2 + (S4 + S5 * 2) * 3;
ir::Expr q17 = (S4 + S5 * 2) * 3 + (S6 / 2 + S7) * 2 + S4;
ir::Expr q18 = (S6 / 2 + S7) * 2 + (S4 + S5 * 2) * 3 + S4;
ir::Expr q19 = (S4 + S5 * 2) * 3 + (S6 / 2 + S7) * 2 + S4;
ir::Expr q20 = (S6 / 2 + S7) * 2 + (S4 + S5 * 2) * 3 + S5;
EXPECT_EQ(q1.as_index().Normalize(), q2.as_index().Normalize());
EXPECT_EQ(q3.as_index().Normalize(), q4.as_index().Normalize());
EXPECT_EQ(q5.as_index().Normalize(), q6.as_index().Normalize());
EXPECT_EQ(q7.as_index().Normalize(), q8.as_index().Normalize());
EXPECT_EQ(q9.as_index().Normalize(), q10.as_index().Normalize());
EXPECT_EQ(q11.as_index().Normalize(), q12.as_index().Normalize());
EXPECT_EQ(q13.as_index().Normalize(), q14.as_index().Normalize());
EXPECT_EQ(q15.as_index().Normalize(), q16.as_index().Normalize());
EXPECT_EQ(q17.as_index().Normalize(), q18.as_index().Normalize());
EXPECT_NE(q19.as_index().Normalize(), q20.as_index().Normalize());
}
TEST_F(TestIndexExpr, IndexExpr_3) {
// `Add` corner cases
ir::Expr q1 = S4 / S5 * S5 + S4 % S5;
ir::Expr q2 = (S4 + S5) / S6 * S6 + (S4 + S5) % S6;
ir::Expr q3 = S4 / (S5 + S6) * (S5 + S6) + S4 % (S5 + S6);
ir::Expr q4 = (S4 + S5) / (S6 + S7) * (S6 + S7) + (S4 + S5) % (S6 + S7);
ir::Expr q5 = (S4 + S5) / 5 * 5 + (S4 + S5) * 11 % 5;
ir::Expr q14 = (S4 + S5) / (S6 * S7) * S6 * S7 + (S4 + S5) % (S6 * S7);
ir::Expr q15 =
(S4 * 256 + S5 + S6 * 1024) % 25088 / 512 * 512 + (S4 * 256 + S5) % 512;
ir::Expr q16 =
((S4 * 256 + S5) / S6 / S7 * S7 + (S4 * 256 + S5) / S6 % S7) * S6 +
(S4 * 256 + S5) % S6;
ir::Expr q17 = S4 / (S5 * S6) * S6 + S4 % (S5 * S6) / S5;
ir::Expr q18 = (S4 * 1024 + S5 * 256 + S6) / 2097152 * 32 +
(S4 * 1024 + S5 * 256 + S6) % 2097152 / 65536;
// `Div` corner cases
ir::Expr q6 = (S4 % S5 - S4) / S5;
ir::Expr q7 = (S4 - S4 % S5) / S5;
ir::Expr q8 = ((S4 + S5) % S6 - S4 - S5) / S6;
ir::Expr q9 = (S4 + S5 - (S4 + S5) % S6) / S6;
// `Mod` corner cases
ir::Expr q10 = (S4 % S5 - S4) % S5;
ir::Expr q11 = (S4 - S4 % S5) % S5;
ir::Expr q12 = ((S4 + S5) % S6 - S4 - S5) % S6;
ir::Expr q13 = (S4 + S5 - (S4 + S5) % S6) % S6;
EXPECT_EQ(q1.as_index().Normalize(), ir::IndexExpr(S4));
EXPECT_EQ(q2.as_index().Normalize(), ir::IndexExpr(S4 + S5));
EXPECT_EQ(q3.as_index().Normalize(), ir::IndexExpr(S4));
EXPECT_EQ(q4.as_index().Normalize(), ir::IndexExpr(S4 + S5));
EXPECT_EQ(q5.as_index().Normalize(), ir::IndexExpr(S4 + S5));
EXPECT_EQ(q6.as_index().Normalize(), ir::IndexExpr((S4 / S5) * (-1)));
EXPECT_EQ(q7.as_index().Normalize(), ir::IndexExpr(S4 / S5));
EXPECT_EQ(q8.as_index().Normalize(), ir::IndexExpr(((S4 + S5) / S6) * (-1)));
EXPECT_EQ(q9.as_index().Normalize(), ir::IndexExpr((S4 + S5) / S6));
EXPECT_EQ(q10.as_index().Normalize(), ir::IndexExpr(0));
EXPECT_EQ(q11.as_index().Normalize(), ir::IndexExpr(0));
EXPECT_EQ(q12.as_index().Normalize(), ir::IndexExpr(0));
EXPECT_EQ(q13.as_index().Normalize(), ir::IndexExpr(0));
EXPECT_EQ(q14.as_index().Normalize(), ir::IndexExpr(S4 + S5));
EXPECT_EQ(q15.as_index().Normalize(),
ir::IndexExpr((S4 * 256 + S5 + S6 * 1024)) % 25088);
EXPECT_EQ(q16.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
ir::IndexExpr(S4 * 256 + S5));
EXPECT_EQ(q17.as_index().Normalize(), ir::IndexExpr(S4 / S5));
EXPECT_EQ(q18.as_index().Normalize(),
ir::IndexExpr((S4 * 1024 + S5 * 256 + S6) / 65536));
}
TEST_F(TestIndexExpr, Change_Seq_Of_Div_Mod) {
ir::Expr q1 = S4 / S5;
ir::Expr q2 = S4 % S5;
ir::Expr q3 = S4 / S5 % S6;
ir::Expr q4 = S4 / S5 % S6;
EXPECT_EQ(ChangeSeqOfDivMod(q1.as_index()), q1);
EXPECT_EQ(ChangeSeqOfDivMod(q2.as_index()), q2);
EXPECT_EQ(ChangeSeqOfDivMod(q3.as_index()), S4 % (S5 * S6) / S5);
}
TEST_F(TestIndexExpr, Test_ConstructIndexExprByNodeType) {
ir::Expr result_add = ConstructIndexExprByNodeType(
ir::IrNodeTy::Add, S4.as_index(), S5.as_index(), true);
ir::Expr result_sub = ConstructIndexExprByNodeType(
ir::IrNodeTy::Sub, S4.as_index(), S5.as_index(), false);
ir::Expr result_mul = ConstructIndexExprByNodeType(
ir::IrNodeTy::Mul, S4.as_index(), S5.as_index(), true);
ir::Expr result_div = ConstructIndexExprByNodeType(
ir::IrNodeTy::Div, S4.as_index(), S5.as_index(), true);
ir::Expr result_mod = ConstructIndexExprByNodeType(
ir::IrNodeTy::Mod, S4.as_index(), S5.as_index(), true);
ir::Expr result_min = ConstructIndexExprByNodeType(
ir::IrNodeTy::Min, S4.as_index(), S5.as_index(), false);
ir::Expr result_max = ConstructIndexExprByNodeType(
ir::IrNodeTy::Max, S4.as_index(), S5.as_index(), false);
EXPECT_EQ(result_add, S4 + S5);
EXPECT_EQ(result_sub, S4 - S5);
EXPECT_EQ(result_mul, S4 * S5);
EXPECT_EQ(result_div, S4 / S5);
EXPECT_EQ(result_mod, S4 % S5);
EXPECT_EQ(result_min, ir::Min::Make(S4, S5));
EXPECT_EQ(result_max, ir::Max::Make(S4, S5));
}
TEST_F(TestIndexExpr, Test_dynamic) {
ir::Expr q =
((((((((((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) % S5) * S6) +
((((S7 * 1024) + S8) + (S9 * 4096)) % S6)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) % 640) %
S4) *
S6) *
S5)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) / 640) *
S5) *
S6) *
S4)) /
((S5 * S6) * S4)) *
S4) +
(((((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) % S5) * S6) +
((((S7 * 1024) + S8) + (S9 * 4096)) % S6)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) % 640) %
S4) *
S6) *
S5)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) / 640) *
S5) *
S6) *
S4)) /
(S5 * S6)) %
S4)) *
S5) +
(((((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) % S5) * S6) +
((((S7 * 1024) + S8) + (S9 * 4096)) % S6)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) % 640) % S4) *
S6) *
S5)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) / 640) * S5) *
S6) *
S4)) /
S6) %
S5)) *
S6) +
((((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) % S5) * S6) +
((((S7 * 1024) + S8) + (S9 * 4096)) % S6)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) % 640) % S4) *
S6) *
S5)) +
(((((((((S7 * 1024) + S8) + (S9 * 4096)) / S6) / S5) / 640) * S5) *
S6) *
S4)) %
S6));
ir::Expr q1 =
((((((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)) / (S5 * S6)) * S6) *
S5) +
(f % (S5 * S6)));
ir::Expr q2 = ((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)) % (S5 * S6);
ir::Expr q3 = (S5 * S6) * S4 / (S5 * S6);
ir::Expr q4 = (S5 * S6) * S4 % (S5 * S6);
ir::Expr q5 =
(((((((((((((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)) / (S5 * S6)) +
((f / ((S5 * S6) * 640)) * S4)) *
S5) *
S6) +
(f % (S5 * S6))) %
((S5 * S6) * S4)) /
(S5 * S6)) +
(((((((((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)) / (S5 * S6)) +
((f / ((S5 * S6) * 640)) * S4)) *
S5) *
S6) +
(f % (S5 * S6))) /
((S5 * S6) * S4)) *
S4)) *
S5) *
S6) +
(f % (S5 * S6)));
EXPECT_EQ(
q.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
((((((((S7 * 1024) + S8) + (S9 * 4096)) / ((S5 * S6) * 640)) * S5) * S6) *
S4) +
(((((S7 * 1024) + S8) + (S9 * 4096)) % ((S5 * S6) * 640)) %
((S5 * S6) * S4))));
EXPECT_EQ(q1.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4)));
EXPECT_EQ(q2.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
f % (S5 * S6));
EXPECT_EQ(q3.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
Expr(S4));
EXPECT_EQ(q4.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2), Expr(0));
EXPECT_EQ(q5.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
(((((f / ((S5 * S6) * 640)) * S4) * S5) * S6) +
((f % ((S5 * S6) * 640)) % ((S5 * S6) * S4))));
}
TEST_F(TestIndexExpr, CommonFactor) {
ir::Var S0 = ir::Var("S0");
ir::Var S1 = ir::Var("S1");
ir::Var S2 = ir::Var("S2");
ir::Var S3 = ir::Var("S3");
ir::Var S4 = ir::Var("S4");
ir::Var S5 = ir::Var("S5");
ir::Var S6 = ir::Var("S6");
ir::Var S7 = ir::Var("S7");
ir::Var S8 = ir::Var("S8");
ir::Var S9 = ir::Var("S9");
ir::Var S13 = ir::Var("S13");
ir::Var S17 = ir::Var("S17");
ir::Var S21 = ir::Var("S21");
ir::Var tx = ir::Var("tx");
ir::Var bx = ir::Var("bx");
ir::Expr q = ((((((((S1 + S13) + S17) + S21) + S5) + S9)) * S2) * S3);
ir::Expr q1 = (((((((S3 * S5) * S2) + ((S3 * S9) * S2)) + ((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1));
ir::Expr q2 =
(((((((((f * 1024) + tx) + (bx * 4096)) %
((((((((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S3) *
S5) *
S2) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S3) *
S9) *
S2)) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S3) *
S21) *
S2)) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S2) *
S3) *
S17)) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S2) *
S3) *
S13)) +
(((((((((((((((((((((S3 * S5) * S2) * S0) +
(((S3 * S9) * S2) * S0)) +
(((S3 * S21) * S2) * S0)) +
(((S2 * S3) * S17) * S0)) +
(((S2 * S3) * S13) * S0)) +
(((S2 * S3) * S1) * S0)) /
4096) *
4096) +
((S3 * S5) * S2)) +
((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1)) +
4095) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) +
((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
S2) *
S3) *
S1))) /
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) + ((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))) *
(((((S1 + S13) + S17) + S21) + S5) + S9)) *
S2) *
S3) +
((((f * 1024) + tx) + (bx * 4096)) %
(((((((S3 * S5) * S2) + ((S3 * S9) * S2)) + ((S3 * S21) * S2)) +
((S2 * S3) * S17)) +
((S2 * S3) * S13)) +
((S2 * S3) * S1))));
EXPECT_EQ(q.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
(((((((S1 + S13) + S17) + S21) + S5) + S9) * S2) * S3));
EXPECT_EQ(q1.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
(((((((S5 + S9) + S21) + S17) + S13) + S1) * S2) * S3));
EXPECT_EQ(
q2.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel2),
((((f * 1024) + tx) + (bx * 4096)) %
((((((((((((((((S5 + S9) + S21) + S17) + S13) + S1) * S2) * S3) * S0) /
4096) *
4096) +
(((((((S5 + S9) + S21) + S17) + S13) + S1) * S2) * S3)) +
4095) /
(((((((S5 + S9) + S21) + S17) + S13) + S1) * S2) * S3)) *
S3) *
S2) *
(((((S5 + S9) + S21) + S17) + S13) + S1))));
}
TEST_F(TestIndexExpr, TestCheckPattern) {
ir::Var a = ir::Var("a");
ir::Var b = ir::Var("b");
ir::Var f = ir::Var("f");
ir::Var S0 = ir::Var("S0");
ir::Var S1 = ir::Var("S1");
ir::Var S2 = ir::Var("S2");
ir::Var S3 = ir::Var("S3");
ir::Var S4 = ir::Var("S4");
ir::Var S5 = ir::Var("S5");
ir::Var S6 = ir::Var("S6");
ir::Var S7 = ir::Var("S7");
ir::Var S8 = ir::Var("S8");
ir::Var S9 = ir::Var("S9");
ir::IndexExpr pattern = f / (a * b) * b + f % (a * b) / a;
ir::IndexExpr pattern1 = f / (a * b) * a + f % (a * b) / b;
ir::IndexExpr e = (S0 * (S1 + S2) + S1 * S2 + S2) / (S4 * S5) * S5 +
(S0 * (S1 + S2) + S1 * S2 + S2) % (S4 * S5) / S4;
ir::IndexExpr e1 = (S0 * (S1 + S2) + S1 * S2 + S2) / (S4 * S5) * S4 +
(S0 * (S1 + S2) + S1 * S2 + S2) % (S4 * S5) / S5;
std::unordered_map<std::string, ir::IndexExpr> map;
EXPECT_TRUE(CheckPattern(e, pattern, &map));
map.clear();
EXPECT_FALSE(CheckPattern(e, pattern1, &map));
map.clear();
EXPECT_FALSE(CheckPattern(e1, pattern, &map));
map.clear();
EXPECT_TRUE(CheckPattern(e1, pattern1, &map));
}
TEST_F(TestIndexExpr, ParseExpression) {
ir::Var a = ir::Var("a");
ir::Var b = ir::Var("b");
ir::Var a1 = ir::Var("a_1");
ir::Var b2 = ir::Var("b2");
ir::Expr e1 = a + b;
ir::Expr e2 = a - b;
ir::Expr e3 = a * b;
ir::Expr e4 = a / b;
ir::Expr e5 = a % b;
ir::Expr e6 = a + ir::Expr(20);
ir::Expr e7 = a - ir::Expr(10);
ir::Expr e8 = ir::Expr(5) * b;
ir::Expr e9 = ir::Expr(20) / b;
ir::Expr e10 = a % ir::Expr(3) + b;
ir::Expr e11 = (a + b) * (a - b);
ir::Expr e12 = (a + (b * a)) - (b / a);
ir::Expr e13 = (a + b) * (a - b) + (a / b) - (b % a);
ir::Expr e14 = a1 + b2;
ir::Expr e15 = a + b;
EXPECT_EQ(e1, ParseExpressionFromString("a + b"));
EXPECT_EQ(e2, ParseExpressionFromString("a - b"));
EXPECT_EQ(e3, ParseExpressionFromString("a * b"));
EXPECT_EQ(e4, ParseExpressionFromString("a / b"));
EXPECT_EQ(e5, ParseExpressionFromString("a % b"));
EXPECT_EQ(e6, ParseExpressionFromString("a + 20"));
EXPECT_EQ(e7, ParseExpressionFromString("a - 10"));
EXPECT_EQ(e8, ParseExpressionFromString("5 * b"));
EXPECT_EQ(e9, ParseExpressionFromString("20 / b"));
EXPECT_EQ(e10, ParseExpressionFromString("a % 3 + b"));
EXPECT_EQ(e11, ParseExpressionFromString("(a + b) * (a - b)"));
EXPECT_EQ(e12, ParseExpressionFromString("(a + (b * a)) - (b / a)"));
EXPECT_EQ(e13,
ParseExpressionFromString("(a + b) * (a - b) + (a / b) - (b % a)"));
EXPECT_EQ(e14, ParseExpressionFromString("a_1 + b2"));
EXPECT_EQ(e15, ParseExpressionFromString(" a + b "));
EXPECT_ANY_THROW(ParseExpressionFromString("a + #"));
EXPECT_ANY_THROW(ParseExpressionFromString("(a + b"));
EXPECT_ANY_THROW(ParseExpressionFromString(""));
}
TEST_F(TestIndexExpr, MatchPattern) {
ir::Var a = ir::Var("a");
ir::Var b = ir::Var("b");
ir::Var x = ir::Var("x");
ir::Var y = ir::Var("y");
ir::IndexExpr expr1 = a + b;
ir::IndexExpr expr2 = a * b;
ir::IndexExpr expr3 = a + (b * 10);
ir::IndexExpr expr4 = (a + b) * 10;
ir::IndexExpr expr5 = x + y;
ir::IndexExpr expr6 = x * y;
auto result1 = MatchPattern(expr1, "a + b", nullptr);
EXPECT_TRUE(result1.has_value());
EXPECT_EQ(result1->at("a"), a);
EXPECT_EQ(result1->at("b"), b);
auto result2 = MatchPattern(expr3, "a + (b * 10)", nullptr);
EXPECT_TRUE(result2.has_value());
EXPECT_EQ(result2->at("a"), a);
EXPECT_EQ(result2->at("b"), b);
auto result3 = MatchPattern(expr1, "a * b", nullptr);
EXPECT_FALSE(result3.has_value());
auto result4 = MatchPattern(expr3, "a + (b * 20)", nullptr);
EXPECT_FALSE(result4.has_value());
auto condition =
[](const std::unordered_map<std::string, ir::IndexExpr> &map) {
return map.at("a") == Expr(ir::Var("a")) &&
map.at("b") == Expr(ir::Var("b"));
};
auto result5 = MatchPattern(expr1, "a + b", condition);
EXPECT_TRUE(result5.has_value());
auto condition2 =
[](const std::unordered_map<std::string, ir::IndexExpr> &map) {
return map.at("a") == ir::Var("x") && map.at("b") == ir::Var("y");
};
auto result6 = MatchPattern(expr1, "a + b", condition2);
EXPECT_FALSE(result6.has_value());
auto result7 = MatchPattern(expr4, "(a + b) * 10", nullptr);
EXPECT_TRUE(result7.has_value());
EXPECT_EQ(result7->at("a"), a);
EXPECT_EQ(result7->at("b"), b);
auto result8 = MatchPattern(expr1, "x + y", nullptr);
EXPECT_TRUE(result8.has_value());
EXPECT_EQ(result8->at("x"), a);
EXPECT_EQ(result8->at("y"), b);
auto result9 = MatchPattern(expr6, "x * y", nullptr);
EXPECT_TRUE(result9.has_value());
EXPECT_EQ(result9->at("x"), x);
EXPECT_EQ(result9->at("y"), y);
}
TEST_F(TestIndexExpr, BoundSimplify) {
ir::Var S0 = ir::Var("S0");
ir::Var i = ir::Var(ir::Expr(0), ir::Expr(5), "i"); // i ∈ [0, 5)
ir::Var j = ir::Var(ir::Expr(0), S0, "j"); // j ∈ [0, S0)
ir::Expr q0 = i / Expr(5);
ir::Expr q1 = i / Expr(4);
ir::Expr q2 = i / Expr(6);
ir::Expr q3 = j / S0;
ir::Expr q4 = j / (S0 - 1);
ir::Expr q5 = j / (S0 + 1);
ir::Expr q6 = i % Expr(5);
ir::Expr q7 = i % Expr(4);
ir::Expr q8 = i % Expr(6);
ir::Expr q9 = j % S0;
ir::Expr q10 = j % (S0 - 1);
ir::Expr q11 = j % (S0 + 1);
EXPECT_EQ(q0.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
ir::Expr(0));
EXPECT_EQ(q1.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
i / Expr(4));
EXPECT_EQ(q2.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
ir::Expr(0));
EXPECT_EQ(q3.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
ir::Expr(0));
EXPECT_EQ(q4.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
j / (S0 + ir::Expr(-1)));
EXPECT_EQ(q5.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
ir::Expr(0));
EXPECT_EQ(q6.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3), i);
EXPECT_EQ(q7.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
i % Expr(4));
EXPECT_EQ(q8.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3), i);
EXPECT_EQ(q9.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3), j);
EXPECT_EQ(q10.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3),
j % (S0 + ir::Expr(-1)));
EXPECT_EQ(q11.as_index().Normalize(ir::IndexExpr::OptLevel::kLevel3), j);
}
} // namespace common
} // namespace cinn
+518
View File
@@ -0,0 +1,518 @@
// Copyright (c) 2024 CINN 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/cinn/common/iter_simplify.h"
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "paddle/cinn/common/integer_set.h"
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/common/simplify_special_pattern.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/schedule/schedule_base.h"
namespace cinn {
namespace common {
#define ITER_MARK_VAR(var) \
ir::IterMark::Make(ir::IndexExpr(var.ptr()), var->upper_bound)
#define ITER_MARK_SUM(sum, ext) ir::IterMark::Make(sum, ext)
#define ITER_SPLIT(mark, ...) ir::IterSplit::Make(mark, ##__VA_ARGS__)
#define ITER_SUM(...) ir::IterSum::Make({__VA_ARGS__}, ir::IndexExpr(0))
#define ITER_SUM_WITH_BASE(base, ...) ir::IterSum::Make({__VA_ARGS__}, base)
#define TEST_EXPR(expr, expected, expr_norm) \
rewriter.Rewrite(&expr); \
EXPECT_EQ(expr, Expr(expected)); \
normalizer.Convert(&expr); \
EXPECT_EQ(expr, expr_norm);
class TestIterSimplify : public ::testing::Test {
public:
void SetUp() override {
i = ir::Var(ir::Expr(0), ir::Expr(2), "i").set_index(1);
j = ir::Var(ir::Expr(0), ir::Expr(4), "j").set_index(1);
k = ir::Var(ir::Expr(0), ir::Expr(8), "k").set_index(1);
i_j_k_fused =
ir::Var(ir::Expr(0), ir::Expr(64), "i_j_k_fused").set_index(1);
var_intervals = {
{"i", CasInterval(i->lower_bound, i->upper_bound - ir::Expr(1))},
{"j", CasInterval(j->lower_bound, j->upper_bound - ir::Expr(1))},
{"k", CasInterval(k->lower_bound, k->upper_bound - ir::Expr(1))},
{"i_j_k_fused",
CasInterval(i_j_k_fused->lower_bound,
i_j_k_fused->upper_bound - ir::Expr(1))}};
};
ir::Var i;
ir::Var j;
ir::Var k;
ir::Var i_j_k_fused;
cas_intervals_t var_intervals;
SymbolicExprAnalyzer analyzer{var_intervals};
};
TEST_F(TestIterSimplify, IterExprMake) {
// IterMark Make func.
auto mark_expr = ITER_MARK_VAR(i);
auto mark_expr_ = ITER_MARK_VAR(j);
// IterSplit Make func.
auto split_0_expr = ITER_SPLIT(mark_expr);
auto split_1_expr = ITER_SPLIT(mark_expr, ir::IndexExpr(1));
auto split_2_expr = ITER_SPLIT(
mark_expr, ir::IndexExpr(1), ir::IndexExpr(2), ir::IndexExpr(1));
auto split_3_expr = ITER_SPLIT(
mark_expr, ir::IndexExpr(2), ir::IndexExpr(2), ir::IndexExpr(1));
auto split_4_expr = ITER_SPLIT(
mark_expr_, ir::IndexExpr(1), ir::IndexExpr(2), ir::IndexExpr(1));
// IterSum Make func.
auto sum_expr = ITER_SUM(split_0_expr, split_1_expr, split_2_expr);
auto mark = mark_expr.As<ir::IterMark>();
auto split_0 = split_0_expr.As<ir::IterSplit>();
auto split_1 = split_1_expr.As<ir::IterSplit>();
auto split_2 = split_2_expr.As<ir::IterSplit>();
auto sum = sum_expr.As<ir::IterSum>();
EXPECT_EQ(mark->source, ir::IndexExpr(i.ptr()));
EXPECT_EQ(mark->extent, ir::IndexExpr(2));
EXPECT_EQ(split_0->source, mark_expr);
EXPECT_EQ(split_0->lower_factor, ir::IndexExpr(1));
EXPECT_EQ(split_0->extent, ir::IndexExpr(2));
EXPECT_EQ(split_0->scale, ir::IndexExpr(1));
EXPECT_EQ(split_1->source, mark_expr);
EXPECT_EQ(split_1->lower_factor, ir::IndexExpr(1));
EXPECT_EQ(split_1->extent, ir::IndexExpr(2));
EXPECT_EQ(split_1->scale, ir::IndexExpr(1));
EXPECT_EQ(split_2->source, mark_expr);
EXPECT_EQ(split_2->lower_factor, ir::IndexExpr(1));
EXPECT_EQ(split_2->extent, ir::IndexExpr(2));
EXPECT_EQ(split_2->scale, ir::IndexExpr(1));
EXPECT_EQ(sum->args.size(), 3);
EXPECT_EQ(sum->base, Expr(0));
EXPECT_NE(mark_expr, mark_expr_);
EXPECT_EQ(split_0_expr, split_1_expr);
EXPECT_EQ(split_1_expr, split_2_expr);
EXPECT_NE(split_2_expr, split_3_expr);
}
TEST_F(TestIterSimplify, conversion) {
IterMapRewriter rewriter{{i}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
ir::Expr e1 = i;
auto gt = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i)));
TEST_EXPR(e1, gt, e1);
}
TEST_F(TestIterSimplify, add) {
IterMapRewriter rewriter{{i, j, k}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 =
ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i)), ITER_SPLIT(ITER_MARK_VAR(j)));
auto gt2 = ITER_SUM_WITH_BASE(ir::IndexExpr(5),
ITER_SPLIT(ITER_MARK_VAR(i)),
ITER_SPLIT(ITER_MARK_VAR(j)),
ITER_SPLIT(ITER_MARK_VAR(k)));
auto gt3 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(2)));
auto gt4 = ITER_SUM_WITH_BASE(ir::IndexExpr(12));
ir::Expr e1 = i + j;
ir::Expr e2 = i + j + k + 5;
ir::Expr e3 = i + i;
ir::Expr e4 = Expr(7) + Expr(5);
TEST_EXPR(e1, gt1, i + j);
TEST_EXPR(e2, gt2, i + j + k + 5);
TEST_EXPR(e3, gt3, i * 2);
TEST_EXPR(e4, gt4, Expr(12));
}
TEST_F(TestIterSimplify, sub) {
IterMapRewriter rewriter{{i, j, k}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i)),
ITER_SPLIT(ITER_MARK_VAR(j), ir::IndexExpr(-1)));
auto gt2 =
ITER_SUM_WITH_BASE(ir::IndexExpr(5),
ITER_SPLIT(ITER_MARK_VAR(i)),
ITER_SPLIT(ITER_MARK_VAR(j)),
ITER_SPLIT(ITER_MARK_VAR(k), ir::IndexExpr(-1)));
auto gt3 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(0)));
auto gt4 = ITER_SUM_WITH_BASE(ir::IndexExpr(2));
ir::Expr e1 = i - j;
ir::Expr e2 = i + j - k + 5;
ir::Expr e3 = i - i;
ir::Expr e4 = Expr(7) - Expr(5);
TEST_EXPR(e1, gt1, (j * -1) + i);
TEST_EXPR(e2, gt2, i + j + (k * -1) + 5);
TEST_EXPR(e3, gt3, Expr(0));
TEST_EXPR(e4, gt4, Expr(2));
}
TEST_F(TestIterSimplify, mul) {
IterMapRewriter rewriter{{i, j, k}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(j)));
auto gt2 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(j), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(k)));
auto gt3 = ITER_SUM_WITH_BASE(ir::IndexExpr(10),
ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(j), ir::IndexExpr(2)),
ITER_SPLIT(ITER_MARK_VAR(k)));
auto gt4 = ITER_SUM_WITH_BASE(ir::IndexExpr(35));
ir::Expr e1 = i * 2 + j;
ir::Expr e2 = (i + j) * 2 + k;
ir::Expr e3 = (i + j + 5) * 2 + k;
ir::Expr e4 = Expr(7) * Expr(5);
TEST_EXPR(e1, gt1, i * 2 + j);
TEST_EXPR(e2, gt2, (i + j) * 2 + k);
TEST_EXPR(e3, gt3, (i + j) * 2 + k + 10);
TEST_EXPR(e4, gt4, Expr(35));
}
TEST_F(TestIterSimplify, div) {
IterMapRewriter rewriter{{i, j, k, i_j_k_fused}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(8),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt2 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(32),
ir::IndexExpr(2),
ir::IndexExpr(1)));
auto gt3 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused)));
auto gt4 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused), ir::IndexExpr(2)));
auto gt5 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(2),
ir::IndexExpr(32),
ir::IndexExpr(1)));
auto gt6 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(8),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(72)),
ir::IndexExpr(16),
ir::IndexExpr(5),
ir::IndexExpr(1)));
auto gt7 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(1),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(65)),
ir::IndexExpr(2),
ir::IndexExpr(33),
ir::IndexExpr(1)));
auto gt8 = ITER_SUM_WITH_BASE(ir::IndexExpr(2),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(8),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt9 = ITER_SUM_WITH_BASE(
ir::IndexExpr(2),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused), ir::IndexExpr(2)));
auto gt10 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(1),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(65)),
ir::IndexExpr(8),
ir::IndexExpr(9),
ir::IndexExpr(1)));
auto gt11 = ITER_SUM_WITH_BASE(ir::IndexExpr(3));
auto gt12 = ITER_SUM_WITH_BASE(ir::IndexExpr(3));
auto gt13 = ITER_SUM_WITH_BASE(ir::IndexExpr(15));
auto gt14 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
ir::Expr e1 = i_j_k_fused / 8;
ir::Expr e2 = i_j_k_fused / 8 / 4;
ir::Expr e3 = i_j_k_fused / 1;
ir::Expr e4 = i_j_k_fused * 16 / 8;
ir::Expr e5 = i_j_k_fused * 8 / 16;
ir::Expr e6 = (i_j_k_fused + 8) / 16;
ir::Expr e7 = (i_j_k_fused * 8 + 8) / 16;
ir::Expr e8 = (i_j_k_fused + 16) / 8;
ir::Expr e9 = (i_j_k_fused * 16 + 16) / 8;
ir::Expr e10 = (i_j_k_fused + 1) / 8;
ir::Expr e11 = Expr(15) / Expr(5);
ir::Expr e12 = Expr(15) / Expr(4);
ir::Expr e13 = Expr(15) / Expr(1);
ir::Expr e14 = Expr(0) / Expr(4);
TEST_EXPR(e1, gt1, i_j_k_fused / 8);
TEST_EXPR(e2, gt2, i_j_k_fused / 32);
TEST_EXPR(e3, gt3, i_j_k_fused);
TEST_EXPR(e4, gt4, i_j_k_fused * 2);
TEST_EXPR(e5, gt5, i_j_k_fused / 2);
TEST_EXPR(e6, gt6, (i_j_k_fused + 8) / 16);
TEST_EXPR(e7, gt7, (i_j_k_fused + 1) / 2);
TEST_EXPR(e8, gt8, i_j_k_fused / 8 + 2);
TEST_EXPR(e9, gt9, i_j_k_fused * 2 + 2);
TEST_EXPR(e10, gt10, (i_j_k_fused + 1) / 8);
TEST_EXPR(e11, gt11, Expr(3));
TEST_EXPR(e12, gt12, Expr(3));
TEST_EXPR(e13, gt13, Expr(15));
TEST_EXPR(e14, gt14, Expr(0));
}
TEST_F(TestIterSimplify, mod) {
IterMapRewriter rewriter{{i, j, k, i_j_k_fused}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt2 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(8),
ir::IndexExpr(4),
ir::IndexExpr(1)));
auto gt3 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt4 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt5 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(2),
ir::IndexExpr(8)));
auto gt6 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(8),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(72)),
ir::IndexExpr(1),
ir::IndexExpr(16),
ir::IndexExpr(1)));
auto gt7 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(1),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(64),
ir::IndexExpr(1))),
ir::IndexExpr(65)),
ir::IndexExpr(1),
ir::IndexExpr(2),
ir::IndexExpr(8)));
auto gt8 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt9 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt10 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM_WITH_BASE(ir::IndexExpr(1),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused))),
ir::IndexExpr(65)),
ir::IndexExpr(1),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt11 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt12 = ITER_SUM_WITH_BASE(ir::IndexExpr(3));
auto gt13 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
auto gt14 = ITER_SUM_WITH_BASE(ir::IndexExpr(0));
ir::Expr e1 = i_j_k_fused % 8;
ir::Expr e2 = i_j_k_fused / 8 % 4;
ir::Expr e3 = i_j_k_fused % 1;
ir::Expr e4 = i_j_k_fused * 16 % 8;
ir::Expr e5 = i_j_k_fused * 8 % 16;
ir::Expr e6 = (i_j_k_fused + 8) % 16;
ir::Expr e7 = (i_j_k_fused * 8 + 8) % 16;
ir::Expr e8 = (i_j_k_fused + 16) % 8;
ir::Expr e9 = (i_j_k_fused * 16 + 16) % 8;
ir::Expr e10 = (i_j_k_fused + 1) % 8;
ir::Expr e11 = Expr(15) % Expr(5);
ir::Expr e12 = Expr(15) % Expr(4);
ir::Expr e13 = Expr(15) % Expr(1);
ir::Expr e14 = Expr(0) % Expr(4);
TEST_EXPR(e1, gt1, i_j_k_fused % 8);
TEST_EXPR(e2, gt2, i_j_k_fused % 32 / 8);
TEST_EXPR(e3, gt3, Expr(0));
TEST_EXPR(e4, gt4, Expr(0));
TEST_EXPR(e5, gt5, i_j_k_fused % 2 * 8);
TEST_EXPR(e6, gt6, (i_j_k_fused + 8) % 16);
TEST_EXPR(e7, gt7, (i_j_k_fused + 1) % 2 * 8);
TEST_EXPR(e8, gt8, i_j_k_fused % 8);
TEST_EXPR(e9, gt9, Expr(0));
TEST_EXPR(e10, gt10, (i_j_k_fused + 1) % 8);
TEST_EXPR(e11, gt11, Expr(0));
TEST_EXPR(e12, gt12, Expr(3));
TEST_EXPR(e13, gt13, Expr(0));
TEST_EXPR(e14, gt14, Expr(0));
}
TEST_F(TestIterSimplify, fuse_not_same_source) {
IterMapRewriter rewriter{{i, j, k, i_j_k_fused}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(
ITER_MARK_SUM(ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i), ir::IndexExpr(32)),
ITER_SPLIT(ITER_MARK_VAR(j), ir::IndexExpr(8)),
ITER_SPLIT(ITER_MARK_VAR(k), ir::IndexExpr(1))),
ir::IndexExpr(64)),
ir::IndexExpr(8),
ir::IndexExpr(8),
ir::IndexExpr(1)));
ir::Expr e1 = (i * 32 + j * 8 + k) / 8;
ir::Expr e2 = (i * 32 + j * 7) / 8;
TEST_EXPR(e1, gt1, (i * 32 + j * 8 + k) / 8);
EXPECT_ANY_THROW(rewriter.Rewrite(&e2));
}
TEST_F(TestIterSimplify, fuse_same_source) {
IterMapRewriter rewriter{{i, j, k, i_j_k_fused}, analyzer};
IterMapToExprNormalizer normalizer{analyzer};
auto gt1 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(32),
ir::IndexExpr(2),
ir::IndexExpr(1)));
auto gt2 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(8),
ir::IndexExpr(4),
ir::IndexExpr(1)));
auto gt3 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(1),
ir::IndexExpr(8),
ir::IndexExpr(1)));
auto gt4 = ITER_SUM(ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(32),
ir::IndexExpr(2),
ir::IndexExpr(1)),
ITER_SPLIT(ITER_MARK_VAR(i_j_k_fused),
ir::IndexExpr(32),
ir::IndexExpr(1),
ir::IndexExpr(1)));
ir::Expr e1 = (i_j_k_fused / 16 / 2 * 32 + i_j_k_fused / 16 % 2 * 16 +
i_j_k_fused % 16) /
8 / 4;
ir::Expr e2 =
(i_j_k_fused / 32 * 32 + i_j_k_fused / 16 % 2 * 16 + i_j_k_fused % 16) /
8 % 4;
ir::Expr e3 =
(i_j_k_fused / 32 * 32 + i_j_k_fused / 16 % 2 * 16 + i_j_k_fused % 16) %
8;
ir::Expr e4 =
((i_j_k_fused / 16) / 2) +
((((i_j_k_fused % 16) / 8) + (2 * ((i_j_k_fused / 16) % 2))) / 4);
ir::Expr e5 = (((i_j_k_fused % 16) / 8) + ((4 * ((i_j_k_fused / 16) / 2)) +
(2 * ((i_j_k_fused / 16) % 2)))) %
4;
ir::Expr e6 = ((i_j_k_fused % 16) + ((32 * ((i_j_k_fused / 16) / 2)) +
(16 * ((i_j_k_fused / 16) % 2)))) %
8;
TEST_EXPR(e1, gt1, i_j_k_fused / 32);
TEST_EXPR(e2, gt2, i_j_k_fused % 32 / 8);
TEST_EXPR(e3, gt3, i_j_k_fused % 8);
TEST_EXPR(e4, gt4, i_j_k_fused / 32);
TEST_EXPR(e5, gt2, i_j_k_fused % 32 / 8);
TEST_EXPR(e6, gt3, i_j_k_fused % 8);
}
TEST_F(TestIterSimplify, SimplifyBindings) {
std::vector<ir::Var> block_vars;
std::vector<ir::Expr> iter_values;
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
// Create block vars and axis vars
for (int i = 0; i < shape.size(); ++i) {
block_vars.push_back(ir::Var(Expr(0),
shape[i],
cinn::UniqName("b" + std::to_string(i)),
false,
false)
.set_index(1));
axis_vars[i]->is_reduce_axis = false;
iter_values.push_back(axis_vars[i]);
}
// Create ScheduleBlock body
ir::Expr body_ = ir::ScheduleBlockRealize::Make(
iter_values,
ir::ScheduleBlock::Make(block_vars, {}, {}, "Test", Expr(0)));
// Create For loops
auto body = body_;
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
// Create outer ScheduleBlockRealize
ir::Expr body_outer = ir::ScheduleBlockRealize::Make(
{}, ir::ScheduleBlock::Make({}, {}, {}, "test1", body));
// Create ir schedule
ir::ModuleExpr mod_expr({ir::Block::Make({body_outer})});
ir::IRSchedule ir_sch(mod_expr);
std::vector<ir::Expr> loops = ir_sch.GetLoops(body_);
// Apply Fuse and Split
ir::Expr loop_fuse = ir_sch.Fuse(loops);
std::vector<ir::Expr> loops_split = ir_sch.Split(loop_fuse, {2, 2, 16});
ir::Expr loop_fuse_2 = ir_sch.Fuse(loops_split);
// Apply SimplifyBindings
SimplifyBlockBinding::SimplifyBindings(loop_fuse_2, {}, analyzer);
// Check result
auto for_op = loop_fuse_2.As<ir::For>();
auto simplified_values = for_op->body.As<ir::Block>()
->stmts[0]
.As<ir::ScheduleBlockRealize>()
->iter_values;
auto f = for_op->loop_var;
EXPECT_EQ(simplified_values[0], f / 32);
EXPECT_EQ(simplified_values[1], f % 32 / 8);
EXPECT_EQ(simplified_values[2], f % 8);
}
TEST_F(TestIterSimplify, MergeMulMod) {
auto S0 = ir::Var(ir::Expr(0), ir::Expr(4), "S0").set_index(1);
auto S1 = ir::Var(ir::Expr(0), ir::Expr(256), "S1").set_index(1);
auto S2 = ir::Var(ir::Expr(0), ir::Expr(13), "S2").set_index(1);
auto e1 = ((((((((S0 * 256) + S1) + (S2 * 1024)) / 2500) * 50) +
(((((S0 * 256) + S1) + (S2 * 1024)) % 2500) / 50)) *
50) +
((((S0 * 256) + S1) + (S2 * 1024)) % 50));
auto e2 = ((((((S0 * 256) + S1) + (S2 * 1024)) / 2500) + -4) * 2500) +
((((S0 * 256) + S1) + (S2 * 1024)) % 2500);
auto e3 = (S1 / 784 * 28 + S1 % 784 / 28) * 28 + S1 % 28;
EXPECT_EQ(MergeMulMod(e1), (((S0 * 256) + S1) + (S2 * 1024)));
EXPECT_EQ(MergeMulMod(e2), ((((S0 * 256) + S1) + (S2 * 1024)) + -10000));
EXPECT_EQ(MergeMulMod(e3), S1);
}
} // namespace common
} // namespace cinn
+119
View File
@@ -0,0 +1,119 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sstream>
#include "paddle/cinn/adt/generate_map_expr.h"
#include "paddle/cinn/adt/map_expr_ctx.h"
#include "paddle/cinn/adt/print.h"
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/manual_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/runtime/flags.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/shape/ir/shape_dialect.h"
#include "paddle/pir/include/dialect/shape/ir/shape_op.h"
#include "paddle/pir/include/dialect/shape/transforms/shape_optimization_pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "test/cpp/pir/tools/test_pir_utils.h"
PD_DECLARE_bool(cinn_enable_map_expr);
PD_DECLARE_bool(cinn_enable_map_expr_dynamic_shape);
PD_DECLARE_bool(cinn_enable_map_expr_index_detail);
namespace {
std::string Trim(const std::string& doc) {
std::stringstream oss{doc};
std::stringstream ret{};
std::string str;
while (oss >> str) {
std::size_t size = str.size();
for (; size > 0 && str[size - 1] == ' '; --size) {
}
ret << str.substr(0, size) << std::endl;
}
return ret.str();
}
} // namespace
TEST(MapExpr, ElementWise_Fusion_0) {
cinn::adt::UniqueId::ResetSeqNumber(0);
::pir::IrContext* ctx = ::pir::IrContext::Instance();
::pir::Program program(ctx);
ctx->GetOrRegisterDialect<pir::shape::ShapeDialect>();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
phi::DDim dims_D_2 = {-1, 1};
pir::Value value1 =
test::CreateDenseTensorOp(ctx, dims_D_2, {"op1_attr"}, {"op1_name"})
->result(0);
pir::Value value2 =
test::CreateDenseTensorOp(ctx, dims_D_2, {"op2_attr"}, {"op2_name"})
->result(0);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
builder.Build<paddle::dialect::SubtractOp>(
value1, builder.Build<paddle::dialect::ExpOp>(value2).result(0));
::pir::PassManager pass_manager(ctx);
// TODO(@jiahy0825): use CreateShapeOptimizationPass() instead of
// CreateInferSymbolicShapePass() which is a fake pass
/*
pass_manager.AddPass(::pir::CreateInferSymbolicShapePass(shape_analysis));
pass_manager.Run(&program);
std::vector<pir::Operation*> vec_op;
for (auto& op : *program.block()) {
vec_op.push_back(&op);
}
auto res = cinn::dialect::ir::OpFusionPassInternal(vec_op);
ASSERT_EQ(res.size(), 1u);
ASSERT_EQ(res[0]->ops.size(), program.block()->size());
auto group_list = cinn::dialect::ir::GeneralFusionMergePassInternal(res);
ASSERT_EQ(group_list.size(), 1u);
FLAGS_cinn_enable_map_expr = true;
FLAGS_cinn_enable_map_expr_dynamic_shape = true;
FLAGS_cinn_enable_map_expr_index_detail = true;
auto group = group_list.at(0);
group->shape_analysis = shape_analysis;
cinn::adt::TryGenerateMapExprFromGroup(group);
std::string map_expr_str =
cinn::adt::ToTxtString(group->map_expr_ctx().map_expr(), "MapExprTest");
std::string target_str = R"TEST(
MapExprTest(t_var_2, t_var_1) {
AnchoredMapStmt(t_var_0) {
MapStmt([i_59, i_60]) {
exp(
&t_var[IndexDot([BI(i_59, sym_17), 0], [sym_17, 1])],
t_var_1[IndexDot([BI(i_59, sym_17), 0], [sym_17, 1])]);
subtract(
&t_var_0[IndexDot([i_59, i_60], [sym_17, 1])],
t_var_2[IndexDot([BI(i_59, sym_17), 0], [sym_17, 1])],
t_var[IndexDot([BI(i_59, sym_17), 0], [sym_17, 1])]);
}
}
}
)TEST";
ASSERT_EQ(Trim(map_expr_str), Trim(target_str));
*/
}
@@ -0,0 +1,144 @@
// Copyright (c) 2024 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sstream>
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/optim/ir_simplify.h"
#include "paddle/cinn/optim/merge_block_utils.h"
namespace cinn {
namespace optim {
namespace {
bool IsBlockForAllEqual(const ForTreeNode& first, const ForTreeNode& second) {
auto ForVarExtentEqual = [&](const ForTreeNode& first,
const ForTreeNode& second) -> bool {
const ir::Expr lhs = first.val->extent();
const ir::Expr rhs = second.val->extent();
if (lhs != rhs) {
return false;
}
return true;
};
if (!ForVarExtentEqual(first, second)) return false;
if (first.children.size() != second.children.size()) return false;
for (size_t i = 0; i < first.children.size(); ++i) {
if (!IsBlockForAllEqual(first.children[i], second.children[i])) {
return false;
}
}
return true;
}
ir::stmt::For MakeForLoops(const std::vector<int> extents, int index) {
ir::stmt::StmtRef body_stmt;
if (index == extents.size() - 1) {
body_stmt = ir::stmt::Schedule(
std::vector<Var>(),
std::vector<Expr>(),
std::vector<Expr>(),
std::vector<Expr>(),
"block",
ir::stmt::BlockRef(std::vector<ir::stmt::StmtRef>()));
} else {
body_stmt = MakeForLoops(extents, index + 1);
}
std::vector<ir::stmt::StmtRef> body = {body_stmt};
return ir::stmt::For(ir::Var("i"),
ir::Expr(0),
ir::Expr(extents[index]),
ir::ForType::Serial,
ir::DeviceAPI::CUDA,
ir::stmt::BlockRef(body),
ir::VectorizeInfo(),
ir::BindInfo());
}
void TestHelper(const std::vector<int>& extents1,
const std::vector<int>& extents2,
bool is_same) {
auto for_loop1 = MakeForLoops(extents1, 0);
auto for_loop2 = MakeForLoops(extents2, 0);
if (is_same) {
EXPECT_TRUE(CanMergeBlocks(for_loop1, for_loop2, IsBlockForAllEqual));
} else {
EXPECT_FALSE(CanMergeBlocks(for_loop1, for_loop2, IsBlockForAllEqual));
}
}
void TestHelper2(const std::vector<std::vector<int>>& extents1,
const std::vector<std::vector<int>>& extents2,
bool is_same) {
auto MakeNestLoops =
[&](const std::vector<std::vector<int>>& extents) -> ir::stmt::For {
std::vector<ir::stmt::StmtRef> for_loops;
for (size_t i = 0; i < extents.size(); ++i) {
for_loops.push_back(MakeForLoops(extents[i], 0));
}
ir::stmt::BlockRef block(for_loops);
ir::stmt::For for_stmt = ir::stmt::For(ir::Var("i"),
ir::Expr(0),
ir::Expr(1),
ir::ForType::Serial,
ir::DeviceAPI::CUDA,
block,
ir::VectorizeInfo(),
ir::BindInfo());
return for_stmt;
};
auto for_stmt1 = MakeNestLoops(extents1);
auto for_stmt2 = MakeNestLoops(extents2);
if (is_same) {
EXPECT_TRUE(CanMergeBlocks(for_stmt1, for_stmt2, IsBlockForAllEqual));
} else {
EXPECT_FALSE(CanMergeBlocks(for_stmt1, for_stmt2, IsBlockForAllEqual));
}
}
TEST(ForInfo, ForInfoEqual) {
TestHelper({10}, {10}, true);
TestHelper({10, 5}, {10, 5}, true);
TestHelper({10, 5, 3}, {10, 5, 3}, true);
TestHelper2({{10}, {10}}, {{10}, {10}}, true);
TestHelper2({{10, 5}, {4, 7}}, {{10, 5}, {4, 7}}, true);
TestHelper2(
{{10, 5, 3}, {4, 7, 9}, {2, 8}}, {{10, 5, 3}, {4, 7, 9}, {2, 8}}, true);
}
TEST(ForInfo, ForInfoNotEqual) {
TestHelper({10}, {9}, false);
TestHelper({10, 5}, {10, 4}, false);
TestHelper({10, 5, 3}, {10, 5, 2}, false);
TestHelper2({{10}, {10}}, {{10}, {9}}, false);
TestHelper2({{10, 5}, {4, 7}}, {{10, 5}, {4, 3}}, false);
TestHelper2(
{{10, 5, 3}, {4, 7, 9}, {2, 8}}, {{10, 5, 3}, {4, 7, 9}, {2, 7}}, false);
}
} // namespace
} // namespace optim
} // namespace cinn
+121
View File
@@ -0,0 +1,121 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_attribute.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/framework/pir/compilation_task.h"
#include "paddle/cinn/hlir/framework/pir/utils.h"
#include "paddle/cinn/hlir/framework/pir_compiler.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
using cinn::hlir::framework::pir::CompatibleInfo;
using cinn::hlir::framework::pir::OpLoweringGroup;
using cinn::hlir::framework::pir::OpLoweringGroupPtr;
using ProgramInfo = std::tuple<std::shared_ptr<::pir::Program>,
std::vector<OpLoweringGroupPtr>>;
ProgramInfo BuildProgram(std::vector<int64_t> input_shape) {
::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());
const float value_one = 1.0;
auto full_op_x = builder.Build<paddle::dialect::FullOp>(
input_shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace());
std::vector<OpLoweringGroupPtr> groups;
const std::string fn_name = CompatibleInfo::GroupOpsName(
std::initializer_list<::pir::Operation*>({full_op_x.operation()}));
groups.emplace_back(std::make_shared<OpLoweringGroup>(
std::initializer_list<::pir::Operation*>({full_op_x.operation()}),
fn_name));
groups.back()->mut_output_ops().insert(full_op_x.operation());
return {program, groups};
}
// TODO(LiuYang): This test is temporarily
// TEST(CompilationTask, Basic) {
// auto prog_info = BuildProgram({4096, 128});
// std::shared_ptr<::pir::Program> program = std::get<0>(prog_info);
// LOG(INFO) << program->block()->size();
// EXPECT_EQ(program->block()->size(), 1u);
// std::stringstream ss;
// program->Print(ss);
// LOG(INFO) << ss.str();
// auto target = cinn::common::DefaultNVGPUTarget();
// auto scope = std::make_shared<cinn::hlir::framework::Scope>();
// std::vector<GroupPtr> groups = std::get<1>(prog_info);
// CHECK_EQ(groups.size(), 1);
// cinn::hlir::framework::GroupCompilationContext compilation_context(
// target, groups[0], scope);
// cinn::hlir::framework::CompilationTask
// compilation_task(&compilation_context); compilation_task.Lowering();
// LOG(INFO) << compilation_context.PrintPredicate2Funcs();
// compilation_task.CodegenAndJit();
// auto instruction = compilation_task.BuildInstruction();
// }
// TEST(CompilationTask, CompileGroup) {
// // Step 1: Construct pir::Program
// int M = 4096, N = 128;
// auto prog_info = BuildProgram({M, N});
// std::shared_ptr<::pir::Program> program = std::get<0>(prog_info);
// LOG(INFO) << program->block()->size();
// EXPECT_EQ(program->block()->size(), 1u);
// std::stringstream ss;
// program->Print(ss);
// LOG(INFO) << ss.str();
// auto target = cinn::common::DefaultNVGPUTarget();
// auto scope = std::make_shared<cinn::hlir::framework::Scope>();
// std::vector<GroupPtr> groups = std::get<1>(prog_info);
// CHECK_EQ(groups.size(), 1);
// cinn::hlir::framework::PirCompiler ir_compiler(*program, target, scope);
// auto runtime_program = ir_compiler.Build(groups);
// // Step 3: Execute Runtime Instruction and check Scope.
// ASSERT_NO_THROW(runtime_program->Execute());
// for (auto& var_name : scope->var_names()) {
// std::string name = {var_name.begin(), var_name.end()};
// int64_t numel = scope->GetTensor(name)->shape().numel();
// ASSERT_EQ(numel, M * N);
// std::vector<float> data =
// cinn::GetTensorData<float>(scope->GetTensor(name), target);
// for (int i = 0; i < numel; ++i) {
// ASSERT_EQ(data[i], 1.0);
// }
// }
// }
+137
View File
@@ -0,0 +1,137 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/pd_to_cinn_pass.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/drr/include/drr_pattern_base.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
auto sum_op =
builder.Build<paddle::dialect::SumOp>(full_input_op.result(0),
std::vector<int64_t>({-1}),
phi::DataType::FLOAT32,
true);
auto relu_op = builder.Build<paddle::dialect::ReluOp>(sum_op.result(0));
auto exp_op = builder.Build<paddle::dialect::ExpOp>(sum_op.result(0));
}
void BuildProgramMax(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp full_input_op =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{4, 3, 16},
1.5,
phi::DataType::FLOAT32,
phi::CPUPlace());
auto max_op = builder.Build<paddle::dialect::MaxOp>(
full_input_op.result(0), std::vector<int64_t>({-1}), true);
auto relu_op = builder.Build<paddle::dialect::ReluOp>(max_op.result(0));
auto exp_op = builder.Build<paddle::dialect::ExpOp>(max_op.result(0));
}
TEST(DrrTest, reduce_sum) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreatePdOpToCinnOpPass());
pm.Run(&program);
auto it = program.block()->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::FullOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<cinn::dialect::ReduceSumOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"cinn::dialect::ReduceSumOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::ReluOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::ReluOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::ExpOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::ExpOp, but it is not."));
}
TEST(DrrTest, reduce_max) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgramMax(builder);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreatePdOpToCinnOpPass());
pm.Run(&program);
auto it = program.block()->begin();
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::FullOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::FullOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<cinn::dialect::ReduceMaxOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"cinn::dialect::ReduceMaxOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::ReluOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::ReluOp, but it is not."));
it++;
PADDLE_ENFORCE_EQ(it->isa<paddle::dialect::ExpOp>(),
true,
common::errors::InvalidArgument(
"The operation should be of type "
"paddle::dialect::ExpOp, but it is not."));
}
@@ -0,0 +1,192 @@
// Copyright (c) 2025 CINN 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/cinn/optim/eliminate_common_factor_of_local_index.h"
#include <gtest/gtest.h>
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/utils/ir_nodes_collector.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace optim {
/*
{
serial for (i_0, 0, 32) {
serial for (j_0, 0, 4) {
var_local[((i_0 * 3) + j_0), ((j_0 * 32) / 128)] =
var_global_in[i_0, ((j_0 * 32) / 128)]
var_global_out[((i_0 * 3) + j_0), j_0] =
var_local[((i_0 * 3) + j_0), ((j_0 * 32) / 128)]
}
}
}
*/
TEST(EliminateCommonFactorOfLocalIndex, SimplifyLocalIndex) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape = {ir::Expr(128), ir::Expr(128)};
// const std::vector<ir::Expr> indices = {ir::Expr(0)};
ir::Tensor var_global_in =
ir::_Tensor_::Make("var_global_in", ir::Float(32), shape, shape);
var_global_in->WithBuffer("global", "var_global_in_buffer");
ir::Tensor var_local_tensor =
ir::_Tensor_::Make("var_local", ir::Float(32), shape, shape);
var_local_tensor->WithBuffer("local", "var_local_buffer");
ir::Tensor var_global_out =
ir::_Tensor_::Make("var_global_out", ir::Float(32), shape, shape);
var_global_out->WithBuffer("global", "var_global_out_buffer");
ir::Var var_i_0 = ir::Var(ir::Expr(0), ir::Expr(32), "i_0");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(4), "j_0");
std::vector<ir::Expr> block_contents = {
ir::Store::Make(
var_local_tensor,
ir::Load::Make(var_global_in, {var_i_0, (var_j_0 * 32) / 128}),
{var_i_0 * 3 + var_j_0, (var_j_0 * 32) / 128}),
ir::Store::Make(
var_global_out,
ir::Load::Make(var_local_tensor,
{var_i_0 * 3 + var_j_0, (var_j_0 * 32) / 128}),
{var_i_0 * 3 + var_j_0, var_j_0})};
ir::Expr inner_loop_body = ir::Block::Make(block_contents);
std::vector<ir::Expr> j_loop_contents = {inner_loop_body};
ir::Expr j_loop = ir::For::Make(var_j_0,
ir::Expr(0),
ir::Expr(4),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make(j_loop_contents));
std::vector<ir::Expr> i_loop_contents = {j_loop};
ir::Expr loop_body = ir::For::Make(var_i_0,
ir::Expr(0),
ir::Expr(32),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make(i_loop_contents));
std::vector<ir::Expr> ij_loop_contents = {loop_body};
ir::Expr expr = ir::Block::Make(ij_loop_contents);
ir::stmt::BlockRef block = ir::ConvertExprBlockToStmtBlock(expr);
VLOG(6) << "Before EliminateCommonFactorOfLocalIndex: " << block;
EliminateCommonFactorOfLocalIndex(block);
VLOG(6) << "After EliminateCommonFactorOfLocalIndex: " << block;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i_0, 0, 32) {
serial for (j_0, 0, 4) {
var_local[i_0, j_0] = var_global_in[i_0, ((j_0 * 32) / 128)]
var_global_out[((i_0 * 3) + j_0), j_0] = var_local[i_0, j_0]
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(block), utils::Trim(expected_ir));
}
/*
{
serial for (i_0, 0, 32) {
serial for (j_0, 0, 4) {
var_local[(i_0 * 3), ((j_0 * 32) / 128)] =
var_global_in[i_0, ((j_0 * 32) / 128)]
var_global_out[((i_0 * 3) + j_0), j_0] =
var_local[(i_0 * 3), ((j_0 * 32) / 128)]
}
}
}
*/
TEST(EliminateCommonFactorOfLocalIndex, SimplifyLocalIndexWithZeroIndex) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape = {ir::Expr(128), ir::Expr(128)};
// const std::vector<ir::Expr> indices = {ir::Expr(0)};
ir::Tensor var_global_in =
ir::_Tensor_::Make("var_global_in", ir::Float(32), shape, shape);
var_global_in->WithBuffer("global", "var_global_in_buffer");
ir::Tensor var_local_tensor =
ir::_Tensor_::Make("var_local", ir::Float(32), shape, shape);
var_local_tensor->WithBuffer("local", "var_local_buffer");
ir::Tensor var_global_out =
ir::_Tensor_::Make("var_global_out", ir::Float(32), shape, shape);
var_global_out->WithBuffer("global", "var_global_out_buffer");
ir::Var var_i_0 = ir::Var(ir::Expr(0), ir::Expr(32), "i_0");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(4), "j_0");
std::vector<ir::Expr> block_contents = {
ir::Store::Make(
var_local_tensor,
ir::Load::Make(var_global_in, {var_i_0, var_j_0 * 32 / 128}),
{var_i_0 * 3, var_j_0 * 32 / 128}),
ir::Store::Make(
var_global_out,
ir::Load::Make(var_local_tensor, {var_i_0 * 3, var_j_0 * 32 / 128}),
{var_i_0 * 3 + var_j_0, var_j_0})};
ir::Expr inner_loop_body = ir::Block::Make(block_contents);
std::vector<ir::Expr> j_loop_contents = {inner_loop_body};
ir::Expr j_loop = ir::For::Make(var_j_0,
ir::Expr(0),
ir::Expr(4),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make(j_loop_contents));
std::vector<ir::Expr> i_loop_contents = {j_loop};
ir::Expr loop_body = ir::For::Make(var_i_0,
ir::Expr(0),
ir::Expr(32),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make(i_loop_contents));
std::vector<ir::Expr> ij_loop_contents = {loop_body};
ir::Expr expr = ir::Block::Make(ij_loop_contents);
ir::stmt::BlockRef block = ir::ConvertExprBlockToStmtBlock(expr);
VLOG(6) << "Before EliminateCommonFactorOfLocalIndex: " << block;
EliminateCommonFactorOfLocalIndex(block);
VLOG(6) << "After EliminateCommonFactorOfLocalIndex: " << block;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i_0, 0, 32) {
serial for (j_0, 0, 4) {
var_local[0, i_0] = var_global_in[i_0, ((j_0 * 32) / 128)]
var_global_out[((i_0 * 3) + j_0), j_0] = var_local[0, i_0]
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(block), utils::Trim(expected_ir));
}
} // namespace optim
} // namespace cinn
+170
View File
@@ -0,0 +1,170 @@
// Copyright (c) 2024 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/ir/group_schedule/config/database.h"
#include "paddle/cinn/ir/group_schedule/config/file_database.h"
#include "paddle/cinn/ir/group_schedule/config/group_tile_config.h"
#include "paddle/cinn/ir/group_schedule/search/config_searcher.h"
#include "paddle/cinn/ir/group_schedule/search/measurer.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/performance_statistician.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
COMMON_DECLARE_bool(print_ir);
PD_DECLARE_string(cinn_tile_config_filename_label);
#define MKDIR(path) mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
bool PathExists(const std::string& path) {
struct stat statbuf;
if (stat(path.c_str(), &statbuf) != -1) {
if (S_ISDIR(statbuf.st_mode)) {
return true;
}
}
return false;
}
void RemoveDir(const cinn::common::Target target,
const cinn::ir::IterSpaceType& iter_space_type) {
std::string dirname = "";
std::string filename = "";
for (auto i : iter_space_type) {
dirname += i.first;
dirname += "_";
filename += i.first + i.second;
filename += "_";
}
const std::string kDirSuffix = "_EREBE";
dirname = dirname.substr(0, dirname.size() - 1) + kDirSuffix;
filename = filename.substr(0, filename.size() - 1);
auto removedir = [](const std::string& test_path,
const std::string& file_name) {
if (PathExists(test_path)) {
std::string full_test_name = test_path + file_name;
std::remove(full_test_name.c_str());
LOG(INFO) << "File exist.";
} else {
LOG(INFO) << "File doesn't exist.";
}
};
std::string root_path = FLAGS_cinn_tile_config_filename_label;
const std::string kTestFileDir = "./tile_file_test/";
if (root_path == "") {
root_path = kTestFileDir;
}
std::string target_str = target.arch_str() + "_" + target.device_name_str();
std::string file_name = "/" + filename + ".json";
removedir(root_path + target_str + "/" + dirname, file_name);
LOG(INFO) << "Dump_path "
<< root_path + target_str + "/" + dirname + " has been removed";
}
TEST(ConfigSearcher, TestReduceDemo) {
constexpr int kThreadsPerWarp = 32;
constexpr int kMaxThreadsPerBlock = 1024;
// Step 1: Construct iter space and tile config.
cinn::ir::BucketInfo bucket_info;
int s_dimension_lower = 13;
int s_dimension_upper = 13;
auto s_dimension_type = "S";
auto s_dimension_is_dynamic = true;
int r_dimension_lower = 4096;
int r_dimension_upper = 4096;
auto r_dimension_type = "R";
auto r_dimension_is_dynamic = true;
bucket_info.space.push_back(
cinn::ir::BucketInfo::Dimension{s_dimension_lower,
s_dimension_upper,
s_dimension_type,
s_dimension_is_dynamic});
bucket_info.space.push_back(
cinn::ir::BucketInfo::Dimension{r_dimension_lower,
r_dimension_upper,
r_dimension_type,
r_dimension_is_dynamic});
cinn::ir::ScheduleConfig::TileConfig tile_config;
tile_config.spatial_inner_num = 9;
tile_config.warp_num = 14;
tile_config.tree_reduce_num = 512;
// Use kTestFileDir in this test.
const std::string prev_flag = FLAGS_cinn_tile_config_filename_label;
const std::string kTestFileDir = "./tile_file_test/";
FLAGS_cinn_tile_config_filename_label = kTestFileDir;
std::vector<std::pair<std::string, std::string>> iter_space_type = {
std::make_pair(s_dimension_type,
s_dimension_is_dynamic == true ? "dynamic" : "static"),
std::make_pair(r_dimension_type,
r_dimension_is_dynamic == true ? "dynamic" : "static")};
// If test file has been created, remove it.
RemoveDir(cinn::common::DefaultTarget(), iter_space_type);
// Step 2: Add to json / Read from json
cinn::ir::FileTileConfigDatabase file_database;
file_database.AddConfig(
cinn::common::DefaultTarget(), bucket_info, tile_config, 2);
cinn::ir::TileConfigMap tile_config_map =
file_database.GetConfigs(cinn::common::DefaultTarget(), iter_space_type);
// Delete the file
RemoveDir(cinn::common::DefaultTarget(), iter_space_type);
// Check the correctness
for (auto& it : tile_config_map) {
LOG(INFO) << "bucket info is: ";
auto dims = it.first.space.size();
for (int i = 0; i < dims; i++) {
LOG(INFO) << "Dimension " << i
<< " 's lower_bound is: " << it.first.space[i].lower_bound;
LOG(INFO) << "Dimension " << i
<< " 's upper_bound is: " << it.first.space[i].upper_bound;
auto dimension_lower = i == 0 ? s_dimension_lower : r_dimension_lower;
auto dimension_upper = i == 0 ? s_dimension_upper : r_dimension_upper;
PADDLE_ENFORCE_EQ(it.first.space[i].lower_bound,
dimension_lower,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong dimension_lower"));
PADDLE_ENFORCE_EQ(it.first.space[i].upper_bound,
dimension_upper,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong dimension_upper"));
}
LOG(INFO) << "tile config is " << it.second.spatial_inner_num << " "
<< it.second.warp_num << " " << it.second.tree_reduce_num;
PADDLE_ENFORCE_EQ(it.second.spatial_inner_num,
tile_config.spatial_inner_num,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong spatial_inner_num"));
PADDLE_ENFORCE_EQ(it.second.warp_num,
tile_config.warp_num,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong warp_num"));
PADDLE_ENFORCE_EQ(it.second.tree_reduce_num,
tile_config.tree_reduce_num,
::common::errors::InvalidArgument(
"GetConfigs function gets wrong tree_reduce_num"));
}
// Restore the previous flag
FLAGS_cinn_tile_config_filename_label = prev_flag;
}
@@ -0,0 +1,93 @@
// 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 "gtest/gtest.h"
#include "paddle/cinn/hlir/dialect/operator/ir/generate_shape_util.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_builder.h"
#include "test/cpp/pir/tools/test_pir_utils.h"
namespace cinn::dialect {
using namespace symbol; // NOLINT
namespace {
DimExpr CreateExampleDimExpr() {
DimExprBuilder dim_expr_builder;
DimExpr sym0 = DimExpr("S0");
DimExpr sym1 = DimExpr("S1");
DimExpr constant = DimExpr(2);
DimExpr expr1 = (sym0 - sym1) * constant / sym0;
DimExpr expr2 = dim_expr_builder.Max(expr1, sym0);
DimExpr output = dim_expr_builder.Min(expr2, sym1);
return output;
}
} // namespace
TEST(DimExprUtil, Convert) {
pir::IrContext* ctx = pir::IrContext::Instance();
DimExpr dim_expr = CreateExampleDimExpr();
::pir::Attribute attr = ConvertDimExprToAttribute(ctx, dim_expr);
std::optional<DimExpr> opt_expr = ConvertAttributeToDimExpr(attr);
ASSERT_TRUE(opt_expr.has_value());
ASSERT_EQ(opt_expr.value(), dim_expr);
}
TEST(DimExprUtil, Substitute) {
DimExpr dim_expr = CreateExampleDimExpr();
const auto& substituted_expr = SubstituteDimExpr(
dim_expr, [](const std::string& str) -> std::optional<DimExpr> {
if (str == "S0") {
return DimExpr("symbol0");
} else if (str == "S1") {
return DimExpr("symbol1");
} else {
return std::nullopt;
}
});
const auto& ret_expr = SubstituteDimExpr(
substituted_expr, [](const std::string& str) -> std::optional<DimExpr> {
if (str == "symbol0") {
return DimExpr("S0");
} else if (str == "symbol1") {
return DimExpr("S1");
} else {
return std::nullopt;
}
});
ASSERT_EQ(ret_expr, dim_expr);
}
TEST(DimExprUtil, MakeGetterDimExpr4SymbolName) {
cinn::dialect::GenerateShapeOp::SymbolBindings symbol_bindings{};
using ShapeSymbolBinding = cinn::dialect::GenerateShapeOp::ShapeSymbolBinding;
symbol_bindings.emplace_back(ShapeSymbolBinding{"Symbol", 0, 0});
const auto& dim_expr = CreateExampleDimExpr();
const auto& shape_or_data_dim_exprs = symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({dim_expr}));
const auto& DimExpr4SymbolName = MakeGetterDimExpr4SymbolName(
symbol_bindings,
[&](int in_tensor_idx) -> const symbol::ShapeOrDataDimExprs& {
return shape_or_data_dim_exprs;
});
const auto& opt_dim_expr = DimExpr4SymbolName("Symbol");
ASSERT_TRUE(opt_dim_expr.has_value());
ASSERT_EQ(opt_dim_expr.value(), dim_expr);
}
} // namespace cinn::dialect
+197
View File
@@ -0,0 +1,197 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/manual_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_attribute.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/cinn_group_cluster_pass.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/build_cinn_pass.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
std::vector<pir::Value> BuildInput(
::pir::Builder* builder,
const std::vector<std::vector<int64_t>>& vec_shapes) {
std::vector<pir::Value> vec_res;
for (size_t i = 0; i < vec_shapes.size(); ++i) {
auto op = builder->Build<paddle::dialect::FullOp>(
vec_shapes[i], 1.0, phi::DataType::FLOAT32, phi::CPUPlace());
vec_res.push_back(op.result(0));
}
return vec_res;
}
TEST(IROpFusionPass, ElementWise_Fusion_0) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
::pir::Program program(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
int h = 32, w = 32;
auto inputs = BuildInput(&builder, {{h, w}, {h, w}, {h, w}});
auto e =
builder.Build<paddle::dialect::AddOp>(inputs[0], inputs[1]).result(0);
auto f = builder.Build<paddle::dialect::AddOp>(e, inputs[2]).result(0);
auto out1 = builder.Build<paddle::dialect::AddOp>(f, inputs[2]).result(0);
builder.Build<paddle::dialect::FetchOp>(out1, "out1", 0);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Fatal("Pass manager run failed."));
PADDLE_ENFORCE_EQ(program.block()->size(),
2u,
common::errors::PreconditionNotMet(
"The number of FusionOp does not meet expectations。"));
}
// Real test 0
TEST(IROpFusionPass, Broadcast_Test_0) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
::pir::Program program(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
int h = 32, w = 32;
auto inputs = BuildInput(&builder, {{w}, {w}, {h, w}, {h, w}});
auto e =
builder.Build<paddle::dialect::AddOp>(inputs[0], inputs[1]).result(0);
auto f =
builder.Build<paddle::dialect::AddOp>(inputs[2], inputs[3]).result(0);
std::vector<int64_t> axes{1};
std::vector<int64_t> out_shape{h, w};
auto e1 =
builder.Build<cinn::dialect::BroadcastOp>(e, axes, out_shape).result(0);
auto out1 = builder.Build<paddle::dialect::AddOp>(e1, f).result(0);
builder.Build<paddle::dialect::FetchOp>(out1, "out1", 0);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Fatal("Pass manager run failed."));
PADDLE_ENFORCE_EQ(program.block()->size(),
2u,
common::errors::PreconditionNotMet(
"The number of FusionOp does not meet expectations。"));
}
// Real test 1
TEST(IROpFusionPass, Broadcast_Test_1) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
::pir::Program program(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
int h = 32, w = 32;
auto inputs = BuildInput(&builder, {{w}, {w}, {w}, {h, w}});
auto e =
builder.Build<paddle::dialect::AddOp>(inputs[0], inputs[1]).result(0);
auto out1 = builder.Build<paddle::dialect::AddOp>(inputs[2], e).result(0);
std::vector<int64_t> axes{1};
std::vector<int64_t> out_shape{h, w};
auto e1 =
builder.Build<cinn::dialect::BroadcastOp>(e, axes, out_shape).result(0);
auto out2 = builder.Build<paddle::dialect::AddOp>(inputs[3], e1).result(0);
builder.Build<paddle::dialect::FetchOp>(out1, "out1", 0);
builder.Build<paddle::dialect::FetchOp>(out2, "out2", 1);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Fatal("Pass manager run failed."));
PADDLE_ENFORCE_EQ(program.block()->size(),
3u,
common::errors::PreconditionNotMet(
"The number of FusionOp does not meet expectations。"));
}
TEST(IROpFusionPass, softmax) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
::pir::Program program(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program.block());
auto inputs = BuildInput(&builder, {{128, 128, 768}});
std::vector<int64_t> axes{-1};
auto x = inputs[0];
auto max = builder.Build<cinn::dialect::ReduceMaxOp>(x, axes, true).result(0);
auto broadcast_1 = builder
.Build<cinn::dialect::BroadcastOp>(
max,
std::vector<int64_t>({0, 1, 2}),
std::vector<int64_t>({128, 128, 768}))
.result(0);
auto sub =
builder.Build<paddle::dialect::SubtractOp>(x, broadcast_1).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder.Build<cinn::dialect::ReduceSumOp>(exp, axes, true).result(0);
auto broadcast_2 = builder
.Build<cinn::dialect::BroadcastOp>(
sum,
std::vector<int64_t>({0, 1, 2}),
std::vector<int64_t>({128, 128, 768}))
.result(0);
auto divide =
builder.Build<paddle::dialect::DivideOp>(exp, broadcast_2).result(0);
builder.Build<paddle::dialect::FetchOp>(divide, "out1", 0);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateBuildCinnPass());
pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
PADDLE_ENFORCE_EQ(pm.Run(&program),
true,
common::errors::Fatal("Pass manager run failed."));
PADDLE_ENFORCE_EQ(program.block()->size(),
2u,
common::errors::PreconditionNotMet(
"The number of FusionOp does not meet expectations。"));
}
+191
View File
@@ -0,0 +1,191 @@
// Copyright (c) 2025 CINN 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/cinn/optim/ir_simplify.h"
#include <gtest/gtest.h>
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_base.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/utils/ir_nodes_collector.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace optim {
/*
i_j_fused: [0ll, 524288ll)
j_0: [0, 128)
Before Normalize:
(j_0 % 128)
After Normalize:
j_0
*/
TEST(IRSimplifyBound, SimplifyMod) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variable
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Final expression
ir::Expr expr = ir::Mod::Make(var_j_0, ir::Expr(128));
VLOG(6) << "Before Simplify: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Simplify: " << res;
// Expected output verification
std::string expected_ir = R"ROC(j_0)ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
/*
i_j_fused: [0ll, 524288ll)
j_0: [0, 128)
Before Normalize:
(j_0 / 128)
After Normalize:
0
*/
TEST(IRSimplifyBound, SimplifyDiv) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variable
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Final expression
ir::Expr expr = ir::Div::Make(var_j_0, ir::Expr(128));
VLOG(6) << "Before Normalize: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Normalize: " << res;
// Expected output verification
std::string expected_ir = R"ROC(0)ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
/*
i_j_fused: [0ll, 524288ll)
j_0: [0, 128)
Before Normalize:
((((i_j_fused % 16) * 128) + j_0) / 128)
After Normalize:
(i_j_fused % 16)
*/
TEST(IRSimplifyBound, SimplifyLinearDiv) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variables
ir::Var var_i_j_fused = ir::Var(ir::Expr(0), ir::Expr(524288), "i_j_fused");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Final expression
ir::Expr expr = ir::Div::Make(
ir::Add::Make(ir::Mul::Make(ir::Mod::Make(var_i_j_fused, ir::Expr(16)),
ir::Expr(128)),
var_j_0),
ir::Expr(128));
VLOG(6) << "Before Normalize: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Normalize: " << res;
// Expected output verification
std::string expected_ir = R"ROC((i_j_fused % 16))ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
/*
i_j_fused: [0ll, 524288ll)
j_0: [0, 128)
Before Normalize:
((((i_j_fused % 16) * 128) + j_0) % 128)
After Normalize:
j_0
*/
TEST(IRSimplifyBound, SimplifyLinearMod) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variables
ir::Var var_i_j_fused = ir::Var(ir::Expr(0), ir::Expr(524288), "i_j_fused");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Final expression
ir::Expr expr = ir::Mod::Make(
ir::Add::Make(ir::Mul::Make(ir::Mod::Make(var_i_j_fused, ir::Expr(16)),
ir::Expr(128)),
var_j_0),
ir::Expr(128));
VLOG(6) << "Before Normalize: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Normalize: " << res;
// Expected output verification
std::string expected_ir = R"ROC(j_0)ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
/*
loop_var_2: [0, 32)
loop_var_3: [0, 4)
Before Normalize:
(((loop_var_3 * 32ll) + loop_var_2) / 128ll)
After Normalize:
0
*/
TEST(IRSimplifyBound, SimplifyLinearDiv2) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
// Define loop variables
ir::Var loop_var_2 = ir::Var(ir::Expr(0), ir::Expr(32), "loop_var_2");
ir::Var loop_var_3 = ir::Var(ir::Expr(0), ir::Expr(4), "loop_var_3");
// Final expression
ir::Expr expr = ir::Div::Make(
ir::Add::Make(ir::Mul::Make(loop_var_3, ir::Expr(32)), loop_var_2),
ir::Expr(128));
VLOG(6) << "Before Normalize: " << expr;
auto res = expr.as_index().ir::IndexExpr::Normalize(
ir::IndexExpr::OptLevel::kLevel3);
VLOG(6) << "After Normalize: " << res;
// Expected output verification
std::string expected_ir = R"ROC(0)ROC";
EXPECT_EQ(utils::GetStreamCnt(res), utils::Trim(expected_ir));
}
} // namespace optim
} // namespace cinn
@@ -0,0 +1,336 @@
// Copyright (c) 2025 CINN 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/cinn/optim/ir_simplify.h"
#include <gtest/gtest.h>
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/utils/ir_nodes_collector.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace optim {
/*
serial for (i, 0ll, 32768ll) {
serial for (j, 0ll, 16ll) {
serial for (reduce_k_0, 0ll, 128ll) {
var_18[i, j] = select((var_18[i, j] > var_17[i, j, reduce_k_0]),
var_18[i, j], var_17[i, j, reduce_k_0])
}
}
}
}
*/
TEST(IRSimplifySelect, SimplifySelectToMax) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape_2d = {ir::Expr(32768), ir::Expr(16)};
const std::vector<ir::Expr> shape_3d = {
ir::Expr(32768), ir::Expr(16), ir::Expr(128)};
ir::Tensor var_17 =
ir::_Tensor_::Make("var_17", ir::Float(32), shape_3d, shape_3d);
var_17->WithBuffer("global", "var_17_buffer");
ir::Tensor var_18 =
ir::_Tensor_::Make("var_18", ir::Float(32), shape_2d, shape_2d);
var_18->WithBuffer("global", "var_18_buffer");
// Define loop variables
ir::Var var_i = ir::Var(ir::Expr(0), ir::Expr(32768), "i");
ir::Var var_j = ir::Var(ir::Expr(0), ir::Expr(16), "j");
ir::Var var_reduce_k_0 = ir::Var(ir::Expr(0), ir::Expr(128), "reduce_k_0");
// Create innermost reduction loop body
ir::Expr reduce_body = ir::Store::Make(
var_18,
ir::Select::Make(
ir::GT::Make(ir::Load::Make(var_18, {var_i, var_j}),
ir::Load::Make(var_17, {var_i, var_j, var_reduce_k_0})),
ir::Load::Make(var_18, {var_i, var_j}),
ir::Load::Make(var_17, {var_i, var_j, var_reduce_k_0})),
{var_i, var_j});
// Create reduction loop
ir::Expr reduce_loop = ir::For::Make(var_reduce_k_0,
ir::Expr(0),
ir::Expr(128),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({reduce_body}));
// Create j loop
ir::Expr j_loop = ir::For::Make(var_j,
ir::Expr(0),
ir::Expr(16),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({reduce_loop}));
// Create i loop
ir::Expr i_loop = ir::For::Make(var_i,
ir::Expr(0),
ir::Expr(32768),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_loop}));
// Final expression
ir::Expr expr = ir::Block::Make({i_loop});
VLOG(6) << "Before Simplify: " << expr;
Simplify(&expr);
VLOG(6) << "After Simplify: " << expr;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i, 0, 32768)
{
serial for (j, 0, 16)
{
serial for (reduce_k_0, 0, 128)
{
var_18[i, j] = cinn_max(var_17[i, j, reduce_k_0], var_18[i, j])
}
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(expr), utils::Trim(expected_ir));
}
/*
serial for (i, 0ll, 32768ll) {
serial for (j, 0ll, 16ll) {
serial for (reduce_k_0, 0ll, 128ll) {
var_18[i, j] = select((var_18[i, j] < var_17[i, j, reduce_k_0]),
var_18[i, j], var_17[i, j, reduce_k_0])
}
}
}
}
*/
TEST(IRSimplifySelect, SimplifySelectToMin) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape_2d = {ir::Expr(32768), ir::Expr(16)};
const std::vector<ir::Expr> shape_3d = {
ir::Expr(32768), ir::Expr(16), ir::Expr(128)};
ir::Tensor var_17 =
ir::_Tensor_::Make("var_17", ir::Float(32), shape_3d, shape_3d);
var_17->WithBuffer("global", "var_17_buffer");
ir::Tensor var_18 =
ir::_Tensor_::Make("var_18", ir::Float(32), shape_2d, shape_2d);
var_18->WithBuffer("global", "var_18_buffer");
// Define loop variables
ir::Var var_i = ir::Var(ir::Expr(0), ir::Expr(32768), "i");
ir::Var var_j = ir::Var(ir::Expr(0), ir::Expr(16), "j");
ir::Var var_reduce_k_0 = ir::Var(ir::Expr(0), ir::Expr(128), "reduce_k_0");
// Create innermost reduction loop body
ir::Expr reduce_body = ir::Store::Make(
var_18,
ir::Select::Make(
ir::LT::Make(ir::Load::Make(var_18, {var_i, var_j}),
ir::Load::Make(var_17, {var_i, var_j, var_reduce_k_0})),
ir::Load::Make(var_18, {var_i, var_j}),
ir::Load::Make(var_17, {var_i, var_j, var_reduce_k_0})),
{var_i, var_j});
// Create reduction loop
ir::Expr reduce_loop = ir::For::Make(var_reduce_k_0,
ir::Expr(0),
ir::Expr(128),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({reduce_body}));
// Create j loop
ir::Expr j_loop = ir::For::Make(var_j,
ir::Expr(0),
ir::Expr(16),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({reduce_loop}));
// Create i loop
ir::Expr i_loop = ir::For::Make(var_i,
ir::Expr(0),
ir::Expr(32768),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_loop}));
// Final expression
ir::Expr expr = ir::Block::Make({i_loop});
VLOG(6) << "Before Simplify: " << expr;
Simplify(&expr);
VLOG(6) << "After Simplify: " << expr;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i, 0, 32768)
{
serial for (j, 0, 16)
{
serial for (reduce_k_0, 0, 128)
{
var_18[i, j] = cinn_min(var_18[i, j], var_17[i, j, reduce_k_0])
}
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(expr), utils::Trim(expected_ir));
}
/*
serial for (i, 0ll, 32768ll)
{
serial for (j, 0, 16)
{
serial for (j_0, 0, 128)
{
var_45[i, j, j_0)] = select(
(var_18[i, ((((j * 128ll) + j_0) / 128ll) + 0ll)] <=
float32(3.4028234663852886e+38)),
select(
(var_18[i, ((((j * 128ll) + j_0) / 128ll) + 0ll)] >=
float32(9.9999997473787516e-05)),
var_18[i, ((((j * 128ll) + j_0) / 128ll) + 0ll)],
float32(9.9999997473787516e-05)
),
float32(3.4028234663852886e+38)
)
}
}
}
*/
TEST(IRSimplifySelect, SimplifySelectToMinMax) {
Context::Global().ResetNameId();
// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape_2d = {ir::Expr(32768), ir::Expr(16)};
const std::vector<ir::Expr> shape_3d = {
ir::Expr(32768), ir::Expr(16), ir::Expr(128)};
ir::Tensor var_18 =
ir::_Tensor_::Make("var_18", ir::Float(32), shape_2d, shape_2d);
var_18->WithBuffer("global", "var_18_buffer");
ir::Tensor var_45 =
ir::_Tensor_::Make("var_45", ir::Float(32), shape_3d, shape_3d);
var_45->WithBuffer("global", "var_45_buffer");
// Define loop variables
ir::Var var_i = ir::Var(ir::Expr(0), ir::Expr(32768), "i");
ir::Var var_j = ir::Var(ir::Expr(0), ir::Expr(16), "j");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Create innermost loop body
ir::Expr body = ir::Store::Make(
var_45,
ir::Select::Make(
ir::LE::Make(
ir::Load::Make(
var_18,
{var_i,
ir::Div::Make(
ir::Add::Make(ir::Mul::Make(var_j, ir::Expr(128)),
var_j_0),
ir::Expr(128))}),
ir::Expr(3.4028234663852886e+38f)),
ir::Select::Make(
ir::GE::Make(
ir::Load::Make(
var_18,
{var_i,
ir::Div::Make(
ir::Add::Make(ir::Mul::Make(var_j, ir::Expr(128)),
var_j_0),
ir::Expr(128))}),
ir::Expr(9.9999997473787516e-05f)),
ir::Load::Make(
var_18,
{var_i,
ir::Div::Make(
ir::Add::Make(ir::Mul::Make(var_j, ir::Expr(128)),
var_j_0),
ir::Expr(128))}),
ir::Expr(9.9999997473787516e-05f)),
ir::Expr(3.4028234663852886e+38f)),
{var_i, var_j, var_j_0});
// Create j_0 loop
ir::Expr j_0_loop = ir::For::Make(var_j_0,
ir::Expr(0),
ir::Expr(128),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
// Create j loop
ir::Expr j_loop = ir::For::Make(var_j,
ir::Expr(0),
ir::Expr(16),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_0_loop}));
// Create i loop
ir::Expr i_loop = ir::For::Make(var_i,
ir::Expr(0),
ir::Expr(32768),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_loop}));
// Final expression
ir::Expr expr = ir::Block::Make({i_loop});
VLOG(6) << "Before Simplify: " << expr;
Simplify(&expr);
VLOG(6) << "After Simplify: " << expr;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i, 0, 32768)
{
serial for (j, 0, 16)
{
serial for (j_0, 0, 128)
{
var_45[i, j, j_0] = cinn_min(cinn_max(var_18[i, (((j * 128) + j_0) / 128)], 9.99999975e-05f), 3.40282347e+38f)
}
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(expr), utils::Trim(expected_ir));
}
} // namespace optim
} // namespace cinn
+576
View File
@@ -0,0 +1,576 @@
// Copyright (c) 2024 CINN 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 <glog/logging.h>
#include <gtest/gtest.h>
#include "paddle/cinn/common/ir_util.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/schedule_base.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/optim/if_fold_pass.h"
#include "paddle/cinn/pass/pass_manager.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace common {
#define MAKE_FUNC(body) \
std::vector<ir::Argument> args{ \
ir::Argument(ir::Var("A"), ir::Argument::IO::kInput), \
ir::Argument(ir::Var("B"), ir::Argument::IO::kOutput)}; \
auto new_func = \
ir::_LoweredFunc_::Make("test_func", args, ir::Block::Make({body}), {}); \
optim::StmtPassManager pass_manager; \
pass_manager.AddPass(optim::CreateIfFoldPass()); \
pass_manager.Run(new_func);
/*
* serial for (i, 0, 2)
* {
* serial for (j, 0, 4)
* {
* serial for (k, 0, 8)
* {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* }
* }
* }
* }
* }
* }
*/
TEST(IRSimplify, if_fold_correct_0) {
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))}))));
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
serial for (i, 0, 2)
{
serial for (j, 0, 4)
{
serial for (k, 0, 8)
{
if (((((i * 1024) + k) + (j * 256)) == 0)) {
int32 a = 1
}
}
}
}
}
)ROC"));
}
/*
* serial for (i, 0, 2)
* {
* serial for (j, 0, 4)
* {
* serial for (k, 0, 8)
* {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* int32 b = 1
* }
* }
* }
* }
* }
* }
*/
TEST(IRSimplify, if_fold_correct_1) {
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1)),
ir::Let::Make(ir::Var("b"), Expr(1))}))));
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
serial for (i, 0, 2)
{
serial for (j, 0, 4)
{
serial for (k, 0, 8)
{
if (((((i * 1024) + k) + (j * 256)) == 0)) {
int32 a = 1
int32 b = 1
}
}
}
}
}
)ROC"));
}
/*
* serial for (i, 0, 2)
* {
* serial for (j, 0, 4)
* {
* serial for (k, 0, 8)
* {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* int32 b = 1
* } else {
* int32 c = 1
* }
* }
* }
* }
* }
* }
*/
TEST(IRSimplify, if_fold_correct_2) {
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1)),
ir::Let::Make(ir::Var("b"), Expr(1))}),
ir::Block::Make({ir::Let::Make(ir::Var("c"), Expr(1))}))));
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
serial for (i, 0, 2)
{
serial for (j, 0, 4)
{
serial for (k, 0, 8)
{
if (((((i * 1024) + k) + (j * 256)) == 0)) {
int32 a = 1
int32 b = 1
} else {
int32 c = 1
}
}
}
}
}
)ROC"));
}
/*
* serial for (i, 0, 2)
* {
* serial for (j, 0, 4)
* {
* serial for (k, 0, 8)
* {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) <= 0)) {
* int32 a = 1
* }
* }
* }
* }
* }
* }
* }
*/
TEST(IRSimplify, if_fold_correct_3) {
std::vector<ir::Expr> shape = {Expr(2), Expr(4), Expr(8)};
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::IfThenElse::Make(
ir::LE::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))})))));
for (int i = shape.size() - 1; i >= 0; --i) {
ir::Var loop_var = axis_vars[i];
ir::Expr loop_extent = shape[i];
body = ir::For::Make(loop_var,
Expr(0),
loop_extent,
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
}
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
serial for (i, 0, 2)
{
serial for (j, 0, 4)
{
serial for (k, 0, 8)
{
if (((((i * 1024) + k) + (j * 256)) == 0)) {
if (((((256 * j) + ((1024 * i) + k)) % 56) <= 0)) {
int32 a = 1
}
}
}
}
}
}
)ROC"));
}
/*
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* }
* } else {
* int32 b = 1
* }
* }
*/
TEST(IRSimplify, if_fold_has_false_brh) {
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))})),
ir::Block::Make({ir::Let::Make(ir::Var("b"), Expr(1))})));
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 0)) {
if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
int32 a = 1
}
} else {
int32 b = 1
}
}
}
)ROC"));
}
/*
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) <= 0)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* }
* }
* }
*/
TEST(IRSimplify, if_fold_LE) {
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::LE::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))}))));
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) <= 0)) {
if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
int32 a = 1
}
}
}
}
)ROC"));
}
/*
* if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
* if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 2)) {
* if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
* int32 a = 1
* }
* }
* }
*/
TEST(IRSimplify, if_fold_EQ_2) {
std::vector<Var> axis_vars = cinn::common::GenDefaultAxis(3);
auto body = ir::IfThenElse::Make(
ir::EQ::Make(
((((256 * axis_vars[1]) + ((1024 * axis_vars[0]) + axis_vars[2])) /
56) /
56),
Expr(0)),
ir::IfThenElse::Make(
ir::EQ::Make(((((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) /
56) %
56),
Expr(2)),
ir::IfThenElse::Make(
ir::EQ::Make(((256 * axis_vars[1]) +
((1024 * axis_vars[0]) + axis_vars[2])) %
56,
Expr(0)),
ir::Block::Make({ir::Let::Make(ir::Var("a"), Expr(1))}))));
MAKE_FUNC(body);
EXPECT_EQ(utils::GetStreamCnt(new_func),
utils::Trim(R"ROC(function test_func (A, B)
{
if ((((((256 * j) + ((1024 * i) + k)) / 56) / 56) == 0)) {
if ((((((256 * j) + ((1024 * i) + k)) / 56) % 56) == 2)) {
if (((((256 * j) + ((1024 * i) + k)) % 56) == 0)) {
int32 a = 1
}
}
}
}
)ROC"));
}
/*
serial for (i_j_fused, 0ll, 524288ll)
{
serial for (j_0, 0, 128)
{
var_45[(i_j_fused / 16), (((i_j_fused % 16) * 128) + j_0)] =
pow(2.0f, ceil(log2((0.00223214296f * var_31[0]))))
}
}
*/
TEST(IRSimplifyPowerCeilLog2BitOpLdexpf, Base) {
Context::Global().ResetNameId();
/// Create input IR matching the specified pattern
const std::vector<ir::Expr> shape_2d = {ir::Expr(32768), ir::Expr(16)};
const std::vector<ir::Expr> shape_3d = {ir::Expr(32768), ir::Expr(16)};
ir::Tensor var_31 =
ir::_Tensor_::Make("var_31", ir::Float(32), shape_2d, shape_2d);
var_31->WithBuffer("global", "var_31_buffer");
ir::Tensor var_45 =
ir::_Tensor_::Make("var_45", ir::Float(32), shape_3d, shape_3d);
var_45->WithBuffer("global", "var_45_buffer");
// Define loop variables
ir::Var var_i_j_fused = ir::Var(ir::Expr(0), ir::Expr(524288), "i_j_fused");
ir::Var var_j_0 = ir::Var(ir::Expr(0), ir::Expr(128), "j_0");
// Create innermost loop body
ir::Expr body = ir::Store::Make(
var_45,
ir::Call::Make(
ir::Float(32), // Return type
"pow", // Intrinsic function name
{ir::Expr(2.0f),
ir::Call::Make(
ir::Float(32),
"ceil",
{ir::Call::Make(
ir::Float(32),
"log2",
{ir::Mul::Make(ir::Expr(0.00223214296f),
ir::Load::Make(var_31, {ir::Expr(0)}))},
{},
ir::CallType::Intrinsic)},
{},
ir::CallType::Intrinsic)},
{},
ir::CallType::Intrinsic),
{ir::Div::Make(var_i_j_fused, ir::Expr(16)),
ir::Add::Make(ir::Mul::Make(ir::Mod::Make(var_i_j_fused, ir::Expr(16)),
ir::Expr(128)),
var_j_0)});
// Create j_0 loop
ir::Expr j_0_loop = ir::For::Make(var_j_0,
ir::Expr(0),
ir::Expr(128),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({body}));
// Create i_j_fused loop
ir::Expr i_j_fused_loop = ir::For::Make(var_i_j_fused,
ir::Expr(0),
ir::Expr(524288),
ir::ForType::Serial,
ir::DeviceAPI::Host,
ir::Block::Make({j_0_loop}));
// Final expression
ir::Expr expr = ir::Block::Make({i_j_fused_loop});
VLOG(6) << "Before Simplify: " << expr;
cinn::optim::Simplify(&expr);
VLOG(6) << "After Simplify: " << expr;
// Expected output verification
std::string expected_ir = R"ROC({
serial for (i_j_fused, 0, 524288)
{
serial for (j_0, 0, 128)
{
var_45[(i_j_fused / 16), (((i_j_fused % 16) * 128) + j_0)] = ldexpf(1.00000000f, ((bitwise_and(right_shift(__float_as_uint((0.00223214296f * var_31[0])), 23), 255) - 127) + select((((bitwise_and(right_shift(__float_as_uint((0.00223214296f * var_31[0])), 23), 255) - 127) != -127) and (bitwise_and(__float_as_uint((0.00223214296f * var_31[0])), 8388607) != 0)), 1, 0)))
}
}
})ROC";
EXPECT_EQ(utils::GetStreamCnt(expr), utils::Trim(expected_ir));
}
} // namespace common
} // namespace cinn
@@ -0,0 +1,111 @@
// Copyright (c) 2024 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/fuse_parallel_matmul_pass.h"
#include "paddle/cinn/hlir/dialect/operator/transforms/pd_to_cinn_pass.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
void BuildProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 0.5);
paddle::dialect::FullOp weight_1 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 32}, 0.5);
paddle::dialect::FullOp weight_2 =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{32, 64}, 0.5);
paddle::dialect::FullOp weight_3 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{32, 128}, 0.5);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_1.out());
paddle::dialect::MatmulOp matmul_op2 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_2.out());
paddle::dialect::MatmulOp matmul_op3 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_3.out());
builder.Build<paddle::dialect::FetchOp>(matmul_op1.out(), "x", 0);
builder.Build<paddle::dialect::FetchOp>(matmul_op2.out(), "y", 1);
builder.Build<paddle::dialect::FetchOp>(matmul_op3.out(), "z", 1);
}
TEST(Cinn, FuseMatmul) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
ASSERT_EQ((program.block()->size()), 10u);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateFuseParallelMatmulPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
ASSERT_EQ((pm.Run(&program)), true);
ASSERT_EQ((program.block()->size()), 20u);
}
// [64, 32] * [16, 32, 32] => [16, 64, 32]
void BuildBatchProgram(pir::Builder &builder) { // NOLINT
paddle::dialect::FullOp x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 32}, 0.5);
paddle::dialect::FullOp weight_1 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{16, 32, 32}, 0.5);
paddle::dialect::FullOp weight_2 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{16, 32, 64}, 0.5);
paddle::dialect::FullOp weight_3 = builder.Build<paddle::dialect::FullOp>(
std::vector<int64_t>{16, 32, 128}, 0.5);
paddle::dialect::MatmulOp matmul_op1 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_1.out());
paddle::dialect::MatmulOp matmul_op2 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_2.out());
paddle::dialect::MatmulOp matmul_op3 =
builder.Build<paddle::dialect::MatmulOp>(x.out(), weight_3.out());
builder.Build<paddle::dialect::FetchOp>(matmul_op1.out(), "x", 0);
builder.Build<paddle::dialect::FetchOp>(matmul_op2.out(), "y", 1);
builder.Build<paddle::dialect::FetchOp>(matmul_op3.out(), "z", 1);
}
TEST(Cinn, FuseBatchMatmul) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildBatchProgram(builder);
ASSERT_EQ((program.block()->size()), 10u);
pir::PassManager pm(ctx);
pm.AddPass(cinn::dialect::ir::CreateFuseParallelMatmulPass());
pm.EnablePassTiming();
pm.EnableIRPrinting();
ASSERT_EQ((pm.Run(&program)), true);
ASSERT_EQ((program.block()->size()), 20u);
}
+726
View File
@@ -0,0 +1,726 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#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/merge_reshape_with_broadcast_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_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/build_cinn_pass.h"
#include "paddle/fluid/pir/transforms/general/dead_code_elimination_pass.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
bool simple_cmp(float a, float b) { return std::abs((a - b) / a) < 1e-5; }
std::vector<::pir::Type> CreateDenseTensorTypes(const phi::DDim& dims) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
::pir::Type fp32_dtype = ::pir::Float32Type::get(ctx);
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {};
size_t offset = 0;
std::vector<::pir::Type> op_output_types = {::pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset)};
return op_output_types;
}
static void RunAndCheckResult(::pir::Program* program,
const bool check_result = true,
const float gt_val = 2.0) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
pir::PassManager stage_1_pm(ctx);
stage_1_pm.AddPass(cinn::dialect::ir::CreatePdOpToCinnOpPass());
stage_1_pm.AddPass(
std::make_unique<cinn::dialect::ir::MergeReshapeWithBroadcastPass>());
stage_1_pm.AddPass(pir::CreateDeadCodeEliminationPass());
stage_1_pm.AddPass(pir::CreateBuildCinnPass());
stage_1_pm.AddPass(cinn::dialect::ir::CreateAddBroadcastToElementwisePass());
PADDLE_ENFORCE_EQ(
stage_1_pm.Run(program),
true,
common::errors::Unavailable("stage_1_pm fail to run program"));
pir::PassManager stage_2_pm(ctx);
stage_2_pm.AddPass(cinn::dialect::ir::CreateCinnGroupClusterPass());
stage_2_pm.AddPass(pir::CreateDeadCodeEliminationPass());
stage_2_pm.AddPass(cinn::dialect::ir::CreateLowerCinnFusionOpPass());
PADDLE_ENFORCE_EQ(
stage_2_pm.Run(program),
true,
common::errors::Unavailable("stage_2_pm fail to run program"));
phi::Place place = phi::GPUPlace(0);
auto kernel_program = pir::PdOpLowerToKernelPass(program, place);
paddle::framework::Scope exe_scope;
paddle::framework::InterpreterCore executor(
place, {"out@fetch"}, kernel_program->block(), &exe_scope);
executor.Run({}, true);
auto out_tensor =
executor.local_scope()->FindVar("out@fetch")->Get<phi::DenseTensor>();
if (check_result) {
bool res0 = simple_cmp(out_tensor.data<float>()[0], gt_val);
EXPECT_EQ(res0, true);
}
}
std::shared_ptr<::pir::Program> BuildGroupProgram() {
::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());
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 1.0;
const std::vector<int64_t> shape = {128, 128, 768};
auto x = builder
.Build<paddle::dialect::FullOp>(
shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto max =
builder.Build<paddle::dialect::MaxOp>(x, std::vector<int64_t>{-1}, true)
.result(0);
auto sub = builder.Build<paddle::dialect::SubtractOp>(x, max).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(
exp, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto out = builder.Build<paddle::dialect::DivideOp>(exp, sum).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuild) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildGroupProgram();
RunAndCheckResult(program.get(), true, 1.0 / 768);
}
std::shared_ptr<::pir::Program> BuildLayerNormProgram() {
::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());
std::vector<int64_t> axes{-1};
auto x =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({128, 128, 768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto bias = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto scale = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto num = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1},
768.0,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto eps = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1},
1e-5,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(x, axes, phi::DataType::FLOAT32, true)
.result(0);
auto mean = builder.Build<paddle::dialect::DivideOp>(sum, num).result(0);
auto power = builder.Build<paddle::dialect::MultiplyOp>(x, x).result(0);
auto power_sum = builder
.Build<paddle::dialect::SumOp>(
power, axes, phi::DataType::FLOAT32, true)
.result(0);
auto mean2 =
builder.Build<paddle::dialect::DivideOp>(power_sum, num).result(0);
auto power_mean =
builder.Build<paddle::dialect::MultiplyOp>(mean, mean).result(0);
auto var =
builder.Build<paddle::dialect::SubtractOp>(mean2, power_mean).result(0);
auto sub = builder.Build<paddle::dialect::SubtractOp>(x, mean).result(0);
auto t1 = builder.Build<paddle::dialect::AddOp>(var, eps).result(0);
auto t2 = builder.Build<paddle::dialect::SqrtOp>(t1).result(0);
auto t3 = builder.Build<paddle::dialect::DivideOp>(sub, t2).result(0);
auto t5 = builder.Build<paddle::dialect::MultiplyOp>(t3, scale).result(0);
auto out = builder.Build<paddle::dialect::MultiplyOp>(t5, bias).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildLayerNorm) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildLayerNormProgram();
RunAndCheckResult(program.get(), false);
}
std::shared_ptr<::pir::Program> BuildDropOutProgram() {
::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());
auto x =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({128, 128, 768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto prob = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({1}),
0.5,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto random = builder
.Build<paddle::dialect::UniformOp>(
std::vector<int64_t>({128, 128, 768}),
phi::DataType::FLOAT32,
0.0,
1.0,
0,
phi::GPUPlace())
.result(0);
auto mask =
builder.Build<paddle::dialect::GreaterThanOp>(random, prob).result(0);
auto mask1 =
builder.Build<paddle::dialect::CastOp>(mask, phi::DataType::FLOAT32)
.result(0);
auto mul = builder.Build<paddle::dialect::MultiplyOp>(x, mask1).result(0);
auto neg_prob = prob =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({1}),
0.5,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto out = builder.Build<paddle::dialect::DivideOp>(mul, neg_prob).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildDropout) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildDropOutProgram();
RunAndCheckResult(program.get(), false);
}
std::shared_ptr<::pir::Program> BuildScaleGroupProgram() {
::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());
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 1.0;
const std::vector<int64_t> shape = {16, 16};
auto x = builder
.Build<paddle::dialect::FullOp>(
shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto out =
builder.Build<paddle::dialect::ScaleOp>(x, 0.5, 0.0, false).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildScale) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildScaleGroupProgram();
RunAndCheckResult(program.get(), true, 0.5);
}
std::shared_ptr<::pir::Program> BuildScaleTensorGroupProgram() {
::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());
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 0.5;
const std::vector<int64_t> shape = {16, 16};
auto x = builder
.Build<paddle::dialect::FullOp>(
shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto scale = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({1}),
0.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto factor = builder.Build<paddle::dialect::CosOp>(scale).result(0);
auto out =
builder.Build<paddle::dialect::ScaleOp>(x, factor, 0.0, false).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildScaleTensor) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildScaleTensorGroupProgram();
RunAndCheckResult(program.get(), true, 0.5);
}
std::shared_ptr<::pir::Program> BuildPowerProgram() {
::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());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto factor =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto power1 =
builder.Build<paddle::dialect::ElementwisePowOp>(x, factor).result(0);
auto power2 = builder.Build<paddle::dialect::PowOp>(power1, 2.0).result(0);
auto out =
builder
.Build<paddle::dialect::ReshapeOp>(power2, std::vector<int64_t>({-1}))
.result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildPower) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildPowerProgram();
RunAndCheckResult(program.get(), true, 16.0);
}
std::shared_ptr<::pir::Program> BuildLayerNorm2Program() {
::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());
std::vector<int64_t> axes{-1};
auto x =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({128, 128, 768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto bias = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto scale = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto num =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{128, 128, 1},
768.0,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(x, axes, phi::DataType::FLOAT32, true)
.result(0);
auto mean = builder.Build<paddle::dialect::DivideOp>(sum, num).result(0);
auto diff = builder.Build<paddle::dialect::SubtractOp>(x, mean).result(0);
auto power = builder.Build<paddle::dialect::MultiplyOp>(diff, diff).result(0);
auto power_sum = builder
.Build<paddle::dialect::SumOp>(
power, axes, phi::DataType::FLOAT32, true)
.result(0);
auto num2 =
builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{128, 128, 1},
768.0,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto var2 =
builder.Build<paddle::dialect::DivideOp>(power_sum, num2).result(0);
auto t1 = builder.Build<paddle::dialect::ScaleOp>(var2, 1.0, 1e-5).result(0);
auto factor = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>{1},
-0.5,
phi::DataType::FLOAT32,
phi::CPUPlace())
.result(0);
auto t2 =
builder.Build<paddle::dialect::ElementwisePowOp>(t1, factor).result(0);
// auto t2 = builder.Build<paddle::dialect::RsqrtOp>(t1).result(0);
auto t3 = builder.Build<paddle::dialect::MultiplyOp>(diff, t2).result(0);
auto t5 = builder.Build<paddle::dialect::MultiplyOp>(t3, scale).result(0);
auto out = builder.Build<paddle::dialect::AddOp>(t5, bias).result(0);
auto mean_out =
builder
.Build<paddle::dialect::ReshapeOp>(mean, std::vector<int64_t>({-1}))
.result(0);
auto mean2_out =
builder
.Build<paddle::dialect::ReshapeOp>(var2, std::vector<int64_t>({-1}))
.result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
builder.Build<paddle::dialect::FetchOp>(mean_out, "mean", 0);
builder.Build<paddle::dialect::FetchOp>(mean2_out, "var", 0);
return program;
}
TEST(GroupOp, TestBuildLayerNorm2) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildLayerNorm2Program();
RunAndCheckResult(program.get(), false);
}
std::shared_ptr<::pir::Program> BuildSum2GroupProgram() {
::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());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
0.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto cos = builder.Build<paddle::dialect::CosOp>(x).result(0);
auto y = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({8, 8}),
0.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto sin = builder.Build<paddle::dialect::SinOp>(y).result(0);
builder.Build<paddle::dialect::FetchOp>(cos, "out", 0);
builder.Build<paddle::dialect::FetchOp>(sin, "out2", 0);
return program;
}
TEST(GroupOp, TestBuildSum2Group) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildSum2GroupProgram();
RunAndCheckResult(program.get(), true, 1.0);
}
std::shared_ptr<::pir::Program> BuildConcatProgram() {
::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());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto t1 =
builder.Build<pir::CombineOp>(std::vector<pir::Value>({x, y})).result(0);
auto out = builder.Build<paddle::dialect::ConcatOp>(t1, 1).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildConcat) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildConcatProgram();
RunAndCheckResult(program.get(), true, 2.0);
}
std::shared_ptr<::pir::Program> BuildSliceProgram() {
::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());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto out = builder
.Build<paddle::dialect::SliceOp>(x,
std::vector<int64_t>({1}),
std::vector<int64_t>({0}),
std::vector<int64_t>({2}),
std::vector<int64_t>({}),
std::vector<int64_t>({}))
.result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildSlice) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildSliceProgram();
RunAndCheckResult(program.get(), true, 2.0);
}
std::shared_ptr<::pir::Program> BuildSplitProgram() {
::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());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto out_arr =
builder.Build<paddle::dialect::SplitWithNumOp>(x, 4, 1).result(0);
auto out = builder.Build<pir::SliceOp>(out_arr, 0).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildSplit) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildSplitProgram();
RunAndCheckResult(program.get(), true, 2.0);
}
std::shared_ptr<::pir::Program> BuildAddNProgram() {
::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());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto z = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto t1 = builder.Build<pir::CombineOp>(std::vector<pir::Value>({x, y, z}))
.result(0);
auto out = builder.Build<paddle::dialect::AddNOp>(t1).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildAddN) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildAddNProgram();
RunAndCheckResult(program.get(), true, 6.0);
}
std::shared_ptr<::pir::Program> BuildSplitSectionProgram() {
::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());
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
2.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto split_arr = builder
.Build<paddle::dialect::SplitOp>(
x, std::vector<int64_t>({3, 5, 8}), -1)
.out();
auto out = builder.Build<pir::SliceOp>(split_arr, 0).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
TEST(GroupOp, TestBuildSplitSection) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildSplitSectionProgram();
RunAndCheckResult(program.get(), 2.0);
}
std::shared_ptr<::pir::Program> BuildReshapeSumProgram() {
::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());
auto x = builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({128 * 128, 768}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace())
.result(0);
auto sum = builder
.Build<paddle::dialect::SumOp>(
x, std::vector<int64_t>{0}, phi::DataType::FLOAT32, true)
.result(0);
builder.Build<paddle::dialect::FetchOp>(sum, "out", 0);
return program;
}
TEST(GroupOp, TestBuildReshapeSum) {
// Step 1: Construct pir::Program
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program = BuildReshapeSumProgram();
RunAndCheckResult(program.get(), true, 128 * 128);
}
+262
View File
@@ -0,0 +1,262 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_attribute.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/hlir/dialect/runtime/ir/jit_kernel_op.h"
#include "paddle/cinn/hlir/dialect/runtime/ir/runtime_dialect.h"
#include "paddle/cinn/hlir/framework/pir/utils.h"
#include "paddle/cinn/hlir/framework/pir_compiler.h"
#include "paddle/cinn/utils/data_util.h"
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/pir/dialect/kernel/ir/kernel_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/api_builder.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
using cinn::hlir::framework::pir::CompatibleInfo;
using cinn::hlir::framework::pir::OpLoweringGroup;
using cinn::hlir::framework::pir::OpLoweringGroupPtr;
bool simple_cmp(float a, float b) { return std::abs((a - b) / a) < 1e-5; }
using ProgramInfo = std::tuple<std::shared_ptr<::pir::Program>,
std::vector<OpLoweringGroupPtr>>;
ProgramInfo BuildProgram() {
::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());
const float value_one = 1.0; // relu(tan(1.)) = 1.5;
const float value_two = 2.0; // relu(tan(2.)) = 0.
auto full_op_x =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 128},
value_one,
phi::DataType::FLOAT32,
phi::GPUPlace());
auto full_op_y =
builder.Build<paddle::dialect::FullOp>(std::vector<int64_t>{64, 128},
value_two,
phi::DataType::FLOAT32,
phi::GPUPlace());
auto tan_op_x = builder.Build<paddle::dialect::TanOp>(full_op_x->result(0));
auto relu_op_x = builder.Build<paddle::dialect::ReluOp>(tan_op_x->result(0));
auto tan_op_y = builder.Build<paddle::dialect::TanOp>(relu_op_x->result(0));
auto relu_op_y = builder.Build<paddle::dialect::ReluOp>(tan_op_y->result(0));
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_x.result(0)});
builder.Build<pir::YieldOp>(std::vector<pir::Value>{full_op_y.result(0)});
builder.Build<pir::YieldOp>(std::vector<pir::Value>{relu_op_y.result(0)});
std::vector<OpLoweringGroupPtr> groups;
const auto full_op_x_ops =
std::initializer_list<::pir::Operation*>({full_op_x.operation()});
groups.emplace_back(std::make_shared<OpLoweringGroup>(
full_op_x_ops,
CompatibleInfo::GroupOpsName(full_op_x_ops))); // For coverage
groups[0]->mut_output_values().push_back(groups[0]->ops().back()->result(0));
const auto full_op_y_ops =
std::initializer_list<::pir::Operation*>({full_op_x.operation()});
groups.emplace_back(std::make_shared<OpLoweringGroup>(
full_op_y_ops, CompatibleInfo::GroupOpsName(full_op_y_ops)));
groups[1]->mut_output_values().push_back(groups[1]->ops().back()->result(0));
const auto vector_ops =
std::vector<::pir::Operation*>({tan_op_x.operation(),
relu_op_x.operation(),
tan_op_y.operation(),
relu_op_y.operation()});
groups.emplace_back(std::make_shared<OpLoweringGroup>(
vector_ops, CompatibleInfo::GroupOpsName(vector_ops)));
groups[2]->mut_output_values().push_back(groups[2]->ops().back()->result(0));
return {program, groups};
}
ProgramInfo BuildSoftmax() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
std::vector<int64_t> axes{-1};
auto x = builder
.Build<paddle::dialect::FullOp>(std::vector<int64_t>({16, 16}),
1.0,
phi::DataType::FLOAT32,
phi::GPUPlace(0))
.result(0);
auto max = builder.Build<cinn::dialect::ReduceMaxOp>(x, axes, true).result(0);
auto broadcast_1 =
builder
.Build<cinn::dialect::BroadcastOp>(
max, std::vector<int64_t>({0, 1}), std::vector<int64_t>({16, 16}))
.result(0);
auto sub =
builder.Build<paddle::dialect::SubtractOp>(x, broadcast_1).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder.Build<cinn::dialect::ReduceSumOp>(exp, axes, true).result(0);
auto broadcast_2 =
builder
.Build<cinn::dialect::BroadcastOp>(
sum, std::vector<int64_t>({0, 1}), std::vector<int64_t>({16, 16}))
.result(0);
auto divide =
builder.Build<paddle::dialect::DivideOp>(exp, broadcast_2).result(0);
auto yield_op = builder.Build<pir::YieldOp>(std::vector<pir::Value>{divide});
std::vector<OpLoweringGroupPtr> groups;
const auto vector_ops =
std::initializer_list<::pir::Operation*>({max.defining_op(),
broadcast_1.defining_op(),
sub.defining_op(),
exp.defining_op(),
sum.defining_op(),
broadcast_2.defining_op(),
divide.defining_op()});
groups.emplace_back(std::make_shared<OpLoweringGroup>(
vector_ops, CompatibleInfo::GroupOpsName(vector_ops)));
groups[0]->mut_output_values().push_back(groups[0]->ops().back()->result(0));
groups[0]->set_op_pattern_kind(cinn::hlir::framework::kReduction);
return {program, groups};
}
// TEST(PirCompiler, CompileSoftmax) {
// // Step 1: Construct pir::Program
// ::pir::IrContext* ctx = ::pir::IrContext::Instance();
// ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
// ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
// ctx->GetOrRegisterDialect<cinn::dialect::RuntimeDialect>();
// ctx->GetOrRegisterDialect<paddle::dialect::KernelDialect>();
// auto new_program = std::make_shared<::pir::Program>(ctx);
// auto prog_info = BuildSoftmax();
// std::shared_ptr<::pir::Program> program = std::get<0>(prog_info);
// std::vector<GroupPtr> groups = std::get<1>(prog_info);
// EXPECT_EQ(program->block()->size(), 9u);
// LOG(INFO) << program->block()->size();
// std::stringstream ss;
// program->Print(ss);
// LOG(INFO) << ss.str();
// // Step 2: Compiler New pir::Program into Runtime Program
// auto target = cinn::common::DefaultNVGPUTarget();
// auto scope = cinn::hlir::framework::BuildScope(target, *program);
// LOG(INFO) << scope->var_names().size();
// ASSERT_EQ(scope->var_names().size(), 8);
// cinn::hlir::framework::PirCompiler ir_compiler(*program, target, scope);
// auto fn_ptr_res = ir_compiler.BuildCUDAJITInfo(groups);
// ::pir::Builder builder = ::pir::Builder(ctx, new_program->block());
// auto x = builder
// .Build<paddle::dialect::FullOp>(std::vector<int64_t>({16,
// 16}),
// 1.0,
// phi::DataType::FLOAT32,
// phi::GPUPlace(0))
// .result(0);
// std::unordered_map<std::string, ::pir::Attribute> op_attrs{
// {cinn::dialect::JitKernelOp::kAttrName,
// cinn::dialect::CINNKernelInfoAttribute::get(ctx, fn_ptr_res[0])},
// };
// std::vector<pir::Type> vec_types;
// vec_types.push_back(groups[0]->ops.back()->result(0).type());
// std::string jit_op_name = cinn::dialect::JitKernelOp::name();
// ::pir::OpInfo op_info = ctx->GetRegisteredOpInfo(jit_op_name);
// ::pir::Operation* cinn_op =
// ::pir::Operation::Create({x}, op_attrs, vec_types, op_info);
// new_program->block()->push_back(cinn_op);
// builder.SetInsertionPointToBlockEnd(new_program->block());
// builder.Build<paddle::dialect::FetchOp>(
// cinn_op->result(cinn_op->num_results() - 1), "out", 0);
// phi::Place place = phi::GPUPlace(0);
// auto kernel_program =
// pir::PdOpLowerToKernelPass(new_program.get(), place);
// paddle::framework::Scope exe_scope;
// paddle::framework::interpreter::ExecutionConfig exe_conf;
// exe_conf.create_local_scope = false;
// paddle::framework::InterpreterCore executor(
// place, {"out@fetch"}, kernel_program->block(), &exe_scope);
// executor.Run({}, true);
// auto out_tensor =
// executor.local_scope()->FindVar("out@fetch")->Get<phi::DenseTensor>();
// bool res0 = simple_cmp(out_tensor.data<float>()[0], 1.0 / 16);
// EXPECT_EQ(res0, true);
// }
// TEST(PirCompiler, CompileGroupOps) {
// // Step 1: Construct pir::Program
// auto prog_info = BuildProgram();
// std::shared_ptr<::pir::Program> program = std::get<0>(prog_info);
// std::vector<GroupPtr> groups = std::get<1>(prog_info);
// EXPECT_EQ(program->block()->size(), 9u);
// LOG(INFO) << program->block()->size();
// std::stringstream ss;
// program->Print(ss);
// LOG(INFO) << ss.str();
// // Step 2: Compiler New pir::Program into Runtime Program
// auto target = cinn::common::DefaultNVGPUTarget();
// auto scope = cinn::hlir::framework::BuildScope(target, *program);
// ASSERT_EQ(scope->var_names().size(), 6);
// cinn::hlir::framework::PirCompiler ir_compiler(*program, target, scope);
// auto runtime_program = ir_compiler.Build(groups);
// // Step 3: Execute Runtime Instruction and check Scope.
// ASSERT_NO_THROW(runtime_program->Execute());
// for (auto& var_name : scope->var_names()) {
// std::string name = {var_name.begin(), var_name.end()};
// std::vector<float> data =
// cinn::GetTensorData<float>(scope->GetTensor(name), target);
// for (int i = 0; i < 1; ++i) {
// LOG_FIRST_N(INFO, 10) << "data: " << data[i];
// }
// }
// }
@@ -0,0 +1,198 @@
// Copyright (c) 2024 CINN 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/cinn/optim/replace_cross_block_reduction.h"
#include <gtest/gtest.h>
#include "paddle/cinn/cinn.h"
#include "paddle/cinn/ir/ir.h"
#include "paddle/cinn/ir/ir_printer.h"
#include "paddle/cinn/ir/op/ir_operators.h"
#include "paddle/cinn/ir/schedule/ir_schedule.h"
#include "paddle/cinn/ir/utils/stmt_converter.h"
#include "paddle/cinn/utils/string.h"
namespace cinn {
namespace optim {
TEST(CrossBlockReductionReplacer, SRLayout) {
Context::Global().ResetNameId();
Placeholder<float> A("A", {Expr(8), Expr(16)});
Var reduce_k(8, "reduce_k");
ir::Tensor B = Compute(
{Expr(16)},
[&](Var i) { return lang::ReduceSum(A(reduce_k, i), {reduce_k}); },
"B");
ir::Tensor C = Compute(
{Expr(16)}, [&](Var i) { return lang::Sqrt(B(i)); }, "C");
ast_gen_ius::TensorGroup tensor_group({A, B, C});
auto func = lang::LowerToAst("reduce_sum_sqrt", {C}, &tensor_group);
ir::Expr expr_func_body = ir::ConvertStmtBlockToExprBlock(func->body_block);
ir::ModuleExpr mod_expr({expr_func_body});
ir::IRSchedule ir_sch(mod_expr);
ir_sch.Bind(ir_sch.GetLoops("B")[0], "blockIdx.x");
ir_sch.Bind(ir_sch.GetLoops("B")[1], "blockIdx.y");
ir_sch.Bind(ir_sch.GetLoops("C")[0], "blockIdx.x");
func->body = ir_sch.GetModule().GetExprs()[0];
A->WithBuffer("global", "_A");
B->WithBuffer("local", "_B_temp_buffer");
func->temp_bufs = {A->buffer, B->buffer};
VLOG(6) << "Before ReplaceCrossBlockReduction: " << func;
ReplaceCrossBlockReduction(func);
VLOG(6) << "After ReplaceCrossBlockReduction: " << func;
EXPECT_EQ(utils::GetStreamCnt(func),
utils::Trim(R"ROC(function reduce_sum_sqrt (_C, _A)
{
ScheduleBlock(root)
{
{
thread_bind[blockIdx.x] for (i, 0, 16)
{
ScheduleBlock(B__reduce_init)
{
i0 = axis.bind(i)
{
B__reduce_init[i0] = 0.00000000f
}
}
thread_bind[blockIdx.y] for (reduce_k, 0, 8)
{
ScheduleBlock(B)
{
i0_0, i1 = axis.bind(i, reduce_k)
{
B[i0_0] = cinn_grid_reduce_sum_fp32(Tensor(A, [8,16]), 16, i0_0)
}
}
}
}
thread_bind[blockIdx.x] for (i, 0, 16)
{
ScheduleBlock(C)
{
i0_1 = axis.bind(i)
{
C[i0_1] = sqrt(B[i0_1])
}
}
}
}
}
}
)ROC"));
EXPECT_EQ(func->temp_spaces.size(), 1);
EXPECT_EQ(func->temp_spaces[0].size().as_int64(), 512);
EXPECT_EQ(func->temp_spaces[0].arg_idx(), 1);
EXPECT_EQ(func->temp_spaces[0].need_zero_init(), false);
}
TEST(CrossBlockReductionReplacer, RSLayout) {
Context::Global().ResetNameId();
Placeholder<float> A("A", {Expr(8), Expr(4), Expr(32)});
Var reduce_k(8, "reduce_k");
ir::Tensor B = Compute(
{Expr(4), Expr(32)},
[&](Var i, Var j) {
return lang::ReduceMax(A(reduce_k, i, j), {reduce_k});
},
"B");
ir::Tensor C = Compute(
{Expr(4), Expr(32)},
[&](Var i, Var j) { return lang::Exp(B(i, j)); },
"C");
ast_gen_ius::TensorGroup tensor_group({A, B, C});
auto func = lang::LowerToAst("reduce_max_exp", {C}, &tensor_group);
ir::Expr expr_func_body = ir::ConvertStmtBlockToExprBlock(func->body_block);
ir::ModuleExpr mod_expr({expr_func_body});
ir::IRSchedule ir_sch(mod_expr);
ir_sch.Bind(ir_sch.GetLoops("B")[0], "blockIdx.x");
ir_sch.Bind(ir_sch.GetLoops("B")[1], "threadIdx.x");
ir_sch.Bind(ir_sch.GetLoops("B")[2], "blockIdx.y");
ir_sch.Bind(ir_sch.GetLoops("C")[0], "blockIdx.x");
ir_sch.Bind(ir_sch.GetLoops("C")[1], "threadIdx.x");
func->body = ir_sch.GetModule().GetExprs()[0];
A->WithBuffer("global", "_A");
B->WithBuffer("local", "_B_temp_buffer");
func->temp_bufs = {A->buffer, B->buffer};
VLOG(6) << "Before ReplaceCrossBlockReduction: " << func;
ReplaceCrossBlockReduction(func);
VLOG(6) << "After ReplaceCrossBlockReduction: " << func;
EXPECT_EQ(utils::GetStreamCnt(func),
utils::Trim(R"ROC(function reduce_max_exp (_C, _A)
{
ScheduleBlock(root)
{
{
thread_bind[blockIdx.x] for (i, 0, 4)
{
thread_bind[threadIdx.x] for (j, 0, 32)
{
ScheduleBlock(B__reduce_init)
{
i0, i1 = axis.bind(i, j)
{
B__reduce_init[i0, i1] = -3.40282347e+38f
}
}
thread_bind[blockIdx.y] for (reduce_k, 0, 8)
{
ScheduleBlock(B)
{
i0_0, i1_0, i2 = axis.bind(i, j, reduce_k)
{
B[i0_0, i1_0] = cinn_grid_reduce_max_fp32(Tensor(A, [8,4,32]), 128, ((i0_0 * 32) + i1_0))
}
}
}
}
}
thread_bind[blockIdx.x] for (i, 0, 4)
{
thread_bind[threadIdx.x] for (j, 0, 32)
{
ScheduleBlock(C)
{
i0_1, i1_1 = axis.bind(i, j)
{
C[i0_1, i1_1] = exp(B[i0_1, i1_1])
}
}
}
}
}
}
}
)ROC"));
EXPECT_EQ(func->temp_spaces.size(), 1);
EXPECT_EQ(func->temp_spaces[0].size().as_int64(), 4096);
EXPECT_EQ(func->temp_spaces[0].arg_idx(), 1);
EXPECT_EQ(func->temp_spaces[0].need_zero_init(), false);
}
} // namespace optim
} // namespace cinn
@@ -0,0 +1,71 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.h"
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/sub_graph_extract_pass.h"
#include "paddle/pir/include/core/builtin_dialect.h"
#include "paddle/pir/include/pass/pass.h"
#include "paddle/pir/include/pass/pass_manager.h"
#include "paddle/pir/include/pattern_rewrite/pattern_rewrite_driver.h"
void BuildProgram(pir::Builder &builder) { // NOLINT
::pir::IrContext *ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
// full -> softmax(max -> subtract -> exp -> sum -> divide)
const float value_one = 1.0;
const std::vector<int64_t> shape = {128, 128, 768};
auto x = builder
.Build<paddle::dialect::FullOp>(
shape, value_one, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto max =
builder.Build<paddle::dialect::MaxOp>(x, std::vector<int64_t>{-1}, true)
.result(0);
auto sub = builder.Build<paddle::dialect::SubtractOp>(x, max).result(0);
auto exp = builder.Build<paddle::dialect::ExpOp>(sub).result(0);
auto sum =
builder
.Build<paddle::dialect::SumOp>(
exp, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto out = builder.Build<paddle::dialect::DivideOp>(exp, sum).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
}
TEST(SubGraphExtract, softmax_test) {
pir::IrContext *ctx = pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::BuiltinDialect>();
pir::Program program(ctx);
pir::Builder builder = pir::Builder(ctx, program.block());
BuildProgram(builder);
program.Print(std::cout);
pir::PassManager pm(ctx);
pm.AddPass(pir::CreateSubGraphExtractPass());
pm.Run(&program);
program.Print(std::cout);
}
+186
View File
@@ -0,0 +1,186 @@
// 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/cinn_op.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/framework/pir/op_lowering_group.h"
#include "paddle/cinn/hlir/framework/pir/op_lowering_impl.h"
#include "paddle/cinn/hlir/framework/pir/utils.h"
#include "paddle/cinn/hlir/framework/pir_compiler.h"
#include "paddle/common/ddim.h"
#include "paddle/fluid/framework/new_executor/interpretercore.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/fluid/pir/transforms/pd_op_to_kernel_pass.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_dialect.h"
#include "paddle/pir/include/dialect/control_flow/ir/cf_op.h"
#include "paddle/pir/include/dialect/shape/utils/shape_or_data_expr.h"
using cinn::hlir::framework::pir::CompatibleInfo;
using cinn::hlir::framework::pir::OpLoweringGroup;
using cinn::hlir::framework::pir::OpLoweringGroupPtr;
bool simple_cmp(float a, float b) { return std::abs((a - b) / a) < 1e-5; }
std::vector<::pir::Type> CreateDenseTensorTypes(const phi::DDim& dims) {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
::pir::Type fp32_dtype = ::pir::Float32Type::get(ctx);
phi::DataLayout data_layout = phi::DataLayout::NCHW;
phi::LegacyLoD lod = {};
size_t offset = 0;
std::vector<::pir::Type> op_output_types = {::pir::DenseTensorType::get(
ctx, fp32_dtype, dims, data_layout, lod, offset)};
return op_output_types;
}
std::tuple<std::shared_ptr<::pir::Program>, std::vector<OpLoweringGroupPtr>>
BuildGroupProgramForLowering() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> x_shape = {-1, 2};
const std::vector<int64_t> y_shape = {1, -1, 2};
auto x = builder
.Build<paddle::dialect::DataOp>(
"input_x", x_shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::DataOp>(
"input_y", y_shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({1, -1, 2})));
builder.SetInsertionPointToBlockEnd(group_op.block());
auto exp = builder.Build<paddle::dialect::ExpOp>(x);
auto reshape = builder.Build<cinn::dialect::ReshapeOp>(
exp.result(0), std::vector<int>{-1, 1, 1});
auto sub = builder.Build<paddle::dialect::SubtractOp>(y, reshape.result(0));
builder.Build<::pir::YieldOp>(std::vector<::pir::Value>{sub.result(0)});
builder.SetInsertionPointToBlockEnd(program->block());
builder.Build<paddle::dialect::FetchOp>(group_op->result(0), "out", 0);
std::vector<OpLoweringGroupPtr> groups;
groups.emplace_back(std::make_shared<OpLoweringGroup>(
std::vector<::pir::Operation*>(
{exp.operation(), reshape.operation(), sub.operation()}),
CompatibleInfo::GroupOpsName(std::vector<::pir::Operation*>(
{exp.operation(), reshape.operation(), sub.operation()}))));
groups[0]->mut_output_ops().insert(groups[0]->ops().back());
std::unordered_map<::pir::Value, symbol::ShapeOrDataDimExprs>
value_to_shape_data;
symbol::DimExpr x_dim_0("S0");
symbol::DimExpr x_dim_1(2);
symbol::DimExpr y_dim_0(1);
symbol::DimExpr y_dim_1("S1");
symbol::DimExpr y_dim_2(2);
value_to_shape_data.emplace(
x,
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({x_dim_0, x_dim_1})));
value_to_shape_data.emplace(
y,
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({y_dim_0, y_dim_1, y_dim_2})));
value_to_shape_data.emplace(exp.result(0), value_to_shape_data.at(x));
value_to_shape_data.emplace(reshape.result(0), value_to_shape_data.at(y));
value_to_shape_data.emplace(sub.result(0), value_to_shape_data.at(y));
groups[0]->set_value_to_shape_or_data_exprs(value_to_shape_data);
return {program, groups};
}
std::tuple<std::shared_ptr<::pir::Program>, std::vector<OpLoweringGroupPtr>>
BuildBroadcastGroupProgramForLowering() {
::pir::IrContext* ctx = ::pir::IrContext::Instance();
ctx->GetOrRegisterDialect<paddle::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<cinn::dialect::OperatorDialect>();
ctx->GetOrRegisterDialect<pir::ControlFlowDialect>();
auto program = std::make_shared<::pir::Program>(ctx);
::pir::Builder builder = ::pir::Builder(ctx, program->block());
const std::vector<int64_t> x_shape = {1, 1, 1};
const std::vector<int64_t> y_shape = {1, -1, 128};
auto x = builder
.Build<paddle::dialect::DataOp>(
"input_x", x_shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto y = builder
.Build<paddle::dialect::DataOp>(
"input_y", y_shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto group_op = builder.Build<cinn::dialect::GroupOp>(
CreateDenseTensorTypes(common::make_ddim({1, -1, 128})));
builder.SetInsertionPointToBlockEnd(group_op.block());
const std::vector<int64_t> x_broadcast_axes = {0, 1, 2};
auto x_broadcast =
builder.Build<cinn::dialect::BroadcastOp>(x, x_broadcast_axes, y_shape);
auto sub =
builder.Build<paddle::dialect::SubtractOp>(x_broadcast->result(0), y);
builder.Build<::pir::YieldOp>(std::vector<::pir::Value>{sub.result(0)});
builder.SetInsertionPointToBlockEnd(program->block());
builder.Build<paddle::dialect::FetchOp>(group_op->result(0), "out", 0);
std::vector<OpLoweringGroupPtr> groups;
groups.emplace_back(std::make_shared<OpLoweringGroup>(
std::vector<::pir::Operation*>(
{x_broadcast.operation(), sub.operation()}),
CompatibleInfo::GroupOpsName(std::vector<::pir::Operation*>(
{x_broadcast.operation(), sub.operation()}))));
groups[0]->mut_output_ops().insert(groups[0]->ops().back());
std::unordered_map<::pir::Value, symbol::ShapeOrDataDimExprs>
value_to_shape_data;
symbol::DimExpr x_dim_0(1);
symbol::DimExpr x_dim_1(1);
symbol::DimExpr x_dim_2(1);
symbol::DimExpr y_dim_0(1);
symbol::DimExpr y_dim_1("S0");
symbol::DimExpr y_dim_2(128);
value_to_shape_data.emplace(
x,
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({x_dim_0, x_dim_1, x_dim_2})));
value_to_shape_data.emplace(
y,
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({y_dim_0, y_dim_1, y_dim_2})));
value_to_shape_data.emplace(
x_broadcast.result(0),
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({y_dim_0, y_dim_1, y_dim_2})));
value_to_shape_data.emplace(
sub.result(0),
symbol::ShapeOrDataDimExprs(
symbol::TensorShapeOrDataDimExprs({y_dim_0, y_dim_1, y_dim_2})));
groups[0]->set_value_to_shape_or_data_exprs(value_to_shape_data);
return {program, groups};
}
@@ -0,0 +1,776 @@
// Copyright (c) 2024 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <sys/stat.h>
#include <memory>
#include <sstream>
#include <fstream>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/ir/group_schedule/config/database.h"
#include "paddle/cinn/ir/group_schedule/config/file_database.h"
#include "paddle/cinn/ir/group_schedule/config/group_tile_config.h"
#include "paddle/cinn/ir/group_schedule/config/schedule_config_manager.h"
#include "paddle/cinn/ir/group_schedule/search/config_searcher.h"
#include "paddle/cinn/ir/group_schedule/search/measurer.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/performance_statistician.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
PD_DECLARE_string(tile_config_policy);
PD_DECLARE_string(cinn_tile_config_filename_label);
COMMON_DECLARE_bool(print_ir);
PD_DECLARE_bool(cinn_measure_kernel_time);
PHI_DECLARE_bool(enable_cinn_compile_cache);
#define MKDIR(path) mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)
struct PerfTestConfig {
int InnerTestNum;
int OuterTestNum;
int MaxTestNum;
float EarlyStopThreshold;
PerfTestConfig()
: InnerTestNum(1),
OuterTestNum(3),
MaxTestNum(3),
EarlyStopThreshold(1.2) {}
PerfTestConfig(int inner_num, int outer_num, int max_num, float earlystop)
: InnerTestNum(inner_num),
OuterTestNum(outer_num),
MaxTestNum(max_num),
EarlyStopThreshold(earlystop) {}
};
std::string GenCSVFilePath(const cinn::common::Target target,
const cinn::ir::IterSpaceType &iter_space_type) {
std::string dirname = "";
std::string filename = "";
for (auto i : iter_space_type) {
dirname += i.first;
dirname += "_";
filename += i.first + i.second;
filename += "_";
}
const std::string kDirSuffix = "_EREBE";
dirname = dirname.substr(0, dirname.size() - 1) + kDirSuffix;
filename = filename.substr(0, filename.size() - 1);
auto checkexist = [](std::string test_path) {
bool path_exists = false;
struct stat statbuf;
if (stat(test_path.c_str(), &statbuf) != -1) {
if (S_ISDIR(statbuf.st_mode)) {
path_exists = true;
}
}
if (!path_exists) {
PADDLE_ENFORCE_NE(MKDIR(test_path.c_str()),
-1,
::common::errors::PreconditionNotMet(
"Can not create directory: %s, Make sure you "
"have permission to write",
test_path));
}
};
std::string root_path = FLAGS_cinn_tile_config_filename_label;
if (root_path == "") {
const std::string kTestFileDir = "./tile_file_test/";
root_path = kTestFileDir;
}
std::string target_str = target.arch_str() + "_" + target.device_name_str();
checkexist(root_path);
checkexist(root_path + target_str);
checkexist(root_path + target_str + "/" + dirname);
VLOG(3) << "Dump_path is " << root_path + dirname + "/" + filename + ".csv";
return root_path + target_str + "/" + dirname + "/" + filename + ".csv";
}
void WriteBucketInfo(
std::ofstream &os,
const std::vector<std::pair<std::string, std::string>> &iter_space_type,
const cinn::ir::BucketInfo &bucket_info) {
std::stringstream ss;
ss << " { ";
for (int i = 0; i < iter_space_type.size(); ++i) {
ss << iter_space_type[i].first << "_" << iter_space_type[i].second << ": "
<< bucket_info.space[i].lower_bound << "-"
<< bucket_info.space[i].upper_bound << " ";
}
ss << "} ";
os << ss.str() << " \t ";
}
// reduce_sum
std::shared_ptr<::pir::Program> BuildReduceSumProgram(int spatial_size,
int reduce_size) {
::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());
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto out = builder
.Build<paddle::dialect::SumOp>(
x, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// layerNorm
std::shared_ptr<::pir::Program> BuildLayerNormProgram(int spatial_size,
int reduce_size) {
::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());
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto sum_val =
builder
.Build<paddle::dialect::SumOp>(
x, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1024, phi::DataType::FLOAT32)
.out();
auto mean_val =
builder.Build<paddle::dialect::DivideOp>(sum_val, divide_num).result(0);
auto sub_num =
builder.Build<paddle::dialect::SubtractOp>(x, mean_val).result(0);
auto pow_val = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto pow_sum =
builder
.Build<paddle::dialect::SumOp>(
pow_val, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto variance_val =
builder.Build<paddle::dialect::DivideOp>(pow_sum, divide_num).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto add_val =
builder.Build<paddle::dialect::AddOp>(variance_val, add_num).result(0);
auto rsqrt_val = builder.Build<paddle::dialect::RsqrtOp>(add_val).result(0);
auto out =
builder.Build<paddle::dialect::MultiplyOp>(rsqrt_val, sub_num).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// softmax
std::shared_ptr<::pir::Program> BuildSoftmaxProgram(int spatial_size,
int reduce_size) {
::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());
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto max_num =
builder.Build<paddle::dialect::MaxOp>(x, std::vector<int64_t>{-1}, true)
.result(0);
auto sub_num =
builder.Build<paddle::dialect::SubtractOp>(x, max_num).result(0);
auto exp_num = builder.Build<paddle::dialect::ExpOp>(sub_num).result(0);
auto exp_sum =
builder
.Build<paddle::dialect::SumOp>(
exp_num, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto out =
builder.Build<paddle::dialect::DivideOp>(exp_num, exp_sum).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// RMSNorm
std::shared_ptr<::pir::Program> BuildRMSNormProgram(int spatial_size,
int reduce_size) {
::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());
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto pow_val = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto sum_val =
builder
.Build<paddle::dialect::SumOp>(
pow_val, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), reduce_size, phi::DataType::FLOAT32)
.out();
auto div_val =
builder.Build<paddle::dialect::DivideOp>(sum_val, divide_num).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto add_val =
builder.Build<paddle::dialect::AddOp>(div_val, add_num).result(0);
auto rsqrt_val = builder.Build<paddle::dialect::RsqrtOp>(add_val).result(0);
auto out = builder.Build<paddle::dialect::MultiplyOp>(rsqrt_val, x).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// Build test program for spatial reduce.
std::shared_ptr<::pir::Program> BuildSpatialReduceProgram(int spatial_size,
int reduce_size) {
std::shared_ptr<::pir::Program> program;
program = BuildSoftmaxProgram(spatial_size, reduce_size);
return program;
}
// Get the tile size configuration for the given dimension lower bound
// dynamically.
int get_tile_size_config_in_small_area(int dimension_lower) {
if (dimension_lower <= 2) {
return 126;
} else if (dimension_lower <= 128) {
return 384;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 1024) {
return 1024;
} else if (dimension_lower <= 2048) {
return 2048;
} else {
PADDLE_THROW(phi::errors::InvalidArgument(
"dimension_lower (%d) exceeds the supported range (<=2048).",
dimension_lower));
}
}
int get_tile_size_config_in_large_area(int dimension_lower) {
if (dimension_lower <= 2) {
return 510;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 4096) {
return 4096;
} else if (dimension_lower <= 8192) {
return 8192;
} else if (dimension_lower <= 16384) {
return 16384;
} else {
PADDLE_THROW(phi::errors::InvalidArgument(
"dimension_lower (%d) exceeds the supported range (<=16384).",
dimension_lower));
}
}
std::shared_ptr<::pir::Program> BuildProgram(bool is_spatial_dynamic,
bool is_reduce_dynamic,
int s_dimension_lower,
int r_dimension_lower) {
std::shared_ptr<::pir::Program> program;
if (!is_spatial_dynamic && !is_reduce_dynamic) {
program = BuildSpatialReduceProgram(s_dimension_lower, r_dimension_lower);
} else if (is_spatial_dynamic && !is_reduce_dynamic) {
program = BuildSpatialReduceProgram(-1, r_dimension_lower);
} else if (!is_spatial_dynamic && is_reduce_dynamic) {
program = BuildSpatialReduceProgram(s_dimension_lower, -1);
} else {
program = BuildSpatialReduceProgram(-1, -1);
}
return program;
}
cinn::ir::BucketInfo CreateBucket(int s_dimension_lower,
int spatial_tile_width,
int r_dimension_lower,
int reduce_tile_width,
bool is_spatial_dynamic,
bool is_reduce_dynamic) {
cinn::ir::BucketInfo bucket_info;
bucket_info.space.push_back(cinn::ir::BucketInfo::Dimension{
s_dimension_lower,
s_dimension_lower + spatial_tile_width - 1,
"S",
/* is_dynamic = */ is_spatial_dynamic});
bucket_info.space.push_back(
cinn::ir::BucketInfo::Dimension{r_dimension_lower,
r_dimension_lower + reduce_tile_width - 1,
"R",
/* is_dynamic = */ is_reduce_dynamic});
return bucket_info;
}
cinn::ir::search::CandidateType GetCandidate(
const cinn::ir::TileConfigMap &best_tile_config_map,
int s_dimension_lower,
int spatial_tile_width,
int r_dimension_lower,
int reduce_tile_width) {
cinn::ir::search::CandidateType best_candidate;
for (auto &it : best_tile_config_map) {
auto s_flag = false, r_flag = false;
auto dims = it.first.space.size();
// SR type support only
if (dims == 2) {
if (it.first.space[0].lower_bound <= s_dimension_lower &&
it.first.space[0].upper_bound >=
s_dimension_lower + spatial_tile_width - 1) {
s_flag = true;
} else if (it.first.space[0].lower_bound == 4096 &&
s_dimension_lower == 4096) {
s_flag = true;
}
if (it.first.space[1].lower_bound <= r_dimension_lower &&
it.first.space[1].upper_bound >=
r_dimension_lower + reduce_tile_width - 1) {
r_flag = true;
} else if (it.first.space[1].lower_bound == 4096 &&
r_dimension_lower == 4096) {
r_flag = true;
}
} else {
PADDLE_THROW(::common::errors::Unavailable("Now just support SR type."));
}
if (s_flag == true && r_flag == true) {
best_candidate = {it.second.warp_num,
it.second.tree_reduce_num,
it.second.spatial_inner_num};
break;
}
}
if (best_candidate.empty()) {
PADDLE_THROW(
::common::errors::Unavailable("Not found the best candidate."));
}
return best_candidate;
}
void TestWindowPerformance(
std::ofstream &os,
const PerfTestConfig &perf_test_config,
const int graph_num,
const int s_dimension_lower,
const int spatial_tile_width,
const int r_dimension_lower,
const int reduce_tile_width,
const int is_spatial_dynamic,
const int is_reduce_dynamic,
const double s_weight,
const double r_weight,
const double sampling_prob,
const int kMaxSamplingTimes,
const int kRepeats,
const cinn::ir::TileConfigMap &best_tile_config_map,
const std::vector<std::pair<std::string, std::string>> &iter_space_type) {
std::vector<double> s_weights =
std::vector<double>(spatial_tile_width, s_weight);
std::vector<double> r_weights =
std::vector<double>(reduce_tile_width, r_weight);
LOG(INFO) << "spatial tile dimension lower bound = " << s_dimension_lower
<< ", reduce tile dimension lower bound = " << r_dimension_lower
<< std::endl;
// Construct pir::Program.
std::shared_ptr<::pir::Program> program;
program = BuildProgram(is_spatial_dynamic,
is_reduce_dynamic,
s_dimension_lower,
r_dimension_lower);
// Construct iter space and objective function.
cinn::ir::BucketInfo bucket_info = CreateBucket(s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width,
is_spatial_dynamic,
is_reduce_dynamic);
std::unique_ptr<cinn::ir::search::BaseObjectiveFunc> obj_func =
std::make_unique<cinn::ir::search::WeightedSamplingTrailObjectiveFunc>(
program.get(),
bucket_info,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
std::vector<std::vector<double>>{s_weights, r_weights});
cinn::ir::search::CandidateType best_candidate =
GetCandidate(best_tile_config_map,
s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width);
// Write current bucket info to csv file.
WriteBucketInfo(os, iter_space_type, bucket_info);
int InnerTestNum = perf_test_config.InnerTestNum;
int OuterTestNum = perf_test_config.OuterTestNum;
int MaxTestNum = perf_test_config.MaxTestNum;
float EarlyStopThreshold = perf_test_config.EarlyStopThreshold;
cinn::ir::search::ScoreType record_baseline_score;
cinn::ir::search::ScoreType record_best_score;
double record_best_variance = 0;
// start test loop
for (int current_test_num = 0; current_test_num < MaxTestNum;
++current_test_num) {
std::vector<cinn::ir::search::ScoreType> vec_best;
std::vector<cinn::ir::search::ScoreType> vec_baseline;
// start outer measure loop
for (int i = 0; i < OuterTestNum; ++i) {
cinn::ir::search::CandidateType default_candidate;
std::vector<cinn::ir::search::ScoreType> vec_best_score;
std::vector<cinn::ir::search::ScoreType> vec_baseline_score;
// start inner measure loop
for (int i = 0; i < InnerTestNum; i++) {
FLAGS_tile_config_policy = "default";
cinn::ir::search::ScoreType temp_baseline_score =
(*obj_func)(default_candidate);
FLAGS_tile_config_policy = "search";
cinn::ir::search::ScoreType temp_best_score =
(*obj_func)(best_candidate);
vec_best_score.push_back(temp_best_score);
vec_baseline_score.push_back(temp_baseline_score);
}
float best_total_time =
std::accumulate(vec_best_score.begin(), vec_best_score.end(), 0.0);
float baseline_total_time = std::accumulate(
vec_baseline_score.begin(), vec_baseline_score.end(), 0.0);
float best_avg_time = best_total_time / InnerTestNum;
float baseline_avg_time = baseline_total_time / InnerTestNum;
vec_best.push_back(best_avg_time);
vec_baseline.push_back(baseline_avg_time);
}
cinn::ir::search::ScoreType best_mean =
std::accumulate(vec_best.begin(), vec_best.end(), 0.0) / OuterTestNum;
cinn::ir::search::ScoreType baseline_mean =
std::accumulate(vec_baseline.begin(), vec_baseline.end(), 0.0) /
OuterTestNum;
// Compute variance of OuterTestNum times of outer measure in each test
double best_variance = 0.0;
double baseline_variance = 0.0;
for (int i = 0; i < OuterTestNum; i++) {
best_variance = best_variance + pow(vec_best[i] - best_mean, 2);
baseline_variance =
baseline_variance + pow(vec_baseline[i] - baseline_mean, 2);
}
best_variance = pow(best_variance / OuterTestNum, 0.5);
baseline_variance = pow(baseline_variance / OuterTestNum, 0.5);
// Early stop when current best_variance smaller than EarlyStopThreshold
if ((best_variance < EarlyStopThreshold) &&
(baseline_variance < EarlyStopThreshold)) {
record_baseline_score = baseline_mean;
record_best_score = best_mean;
record_best_variance = best_variance + baseline_variance;
break;
} else {
if (record_best_variance == 0) {
record_best_variance = best_variance + baseline_variance;
record_baseline_score = baseline_mean;
record_best_score = best_mean;
} else if ((best_variance + baseline_variance) < record_best_variance) {
record_best_variance = best_variance + baseline_variance;
record_baseline_score = baseline_mean;
record_best_score = best_mean;
}
}
} // end of test_num loop
cinn::ir::search::ScoreType optim_percentage =
(1 / (record_best_score)-1 / record_baseline_score) *
record_baseline_score;
LOG(INFO) << "Best score: " << record_best_score / graph_num;
LOG(INFO) << "Baseline score: " << record_baseline_score / graph_num;
LOG(INFO) << "variance: " << (record_best_variance / 2);
LOG(INFO) << "optim percentage: " << optim_percentage;
LOG(INFO) << "Best candidate: " << best_candidate[0] << " "
<< best_candidate[1] << " " << best_candidate[2];
// Write measure results to csv file
os << std::setprecision(3) << " " << record_baseline_score / graph_num
<< " \t " << record_best_score / graph_num << " \t " << optim_percentage
<< "\n";
}
void TestPerformanceForTileConfig(int spatial_left_bound,
int spatial_right_bound,
int reduce_left_bound,
int reduce_right_bound,
bool is_spatial_dynamic,
bool is_reduce_dynamic,
bool test_single_large) {
FLAGS_enable_cinn_compile_cache = false;
FLAGS_cinn_measure_kernel_time = true;
// set tile_file path to test path when user use default setting
std::string root_path = FLAGS_cinn_tile_config_filename_label;
if (root_path == "") {
const std::string kTestFileDir = "./tile_file_test/";
FLAGS_cinn_tile_config_filename_label = kTestFileDir;
}
constexpr int kThreadsPerWarp = 32;
constexpr int kMaxThreadsPerBlock = 1024;
// now each has the same weight
constexpr double s_w = 0.05;
constexpr double r_w = 0.05;
constexpr double sampling_prob = 1.0;
constexpr int kMaxSamplingTimes = 360;
constexpr int kRepeats = 5;
constexpr int kInnerTestNum = 1;
constexpr int kOuterTestNum = 3;
constexpr int kMaxTestNum = 1;
constexpr float kEarlyStopThreshold = 1.2;
// number of nodes in cudaGraph for test, which is defined in
// performance_statistician.h as graph_nodes_num_. This parameter is set to
// make measure results corresponding to one launch for better readability
constexpr int kGraphNum = 25;
// Define the initial grid size for the spatial and reduction dimensions
int spatial_tile_config = 0, reduce_tile_config = 0;
int spatial_tile_width = 0, reduce_tile_width = 0;
// Define weight for each dimension
double s_weight = (is_spatial_dynamic ? s_w : 1.0);
double r_weight = (is_reduce_dynamic ? r_w : 1.0);
auto s_dimension_type = "S";
auto r_dimension_type = "R";
// Define the performance test configuration.
PerfTestConfig perf_test_config = {
kInnerTestNum, kOuterTestNum, kMaxTestNum, kEarlyStopThreshold};
// Get best configuration from json by file database.
std::vector<std::pair<std::string, std::string>> iter_space_type = {
std::make_pair(s_dimension_type,
is_spatial_dynamic == true ? "dynamic" : "static"),
std::make_pair(r_dimension_type,
is_reduce_dynamic == true ? "dynamic" : "static")};
cinn::ir::FileTileConfigDatabase file_database;
cinn::ir::TileConfigMap best_tile_config_map =
file_database.GetConfigs(cinn::common::DefaultTarget(), iter_space_type);
// build csv file
std::string dump_path =
GenCSVFilePath(cinn::common::DefaultTarget(), iter_space_type);
std::ofstream os(dump_path, std::ofstream::app);
PADDLE_ENFORCE_EQ(os.good(),
true,
::common::errors::InvalidArgument(
"Cannot open the file to write: %s", dump_path));
// run performance test for each data grid
for (int s_dimension_lower = spatial_left_bound;
s_dimension_lower < spatial_right_bound ||
s_dimension_lower == spatial_right_bound &&
spatial_left_bound == spatial_right_bound;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config = get_tile_size_config_in_small_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = reduce_left_bound;
r_dimension_lower < reduce_right_bound ||
r_dimension_lower == reduce_right_bound &&
reduce_left_bound == reduce_right_bound;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_small_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
TestWindowPerformance(os,
perf_test_config,
kGraphNum,
s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width,
is_spatial_dynamic,
is_reduce_dynamic,
s_weight,
r_weight,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
best_tile_config_map,
iter_space_type);
} // end of r_dimension_lower loop
} // end of s_dimension_lower loop
if (test_single_large) {
// (II) Test in the single large areas,
// i.e., S:[4096-32768]*R:[2-1024], S:[2-1024]*R:[4096-32768]
for (int s_dimension_lower = 2; s_dimension_lower < 1024;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dynamically
spatial_tile_config =
get_tile_size_config_in_large_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = 4096; r_dimension_lower < 32768;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_large_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
TestWindowPerformance(os,
perf_test_config,
kGraphNum,
s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width,
is_spatial_dynamic,
is_reduce_dynamic,
s_weight,
r_weight,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
best_tile_config_map,
iter_space_type);
}
}
for (int s_dimension_lower = 4096; s_dimension_lower < 32768;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config =
get_tile_size_config_in_large_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = 2; r_dimension_lower < 1024;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_large_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
// Run performance test and write measure results to csv file.
TestWindowPerformance(os,
perf_test_config,
kGraphNum,
s_dimension_lower,
spatial_tile_width,
r_dimension_lower,
reduce_tile_width,
is_spatial_dynamic,
is_reduce_dynamic,
s_weight,
r_weight,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
best_tile_config_map,
iter_space_type);
}
}
}
os.close();
}
/**
* @brief Test case for the ConfigSearcher.
*
* This test case performs a perormance test for the best configuration derived
* from the ConfigSearcher. It iterates over different spatial and reduce tile
* sizes and constructs a pir::Program. The objective function used for the
* search is a WeightedSamplingTrailObjectiveFunc. The performance results are
* written into a CSV file, including the default score and the best candidate
* score.
*/
TEST(ConfigSearcher, TestPerfDynamicDynamic) {
constexpr int spatial_left_bound = 2; // for full test, set it to 2
constexpr int spatial_right_bound = 2; // for full test, set it to 4096
constexpr int reduce_left_bound = 2; // for full test, set it to 2
constexpr int reduce_right_bound = 2; // for full test, set it to 4096
constexpr bool is_spatial_dynamic = true;
constexpr bool is_reduce_dynamic = true;
bool test_single_large = false; // To test single large area, set it to true
TestPerformanceForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
test_single_large);
}
TEST(ConfigSearcher, TestPerfStaticDynamic) {
constexpr int spatial_left_bound = 2; // for full test, set it to 2
constexpr int spatial_right_bound = 2; // for full test, set it to 4096
constexpr int reduce_left_bound = 2; // for full test, set it to 2
constexpr int reduce_right_bound = 2; // for full test, set it to 4096
constexpr bool is_spatial_dynamic = false;
constexpr bool is_reduce_dynamic = true;
bool test_single_large = false; // To test single large area, set it to true
TestPerformanceForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
test_single_large);
}
TEST(ConfigSearcher, TestPerfDynamicStatic) {
constexpr int spatial_left_bound = 2; // for full test, set it to 2
constexpr int spatial_right_bound = 2; // for full test, set it to 4096
constexpr int reduce_left_bound = 2; // for full test, set it to 2
constexpr int reduce_right_bound = 2; // for full test, set it to 4096
constexpr bool is_spatial_dynamic = true;
constexpr bool is_reduce_dynamic = false;
bool test_single_large = false; // To test single large area, set it to true
TestPerformanceForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
test_single_large);
}
@@ -0,0 +1,284 @@
// Copyright (c) 2024 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/ir/group_schedule/config/database.h"
#include "paddle/cinn/ir/group_schedule/config/file_database.h"
#include "paddle/cinn/ir/group_schedule/config/group_tile_config.h"
#include "paddle/cinn/ir/group_schedule/config/schedule_config_manager.h"
#include "paddle/cinn/ir/group_schedule/search/config_searcher.h"
#include "paddle/cinn/ir/group_schedule/search/measurer.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/performance_statistician.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
COMMON_DECLARE_bool(print_ir);
PD_DECLARE_bool(cinn_measure_kernel_time);
PHI_DECLARE_bool(enable_cinn_compile_cache);
PD_DECLARE_string(tile_config_policy);
constexpr int kThreadsPerWarp = 32;
constexpr int kMaxThreadsPerBlock = 1024;
// now each has the same weight
constexpr double s_w = 0.05;
constexpr double sampling_prob = 1.0;
constexpr int kMaxSamplingTimes = 300;
constexpr int kRepeats = 3;
// PureSpatial
std::shared_ptr<::pir::Program> BuildSpatialProgram(int spatial_size) {
::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());
const std::vector<int64_t> shape = {spatial_size, 1};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto x_pow = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto x_add = builder.Build<paddle::dialect::AddOp>(add_num, x_pow).result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1024, phi::DataType::FLOAT32)
.out();
auto x_div =
builder.Build<paddle::dialect::DivideOp>(x_add, divide_num).result(0);
builder.Build<paddle::dialect::FetchOp>(x_div, "out", 0);
return program;
}
// Get the tile size configuration for the given dimension lower bound
// dynamically.
int get_tile_size_config(int dimension_lower) {
if (dimension_lower <= 2) {
return 126;
} else if (dimension_lower <= 128) {
return 384;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 1024) {
return 1024;
} else if (dimension_lower <= 2048) {
return 2048;
} else if (dimension_lower <= 4096) {
return 4096;
} else if (dimension_lower <= 8192) {
return 8192;
} else if (dimension_lower <= 16384) {
return 16384;
}
}
std::vector<cinn::ir::search::ConstraintFunc> GetSConstraints(
int s_dimension_lower) {
std::vector<cinn::ir::search::ConstraintFunc> constraints;
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp <= kMaxThreadsPerBlock;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[1] < 256 && candidate[1] % kThreadsPerWarp == 0 ||
candidate[1] == 1 ||
candidate[1] <= 512 && candidate[1] % 128 == 0 ||
candidate[1] % 256 == 0;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp >= candidate[1];
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp % candidate[1] == 0;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[2] == 1 || candidate[2] == 2 || candidate[2] == 4 ||
candidate[2] == 8 || candidate[2] == 16 || candidate[2] == 32;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] <= 4 ||
candidate[0] <= 8 && candidate[0] % 2 == 0 ||
candidate[0] % 4 == 0;
});
return constraints;
}
// Create Bucket Info
cinn::ir::BucketInfo CreateSBucket(int s_dimension_lower,
int spatial_tile_width,
bool is_spatial_dynamic) {
cinn::ir::BucketInfo bucket_info;
bucket_info.space.push_back(cinn::ir::BucketInfo::Dimension{
s_dimension_lower,
s_dimension_lower + spatial_tile_width - 1,
"S",
/* is_dynamic = */ is_spatial_dynamic});
return bucket_info;
}
// Search One Window for corresponding pattern
void SearchSWindow(bool is_spatial_dynamic,
int s_dimension_lower,
int spatial_tile_width,
int spatial_tile_config,
double s_weight) {
std::vector<double> s_weights =
std::vector<double>(spatial_tile_width, s_weight);
// Step 1: Construct pir::Program.
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program_pure_spatial;
if (!is_spatial_dynamic) {
program_pure_spatial = BuildSpatialProgram(s_dimension_lower);
} else {
program_pure_spatial = BuildSpatialProgram(-1);
}
// Step 2: Switch schedule config manager mode.
auto& schedule_config_manager = cinn::ir::ScheduleConfigManager::Instance();
// Step 3: Construct iter space and objective function.
cinn::ir::BucketInfo bucket_info;
bucket_info.space.push_back(cinn::ir::BucketInfo::Dimension{
s_dimension_lower,
s_dimension_lower + spatial_tile_width - 1,
"S",
/* is_dynamic = */ is_spatial_dynamic});
std::unique_ptr<cinn::ir::search::BaseObjectiveFunc> obj_func_pure_spatial =
std::make_unique<cinn::ir::search::WeightedSamplingTrailObjectiveFunc>(
program_pure_spatial.get(),
bucket_info,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
std::vector<std::vector<double>>{s_weights});
std::vector<std::unique_ptr<cinn::ir::search::BaseObjectiveFunc>>
objective_funcs;
objective_funcs.emplace_back(std::move(obj_func_pure_spatial));
// Step 4: Construct config candidate range and constraints.
std::vector<std::pair<int, int>> candidate_range{
{1, 1}, {1, 1}, {1, 1}}; // {1, 32}, {1, 1}, {1, 32}
std::vector<cinn::ir::search::ConstraintFunc> constraints =
GetSConstraints(s_dimension_lower);
// Step 5: Construct searcher and search.
cinn::ir::search::ScheduleConfigSearcher searcher(
std::move(objective_funcs), candidate_range, constraints);
auto search_res = searcher.Search();
// Step 6: Save the best candidate's config of each grid search to json
cinn::ir::FileTileConfigDatabase file_database;
cinn::ir::ScheduleConfig::TileConfig tile_bestconfig;
tile_bestconfig.warp_num = search_res.second[0];
tile_bestconfig.tree_reduce_num = search_res.second[1];
tile_bestconfig.spatial_inner_num = search_res.second[2];
// Extend bucketinfo 's static dim region
if (bucket_info.space[0].is_dynamic == false &&
bucket_info.space[0].lower_bound == bucket_info.space[0].upper_bound) {
bucket_info.space[0].upper_bound =
s_dimension_lower + spatial_tile_config - 1;
}
// Extend bucketinfo 's large value to infinite
if (spatial_tile_config == 16384) {
bucket_info.space[0].upper_bound = static_cast<int>(2e10);
}
file_database.AddConfig(
cinn::common::DefaultTarget(), bucket_info, tile_bestconfig, 0);
LOG(INFO) << "spatial tile dimension lower bound = " << s_dimension_lower
<< std::endl;
LOG(INFO) << "min score = " << search_res.first;
LOG(INFO) << "best candidate: "
<< cinn::utils::Join<int64_t>(search_res.second, ", ");
}
/**
* @brief Test case for the ConfigSearcher.
*
*/
void SearchForSTileConfig(int spatial_l_bound,
int spatial_r_bound,
bool is_s_dynamic) {
FLAGS_cinn_measure_kernel_time = true;
FLAGS_enable_cinn_compile_cache = false;
FLAGS_tile_config_policy = "search";
// Define the search space bounds and sampling probabilities.
int spatial_left_bound = spatial_l_bound;
int spatial_right_bound = spatial_r_bound; // for easy test, set to 2. for
// the whole test, set to 32767
bool is_spatial_dynamic = is_s_dynamic;
// Define the initial grid size for the spatial and reduction dimensions
int spatial_tile_config = 0, reduce_tile_config = 0;
int spatial_tile_width = 0, reduce_tile_width = 0;
// Define weight for each dimension
double s_weight = (is_spatial_dynamic ? s_w : 1.0);
// Search for every window
for (int s_dimension_lower = spatial_left_bound;
s_dimension_lower < spatial_right_bound ||
s_dimension_lower == spatial_right_bound &&
spatial_left_bound == spatial_right_bound;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config = get_tile_size_config(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
SearchSWindow(is_spatial_dynamic,
s_dimension_lower,
spatial_tile_width,
spatial_tile_config,
s_weight);
}
}
TEST(ConfigSearcher, TestPureSpatialstatic) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 32767
bool is_spatial_dynamic = false;
SearchForSTileConfig(
spatial_left_bound, spatial_right_bound, is_spatial_dynamic);
}
TEST(ConfigSearcher, TestPureSpatialDynamic) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 32767
bool is_spatial_dynamic = true;
SearchForSTileConfig(
spatial_left_bound, spatial_right_bound, is_spatial_dynamic);
}
@@ -0,0 +1,553 @@
// Copyright (c) 2024 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 <glog/logging.h>
#include <gtest/gtest.h>
#include <memory>
#include <sstream>
#include "paddle/cinn/hlir/dialect/operator/ir/op_dialect.h"
#include "paddle/cinn/ir/group_schedule/config/database.h"
#include "paddle/cinn/ir/group_schedule/config/file_database.h"
#include "paddle/cinn/ir/group_schedule/config/group_tile_config.h"
#include "paddle/cinn/ir/group_schedule/config/schedule_config_manager.h"
#include "paddle/cinn/ir/group_schedule/search/config_searcher.h"
#include "paddle/cinn/ir/group_schedule/search/measurer.h"
#include "paddle/cinn/utils/string.h"
#include "paddle/common/performance_statistician.h"
#include "paddle/fluid/pir/dialect/operator/ir/op_dialect.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_api.h"
#include "paddle/fluid/pir/dialect/operator/ir/pd_op.h"
#include "paddle/pir/include/core/builtin_type.h"
#include "paddle/pir/include/core/ir_context.h"
#include "paddle/pir/include/core/program.h"
COMMON_DECLARE_bool(print_ir);
PD_DECLARE_bool(cinn_measure_kernel_time);
PHI_DECLARE_bool(enable_cinn_compile_cache);
PD_DECLARE_string(tile_config_policy);
PD_DECLARE_string(cinn_tile_config_filename_label);
constexpr int kThreadsPerWarp = 32;
constexpr int kMaxThreadsPerBlock = 1024;
// now each has the same weight
constexpr double s_w = 0.05;
constexpr double r_w = 0.05;
constexpr double sampling_prob = 1.0;
constexpr int kMaxSamplingTimes = 300;
constexpr int kRepeats = 3;
// layernorm
std::shared_ptr<::pir::Program> BuildLayerNormProgram(int spatial_size,
int reduce_size) {
::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());
const float value_one = 1.0;
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto sum_val =
builder
.Build<paddle::dialect::SumOp>(
x, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1024, phi::DataType::FLOAT32)
.out();
auto mean_val =
builder.Build<paddle::dialect::DivideOp>(sum_val, divide_num).result(0);
auto sub_num =
builder.Build<paddle::dialect::SubtractOp>(x, mean_val).result(0);
auto pow_val = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto pow_sum =
builder
.Build<paddle::dialect::SumOp>(
pow_val, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto variance_val =
builder.Build<paddle::dialect::DivideOp>(pow_sum, divide_num).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto add_val =
builder.Build<paddle::dialect::AddOp>(variance_val, add_num).result(0);
auto rsqrt_val = builder.Build<paddle::dialect::RsqrtOp>(add_val).result(0);
auto out =
builder.Build<paddle::dialect::MultiplyOp>(rsqrt_val, sub_num).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// rmsnorm
std::shared_ptr<::pir::Program> BuildRMSNormProgram(int spatial_size,
int reduce_size) {
::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());
const float value_one = 1.0;
const std::vector<int64_t> shape = {spatial_size, reduce_size};
auto x = builder
.Build<paddle::dialect::DataOp>(
"x", shape, phi::DataType::FLOAT32, phi::GPUPlace())
.result(0);
auto pow_val = builder.Build<paddle::dialect::PowOp>(x, 2.0).result(0);
auto sum_val =
builder
.Build<paddle::dialect::SumOp>(
pow_val, std::vector<int64_t>{-1}, phi::DataType::FLOAT32, true)
.result(0);
auto divide_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), reduce_size, phi::DataType::FLOAT32)
.out();
auto div_val =
builder.Build<paddle::dialect::DivideOp>(sum_val, divide_num).result(0);
auto add_num =
builder
.Build<paddle::dialect::FullOp>(
std::vector<int64_t>({1}), 1e-6, phi::DataType::FLOAT32)
.out();
auto add_val =
builder.Build<paddle::dialect::AddOp>(div_val, add_num).result(0);
auto rsqrt_val = builder.Build<paddle::dialect::RsqrtOp>(add_val).result(0);
auto out = builder.Build<paddle::dialect::MultiplyOp>(rsqrt_val, x).result(0);
builder.Build<paddle::dialect::FetchOp>(out, "out", 0);
return program;
}
// Get the tile size configuration for the given dimension lower bound
// dynamically.
int get_tile_size_config_in_small_area(int dimension_lower) {
if (dimension_lower <= 2) {
return 126;
} else if (dimension_lower <= 128) {
return 384;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 1024) {
return 1024;
} else if (dimension_lower <= 2048) {
return 2048;
}
}
int get_tile_size_config_in_large_area(int dimension_lower) {
if (dimension_lower <= 2) {
return 510;
} else if (dimension_lower <= 512) {
return 512;
} else if (dimension_lower <= 4096) {
return 4096;
} else if (dimension_lower <= 8192) {
return 8192;
} else if (dimension_lower <= 16384) {
return 16384;
}
}
int get_spatial_range(int s_dimension_lower, int r_dimension_lower) {
int compute_size = s_dimension_lower * r_dimension_lower;
if (compute_size <= 1024 * 1024) {
return 1;
} else if (compute_size <= 1024 * 2048) {
return 2;
} else if (compute_size <= 2048 * 2048) {
return 4;
} else if ((s_dimension_lower > 4096) || (r_dimension_lower > 4096)) {
return 8;
}
return 1;
}
void search_then_save_one_window(bool is_spatial_dynamic,
bool is_reduce_dynamic,
int s_dimension_lower,
int r_dimension_lower,
int spatial_tile_width,
int reduce_tile_width,
int spatial_tile_config,
int reduce_tile_config,
double s_weight,
double r_weight) {
std::vector<double> s_weights =
std::vector<double>(spatial_tile_width, s_weight);
std::vector<double> r_weights =
std::vector<double>(reduce_tile_width, r_weight);
// Step 1: Construct pir::Program.
::pir::IrContext* ctx = ::pir::IrContext::Instance();
std::shared_ptr<::pir::Program> program_layer_norm;
std::shared_ptr<::pir::Program> program_rms_norm;
if (!is_spatial_dynamic && !is_reduce_dynamic) {
program_layer_norm =
BuildLayerNormProgram(s_dimension_lower, r_dimension_lower);
} else if (is_spatial_dynamic && !is_reduce_dynamic) {
program_layer_norm = BuildLayerNormProgram(-1, r_dimension_lower);
} else if (!is_spatial_dynamic && is_reduce_dynamic) {
program_layer_norm = BuildLayerNormProgram(s_dimension_lower, -1);
} else {
program_layer_norm = BuildLayerNormProgram(-1, -1);
}
if (!is_spatial_dynamic && !is_reduce_dynamic) {
program_rms_norm =
BuildRMSNormProgram(s_dimension_lower, r_dimension_lower);
} else if (is_spatial_dynamic && !is_reduce_dynamic) {
program_rms_norm = BuildRMSNormProgram(-1, r_dimension_lower);
} else if (!is_spatial_dynamic && is_reduce_dynamic) {
program_rms_norm = BuildRMSNormProgram(s_dimension_lower, -1);
} else {
program_rms_norm = BuildRMSNormProgram(-1, -1);
}
// Step 2: Switch schedule config manager mode.
auto& schedule_config_manager = cinn::ir::ScheduleConfigManager::Instance();
// Step 3: Construct iter space and objective function.
cinn::ir::BucketInfo bucket_info;
bucket_info.space.push_back(cinn::ir::BucketInfo::Dimension{
s_dimension_lower,
s_dimension_lower + spatial_tile_width - 1,
"S",
/* is_dynamic = */ is_spatial_dynamic});
bucket_info.space.push_back(
cinn::ir::BucketInfo::Dimension{r_dimension_lower,
r_dimension_lower + reduce_tile_width - 1,
"R",
/* is_dynamic = */ is_reduce_dynamic});
std::unique_ptr<cinn::ir::search::BaseObjectiveFunc> obj_func_layernorm =
std::make_unique<cinn::ir::search::WeightedSamplingTrailObjectiveFunc>(
program_layer_norm.get(),
bucket_info,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
std::vector<std::vector<double>>{s_weights, r_weights});
std::unique_ptr<cinn::ir::search::BaseObjectiveFunc> obj_func_rmsnorm =
std::make_unique<cinn::ir::search::WeightedSamplingTrailObjectiveFunc>(
program_rms_norm.get(),
bucket_info,
sampling_prob,
kMaxSamplingTimes,
kRepeats,
std::vector<std::vector<double>>{s_weights, r_weights});
std::vector<std::unique_ptr<cinn::ir::search::BaseObjectiveFunc>>
objective_funcs;
objective_funcs.emplace_back(std::move(obj_func_layernorm));
objective_funcs.emplace_back(std::move(obj_func_rmsnorm));
// Step 4: Construct config candidate range and constraints.
std::vector<std::pair<int, int>> candidate_range{
{1, 1}, {1, 32}, {1, 1}}; // {1, 32}, {1, 1024}, {1, 8}
std::vector<cinn::ir::search::ConstraintFunc> constraints;
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp <= kMaxThreadsPerBlock;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[1] < 256 && candidate[1] % kThreadsPerWarp == 0 ||
candidate[1] == 1 ||
candidate[1] <= 512 && candidate[1] % 128 == 0 ||
candidate[1] % 256 == 0;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp >= candidate[1];
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp % candidate[1] == 0;
});
constraints.emplace_back(
[&](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] * kThreadsPerWarp / candidate[1] * candidate[2] <=
s_dimension_lower;
});
constraints.emplace_back(
[&](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[2] <=
get_spatial_range(s_dimension_lower, r_dimension_lower);
});
constraints.emplace_back(
[&](const cinn::ir::search::CandidateType& candidate) -> bool {
return r_dimension_lower % candidate[1] == 0 || candidate[1] == 32 ||
candidate[1] == 64;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[2] <= candidate[1];
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[2] == 1 || candidate[2] == 2 || candidate[2] == 4 ||
candidate[2] == 8;
});
constraints.emplace_back(
[](const cinn::ir::search::CandidateType& candidate) -> bool {
return candidate[0] <= 4 ||
candidate[0] <= 8 && candidate[0] % 2 == 0 ||
candidate[0] % 4 == 0;
});
// Step 5: Construct searcher and search.
cinn::ir::search::ScheduleConfigSearcher searcher(
std::move(objective_funcs), candidate_range, constraints);
auto search_res = searcher.Search();
// Step 6: Save the best candidate's config of each grid search to json
cinn::ir::FileTileConfigDatabase file_database;
cinn::ir::ScheduleConfig::TileConfig tile_bestconfig;
tile_bestconfig.warp_num = search_res.second[0];
tile_bestconfig.tree_reduce_num = search_res.second[1];
tile_bestconfig.spatial_inner_num = search_res.second[2];
// Extend bucketinfo 's static dim region
if (bucket_info.space[0].is_dynamic == false &&
bucket_info.space[0].lower_bound == bucket_info.space[0].upper_bound) {
bucket_info.space[0].upper_bound =
s_dimension_lower + spatial_tile_config - 1;
}
if (bucket_info.space[1].is_dynamic == false &&
bucket_info.space[1].lower_bound == bucket_info.space[1].upper_bound) {
bucket_info.space[1].upper_bound =
r_dimension_lower + reduce_tile_config - 1;
}
// Extend bucketinfo 's large value to infinite
if (spatial_tile_config == 1000) {
bucket_info.space[0].upper_bound = static_cast<int>(2e10);
}
if (reduce_tile_config == 1000) {
bucket_info.space[1].upper_bound = static_cast<int>(2e10);
}
file_database.AddConfig(
cinn::common::DefaultTarget(), bucket_info, tile_bestconfig, 0);
LOG(INFO) << "spatial tile dimension lower bound = " << s_dimension_lower
<< ", reduce tile dimension lower bound = " << r_dimension_lower
<< std::endl;
LOG(INFO) << "min score = " << search_res.first;
LOG(INFO) << "best candidate: "
<< cinn::utils::Join<int64_t>(search_res.second, ", ");
if (r_dimension_lower * s_dimension_lower >= (2048 * 1024)) {
sleep(15);
} else {
sleep(2);
}
}
/**
* @brief Test case for the ConfigSearcher.
*
* This test case performs a search for the best configuration using the
* ConfigSearcher. It iterates over different spatial and reduce tile sizes and
* constructs a pir::Program. The search is performed using a
* ScheduleConfigSearcher, which takes into account candidate ranges and
* constraints. The objective function used for the search is a
* WeightedSamplingTrailObjectiveFunc. The search results are logged, including
* the minimum score and the best candidate configuration found.
*/
void TestSearchForTileConfig(int spatial_l_bound,
int spatial_r_bound,
int reduce_l_bound,
int reduce_r_bound,
bool is_s_dynamic,
bool is_r_dynamic,
bool search_single_large) {
FLAGS_cinn_measure_kernel_time = true;
FLAGS_enable_cinn_compile_cache = false;
FLAGS_tile_config_policy = "search";
// set tile_file path to test path when user use default setting
std::string root_path = FLAGS_cinn_tile_config_filename_label;
if (root_path == "") {
const std::string kTestFileDir = "./tile_file_test/";
FLAGS_cinn_tile_config_filename_label = kTestFileDir;
}
// Define the search space bounds and sampling probabilities.
int spatial_left_bound = spatial_l_bound;
int spatial_right_bound = spatial_r_bound; // for easy test, set to 2. for
// the whole test, set to 4096
int reduce_left_bound = reduce_l_bound;
int reduce_right_bound = reduce_r_bound; // for easy test : set to 2. for the
// whole test, set to 4096
bool is_spatial_dynamic = is_s_dynamic;
bool is_reduce_dynamic = is_r_dynamic;
// Define the initial grid size for the spatial and reduction dimensions
int spatial_tile_config = 0, reduce_tile_config = 0;
int spatial_tile_width = 0, reduce_tile_width = 0;
// Define weight for each dimension
double s_weight = (is_spatial_dynamic ? s_w : 1.0);
double r_weight = (is_reduce_dynamic ? r_w : 1.0);
// (I) Search in the small area,
// i.e, S:[2-4096]*R:[2-4096]
for (int s_dimension_lower = spatial_left_bound;
s_dimension_lower < spatial_right_bound ||
s_dimension_lower == spatial_right_bound &&
spatial_left_bound == spatial_right_bound;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config = get_tile_size_config_in_small_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = reduce_left_bound;
r_dimension_lower < reduce_right_bound ||
r_dimension_lower == reduce_right_bound &&
reduce_left_bound == reduce_right_bound;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_small_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
search_then_save_one_window(is_spatial_dynamic,
is_reduce_dynamic,
s_dimension_lower,
r_dimension_lower,
spatial_tile_width,
reduce_tile_width,
spatial_tile_config,
reduce_tile_config,
s_weight,
r_weight);
}
}
if (search_single_large) {
// (II) Search in the single large areas,
// i.e., S:[4096-32768]*R:[2-1024], S:[2-1024]*R:[4096-32768]
for (int s_dimension_lower = 2; s_dimension_lower < 1024;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config =
get_tile_size_config_in_large_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = 4096; r_dimension_lower < 32768;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_large_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
search_then_save_one_window(is_spatial_dynamic,
is_reduce_dynamic,
s_dimension_lower,
r_dimension_lower,
spatial_tile_width,
reduce_tile_width,
spatial_tile_config,
reduce_tile_config,
s_weight,
r_weight);
}
}
for (int s_dimension_lower = 4096; s_dimension_lower < 32768;
s_dimension_lower += spatial_tile_config) {
// adjust the tile size for the spatial dimension dymaically
spatial_tile_config =
get_tile_size_config_in_large_area(s_dimension_lower);
spatial_tile_width = (is_spatial_dynamic ? spatial_tile_config : 1);
for (int r_dimension_lower = 2; r_dimension_lower < 1024;
r_dimension_lower += reduce_tile_config) {
// adjust the tile size for the reduce dimension dymaically
reduce_tile_config =
get_tile_size_config_in_large_area(r_dimension_lower);
reduce_tile_width = (is_reduce_dynamic ? reduce_tile_config : 1);
search_then_save_one_window(is_spatial_dynamic,
is_reduce_dynamic,
s_dimension_lower,
r_dimension_lower,
spatial_tile_width,
reduce_tile_width,
spatial_tile_config,
reduce_tile_config,
s_weight,
r_weight);
}
}
}
}
TEST(ConfigSearcher, TestDynamicDynamic) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 4096
int reduce_left_bound = 2;
int reduce_right_bound = 2; // To reproduce, set it to 4096
bool is_spatial_dynamic = true;
bool is_reduce_dynamic = true;
bool search_single_large =
false; // To search rsingle large area, set it to true
TestSearchForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
search_single_large);
}
TEST(ConfigSearcher, TestDynamicReduce) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 4096
int reduce_left_bound = 2;
int reduce_right_bound = 2; // To reproduce, set it to 4096
bool is_spatial_dynamic = false;
bool is_reduce_dynamic = true;
bool search_single_large =
false; // To search rsingle large area, set it to true
TestSearchForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
search_single_large);
}
TEST(ConfigSearcher, TestDynamicSpatial) {
int spatial_left_bound = 2;
int spatial_right_bound = 2; // To reproduce, set it to 4096
int reduce_left_bound = 2;
int reduce_right_bound = 2; // To reproduce, set it to 4096
bool is_spatial_dynamic = true;
bool is_reduce_dynamic = false;
bool search_single_large =
false; // To search single large area, set it to true
TestSearchForTileConfig(spatial_left_bound,
spatial_right_bound,
reduce_left_bound,
reduce_right_bound,
is_spatial_dynamic,
is_reduce_dynamic,
search_single_large);
}