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

145 lines
4.7 KiB
C++

// 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