chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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())));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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。"));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user