chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
add_subdirectory(memory)
add_subdirectory(benchmark)
add_subdirectory(framework)
add_subdirectory(platform)
add_subdirectory(controlflow)
add_subdirectory(elementwise)
add_subdirectory(fused)
add_subdirectory(math)
if(WITH_ONEDNN)
add_subdirectory(onednn)
endif()
add_subdirectory(reader)
add_subdirectory(reduce_ops)
if(WITH_GPU AND TENSORRT_FOUND)
add_subdirectory(tensorrt)
endif()
set(COMMON_OP_DEPS ${COMMON_OP_DEPS} executor)
paddle_test(gather_test SRCS gather_test.cc)
paddle_test(assign_op_test SRCS assign_op_test.cc)
paddle_test(scatter_test SRCS scatter_test.cc DEPS common)
paddle_test(beam_search_decode_op_test SRCS beam_search_decode_op_test.cc)
paddle_test(save_load_op_test SRCS save_load_op_test.cc)
if(WITH_XPU)
paddle_test(save_load_op_test_xpu SRCS save_load_op_test_xpu.cc)
paddle_test(beam_search_op_test_xpu SRCS beam_search_op_test_xpu.cc)
paddle_test(save_load_combine_op_test_xpu SRCS
save_load_combine_op_test_xpu.cc)
endif()
paddle_test(save_load_combine_op_test SRCS save_load_combine_op_test.cc)
if(WITH_CINN)
set(CINN_DEPS python)
endif()
if(WITH_GPU)
nv_test(
dropout_op_test
SRCS dropout_op_test.cc
DEPS dropout_op tensor phi common global_utils)
nv_test(
test_leaky_relu_grad_grad_functor
SRCS test_leaky_relu_grad_grad_functor.cc
test_leaky_relu_grad_grad_functor.cu
DEPS tensor phi eigen3)
elseif(WITH_ROCM)
hip_test(
dropout_op_test
SRCS dropout_op_test.cc
DEPS dropout_op tensor phi common)
hip_test(
test_leaky_relu_grad_grad_functor
SRCS test_leaky_relu_grad_grad_functor.cc
test_leaky_relu_grad_grad_functor.cu
DEPS tensor phi eigen3)
else()
paddle_test(test_leaky_relu_grad_grad_functor SRCS
test_leaky_relu_grad_grad_functor.cc)
endif()
paddle_test(share_buffer_op_cpp_test SRCS share_buffer_op_test.cc)
if(WITH_CINN)
paddle_test(op_debug_string_test SRCS op_debug_string_test.cc)
else()
paddle_test(op_debug_string_test SRCS op_debug_string_test.cc)
endif()
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(op_debug_string_test)
endif()
+114
View File
@@ -0,0 +1,114 @@
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/operators/assign_op.h"
#include <gtest/gtest.h>
#include "paddle/common/ddim.h"
#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/variable.h"
#include "paddle/phi/common/place.h"
TEST(AssignOp, AssignDenseTensor) {
phi::CPUPlace cpu_place;
phi::CPUContext ctx(cpu_place);
paddle::framework::Variable output;
paddle::operators::AssignFunctor assign_functor(&output, ctx);
phi::DenseTensor input;
phi::DDim in_dims = common::make_ddim({3, 4});
int* in_data = input.mutable_data<int>(in_dims, cpu_place);
for (int i = 0; i < 12; ++i) {
in_data[i] = i;
}
assign_functor(input);
auto& out_tensor = output.Get<phi::DenseTensor>();
phi::DDim out_dims = out_tensor.dims();
EXPECT_EQ(in_dims, out_dims);
auto* out_data = out_tensor.data<int>();
for (int i = 0; i < 12; ++i) {
EXPECT_EQ(i, out_data[i]);
}
}
TEST(AssignOp, AssignDenseTensorArray) {
phi::CPUPlace cpu_place;
phi::CPUContext ctx(cpu_place);
paddle::framework::Variable output;
paddle::operators::AssignFunctor assign_functor(&output, ctx);
phi::TensorArray input;
for (int i = 0; i < 5; ++i) {
phi::DDim in_dims = common::make_ddim({i + 1, i + 2});
phi::DenseTensor dense_tensor;
float* in_data = dense_tensor.mutable_data<float>(in_dims, cpu_place);
for (int j = 0; j < (i + 1) * (i + 2); ++j) {
in_data[j] = static_cast<float>(j);
}
input.push_back(dense_tensor);
}
assign_functor(input);
auto& out_array = output.Get<phi::TensorArray>();
for (int i = 0; i < 5; ++i) {
phi::DDim out_dims = out_array[i].dims();
EXPECT_EQ(common::make_ddim({i + 1, i + 2}), out_dims);
const float* out_data = out_array[i].data<float>();
for (int j = 0; j < (i + 1) * (i + 2); ++j) {
EXPECT_EQ(static_cast<float>(j), out_data[j]);
}
}
}
TEST(AssignOp, AssignSelectedRows) {
phi::CPUPlace cpu_place;
phi::CPUContext ctx(cpu_place);
paddle::framework::Variable output;
paddle::operators::AssignFunctor assign_functor(&output, ctx);
std::vector<int64_t> rows{0, 4, 7};
int64_t height = 10;
phi::SelectedRows input(rows, height);
phi::DenseTensor* input_tensor = input.mutable_value();
phi::DDim in_dims = common::make_ddim({3, 4});
int* in_data = input_tensor->mutable_data<int>(in_dims, cpu_place);
for (int i = 0; i < 12; ++i) {
in_data[i] = i;
}
assign_functor(input);
auto& out_selected_row = output.Get<phi::SelectedRows>();
const phi::Vector<int64_t>& out_rows = out_selected_row.rows();
EXPECT_EQ(rows.size(), out_rows.size());
for (size_t i = 0; i < rows.size(); ++i) {
EXPECT_EQ(rows[i], out_rows[i]);
}
EXPECT_EQ(height, out_selected_row.height());
const phi::DenseTensor& out_tensor = out_selected_row.value();
phi::DDim out_dims = out_tensor.dims();
EXPECT_EQ(in_dims, out_dims);
auto* out_data = out_tensor.data<int>();
for (int i = 0; i < 12; ++i) {
EXPECT_EQ(i, out_data[i]);
}
}
@@ -0,0 +1,169 @@
/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/operators/beam_search_decode_op.h"
#include "gtest/gtest.h"
using CPUPlace = phi::CPUPlace;
using LegacyLoD = phi::LegacyLoD;
using DenseTensorArray = phi::TensorArray;
template <typename T>
using BeamSearchDecoder = paddle::operators::BeamSearchDecoder<T>;
template <typename T>
using Sentence = paddle::operators::Sentence<T>;
template <typename T>
using SentenceVector = paddle::operators::SentenceVector<T>;
namespace paddle {
namespace test {
template <typename T>
void GenerateExample(const std::vector<size_t>& level_0,
const std::vector<size_t>& level_1,
const std::vector<int>& data,
DenseTensorArray* ids,
DenseTensorArray* scores) {
PADDLE_ENFORCE_EQ(level_0.back(),
level_1.size() - 1,
common::errors::InvalidArgument(
"source level is used to describe candidate set, "
"so it's element should less than level_1 length. "
"And the value of source "
"level is %d. ",
level_1.size() - 1));
PADDLE_ENFORCE_EQ(level_1.back(),
data.size(),
common::errors::InvalidArgument(
"the lowest level is used to describe data"
", so it's last element should be data length %d. ",
data.size()));
CPUPlace place;
LegacyLoD lod;
lod.push_back(level_0);
lod.push_back(level_1);
// Ids
phi::DenseTensor tensor_id;
tensor_id.set_lod(lod);
tensor_id.Resize({static_cast<int64_t>(data.size())});
// malloc memory
int64_t* id_ptr = tensor_id.mutable_data<int64_t>(place);
for (size_t i = 0; i < data.size(); ++i) {
id_ptr[i] = static_cast<int64_t>(data.at(i));
}
// Scores
phi::DenseTensor tensor_score;
tensor_score.set_lod(lod);
tensor_score.Resize({static_cast<int64_t>(data.size())});
// malloc memory
T* score_ptr = tensor_score.mutable_data<T>(place);
for (size_t i = 0; i < data.size(); ++i) {
score_ptr[i] = static_cast<T>(data.at(i));
}
ids->push_back(tensor_id);
scores->push_back(tensor_score);
}
template <typename T>
void BeamSearchDecodeTestFrame() {
CPUPlace place;
// Construct sample data with 5 steps and 2 source sentences
// beam_size = 2, start_id = 0, end_id = 1
DenseTensorArray ids;
DenseTensorArray scores;
GenerateExample<T>(std::vector<size_t>{0, 1, 2},
std::vector<size_t>{0, 1, 2},
std::vector<int>{0, 0},
&ids,
&scores); // start with start_id
GenerateExample<T>(std::vector<size_t>{0, 1, 2},
std::vector<size_t>{0, 2, 4},
std::vector<int>{2, 3, 4, 5},
&ids,
&scores);
GenerateExample<T>(std::vector<size_t>{0, 2, 4},
std::vector<size_t>{0, 2, 2, 4, 4},
std::vector<int>{3, 1, 5, 4},
&ids,
&scores);
GenerateExample<T>(std::vector<size_t>{0, 2, 4},
std::vector<size_t>{0, 1, 2, 3, 4},
std::vector<int>{1, 1, 3, 5},
&ids,
&scores);
GenerateExample<T>(
std::vector<size_t>{0, 2, 4},
std::vector<size_t>{0, 0, 0, 2, 2}, // the branches of the first source
// sentence are pruned since finished
std::vector<int>{5, 1},
&ids,
&scores);
ASSERT_EQ(ids.size(), 5UL);
ASSERT_EQ(scores.size(), 5UL);
BeamSearchDecoder<T> helper(2, 1); // beam_size = 2, end_id = 1
phi::DenseTensor id_tensor;
phi::DenseTensor score_tensor;
helper.Backtrace(ids, scores, &id_tensor, &score_tensor);
LegacyLoD lod = id_tensor.lod();
std::vector<size_t> expect_source_lod = {0, 2, 4};
EXPECT_EQ(lod[0], expect_source_lod);
std::vector<size_t> expect_sentence_lod = {0, 4, 7, 12, 17};
EXPECT_EQ(lod[1], expect_sentence_lod);
std::vector<int> expect_data = {
0, 2, 3, 1, 0, 2, 1, 0, 4, 5, 3, 5, 0, 4, 5, 3, 1};
ASSERT_EQ(id_tensor.dims()[0], static_cast<int64_t>(expect_data.size()));
for (size_t i = 0; i < expect_data.size(); ++i) {
ASSERT_EQ(id_tensor.data<int64_t>()[i],
static_cast<int64_t>(expect_data[i]));
}
for (int64_t i = 0; i < id_tensor.dims()[0]; ++i) {
ASSERT_EQ(score_tensor.data<T>()[i],
static_cast<T>(id_tensor.data<int64_t>()[i]));
}
}
} // namespace test
} // namespace paddle
TEST(BeamSearchDecodeOp, Backtrace_CPU_Float) {
paddle::test::BeamSearchDecodeTestFrame<float>();
}
TEST(BeamSearchDecodeOp, Backtrace_CPU_Float16) {
paddle::test::BeamSearchDecodeTestFrame<phi::dtype::float16>();
}
TEST(BeamSearchDecodeOp, Backtrace_CPU_Double) {
paddle::test::BeamSearchDecodeTestFrame<double>();
}
TEST(BeamSearchDecodeOp, Backtrace_CPU_Int) {
paddle::test::BeamSearchDecodeTestFrame<int>();
}
TEST(BeamSearchDecodeOp, Backtrace_CPU_Int64) {
paddle::test::BeamSearchDecodeTestFrame<int64_t>();
}
+248
View File
@@ -0,0 +1,248 @@
// 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 "paddle/phi/kernels/funcs/math/beam_search.h"
#include <gtest/gtest.h>
#include "paddle/fluid/framework/operator.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/platform/device_context.h"
void PrepareCPUTensors(phi::DenseTensor* ids,
phi::DenseTensor* scores,
phi::DenseTensor* pre_ids,
phi::DenseTensor* pre_scores) {
// lod
phi::LegacyLoD lod;
std::vector<size_t> level0({0, 2, 4});
std::vector<size_t> level1({0, 1, 2, 3, 4});
lod.push_back(level0);
lod.push_back(level1);
ids->set_lod(lod);
scores->set_lod(lod);
auto dims = common::make_ddim({4, 3});
ids->Resize(dims);
scores->Resize(dims);
phi::CPUPlace place;
auto* ids_data = ids->mutable_data<int64_t>(place);
auto* scores_data = scores->mutable_data<float>(place);
std::vector<int64_t> ids_vec_data({4, 2, 5, 2, 1, 3, 3, 5, 2, 8, 2, 1});
std::vector<float> scores_vec_data(
{0.6f, 0.3f, 0.5f, 0.2f, 0.3f, 0.1f, 0.9f, 0.5f, 0.1f, 0.7f, 0.5f, 0.1f});
PADDLE_ENFORCE_EQ(
static_cast<size_t>(ids->numel()),
ids_vec_data.size(),
common::errors::InvalidArgument(
"Required ids->numel() should be equal to ids_vec_data.size(). "));
PADDLE_ENFORCE_EQ(
static_cast<size_t>(ids->numel()),
scores_vec_data.size(),
common::errors::InvalidArgument(
"Required ids->numel() should be equal to scores_vec_data.size(). "));
for (int i = 0; i < ids->numel(); i++) {
ids_data[i] = ids_vec_data[i];
scores_data[i] = scores_vec_data[i];
}
// pre_ids
pre_ids->Resize(common::make_ddim({4, 1}));
for (int i = 0; i < 4; i++) {
pre_ids->mutable_data<int64_t>(place)[i] = i + 1;
}
// pre_scores
pre_scores->Resize(common::make_ddim({4, 1}));
for (int i = 0; i < 4; i++) {
pre_scores->mutable_data<float>(place)[i] = 0.1 * (i + 1); // NOLINT
}
}
template <typename DeviceContext, typename Place>
void TestBeamSearch() {
phi::DenseTensor ids;
phi::DenseTensor scores;
phi::DenseTensor pre_ids;
phi::DenseTensor pre_scores;
auto* place = new Place();
DeviceContext* context = new DeviceContext(*place);
context->SetAllocator(paddle::memory::allocation::AllocatorFacade::Instance()
.GetAllocator(phi::CPUPlace())
.get());
if (phi::is_cpu_place(*place)) {
PrepareCPUTensors(&ids, &scores, &pre_ids, &pre_scores);
} else {
phi::DenseTensor cpu_ids;
phi::DenseTensor cpu_scores;
phi::DenseTensor cpu_pre_ids;
phi::DenseTensor cpu_pre_scores;
PrepareCPUTensors(&cpu_ids, &cpu_scores, &cpu_pre_ids, &cpu_pre_scores);
paddle::framework::TensorCopySync(cpu_ids, *place, &ids);
paddle::framework::TensorCopySync(cpu_scores, *place, &scores);
paddle::framework::TensorCopySync(cpu_pre_ids, *place, &pre_ids);
paddle::framework::TensorCopySync(cpu_pre_scores, *place, &pre_scores);
ids.set_lod(cpu_ids.lod());
scores.set_lod(cpu_scores.lod());
pre_ids.set_lod(cpu_pre_ids.lod());
pre_scores.set_lod(cpu_pre_scores.lod());
}
phi::DenseTensor selected_ids;
phi::DenseTensor selected_scores;
phi::DenseTensor parent_idx;
size_t level = 0;
size_t beam_size = 2;
int end_id = 0;
phi::math::BeamSearchFunctor<DeviceContext, float> beamsearch;
beamsearch(*context,
&pre_ids,
&pre_scores,
&ids,
&scores,
&selected_ids,
&selected_scores,
&parent_idx,
level,
beam_size,
end_id,
true);
ASSERT_EQ(selected_ids.lod(), selected_scores.lod());
phi::DenseTensor cpu_selected_ids;
phi::DenseTensor cpu_selected_scores;
if (phi::is_cpu_place(*place)) {
cpu_selected_ids = selected_ids;
cpu_selected_scores = selected_scores;
} else {
paddle::framework::TensorCopySync(
selected_ids, phi::CPUPlace(), &cpu_selected_ids);
paddle::framework::TensorCopySync(
selected_scores, phi::CPUPlace(), &cpu_selected_scores);
cpu_selected_ids.set_lod(selected_ids.lod());
cpu_selected_scores.set_lod(selected_scores.lod());
}
std::vector<int64_t> expected_ids({4, 5, 3, 8});
std::vector<float> expected_scores({0.6f, 0.5f, 0.9f, 0.7f});
for (int i = 0; i < 4; i++) {
ASSERT_EQ(expected_ids[i], cpu_selected_ids.data<int64_t>()[i]);
ASSERT_EQ(expected_scores[i], cpu_selected_scores.data<float>()[i]);
}
delete place;
delete context;
}
#if defined(PADDLE_WITH_XPU)
template <>
void TestBeamSearch<phi::XPUContext, phi::XPUPlace>() {
phi::DenseTensor ids;
phi::DenseTensor scores;
phi::DenseTensor pre_ids;
phi::DenseTensor pre_scores;
auto* place = new phi::XPUPlace();
auto* context = new phi::XPUContext(*place);
context->SetAllocator(paddle::memory::allocation::AllocatorFacade::Instance()
.GetAllocator(*place, context->stream())
.get());
context->SetHostAllocator(
paddle::memory::allocation::AllocatorFacade::Instance()
.GetAllocator(phi::CPUPlace())
.get());
if (phi::is_cpu_place(*place)) {
PrepareCPUTensors(&ids, &scores, &pre_ids, &pre_scores);
} else {
phi::DenseTensor cpu_ids;
phi::DenseTensor cpu_scores;
phi::DenseTensor cpu_pre_ids;
phi::DenseTensor cpu_pre_scores;
PrepareCPUTensors(&cpu_ids, &cpu_scores, &cpu_pre_ids, &cpu_pre_scores);
paddle::framework::TensorCopySync(cpu_ids, *place, &ids);
paddle::framework::TensorCopySync(cpu_scores, *place, &scores);
paddle::framework::TensorCopySync(cpu_pre_ids, *place, &pre_ids);
paddle::framework::TensorCopySync(cpu_pre_scores, *place, &pre_scores);
ids.set_lod(cpu_ids.lod());
scores.set_lod(cpu_scores.lod());
pre_ids.set_lod(cpu_pre_ids.lod());
pre_scores.set_lod(cpu_pre_scores.lod());
}
phi::DenseTensor selected_ids;
phi::DenseTensor selected_scores;
phi::DenseTensor parent_idx;
size_t level = 0;
size_t beam_size = 2;
int end_id = 0;
phi::math::BeamSearchFunctor<phi::XPUContext, float> beamsearch;
beamsearch(*context,
&pre_ids,
&pre_scores,
&ids,
&scores,
&selected_ids,
&selected_scores,
&parent_idx,
level,
beam_size,
end_id,
true);
ASSERT_EQ(selected_ids.lod(), selected_scores.lod());
phi::DenseTensor cpu_selected_ids;
phi::DenseTensor cpu_selected_scores;
if (phi::is_cpu_place(*place)) {
cpu_selected_ids = selected_ids;
cpu_selected_scores = selected_scores;
} else {
paddle::framework::TensorCopySync(
selected_ids, phi::CPUPlace(), &cpu_selected_ids);
paddle::framework::TensorCopySync(
selected_scores, phi::CPUPlace(), &cpu_selected_scores);
cpu_selected_ids.set_lod(selected_ids.lod());
cpu_selected_scores.set_lod(selected_scores.lod());
}
std::vector<int64_t> expected_ids({4, 5, 3, 8});
std::vector<float> expected_scores({0.6f, 0.5f, 0.9f, 0.7f});
for (int i = 0; i < 4; i++) {
ASSERT_EQ(expected_ids[i], cpu_selected_ids.data<int64_t>()[i]);
ASSERT_EQ(expected_scores[i], cpu_selected_scores.data<float>()[i]);
}
delete place;
delete context;
}
#endif
TEST(BeamSearch, CPU) { TestBeamSearch<phi::CPUContext, phi::CPUPlace>(); }
#if defined(PADDLE_WITH_XPU)
TEST(BeamSearch, XPU) { TestBeamSearch<phi::XPUContext, phi::XPUPlace>(); }
#endif
+7
View File
@@ -0,0 +1,7 @@
paddle_test(op_tester SRCS op_tester.cc DEPS common phi)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(op_tester)
endif()
+552
View File
@@ -0,0 +1,552 @@
/* Copyright (c) 2016 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 "test/cpp/fluid/benchmark/op_tester.h"
#include <fstream>
#include "gtest/gtest.h"
#include "paddle/common/flags.h"
#include "paddle/fluid/framework/op_info.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/variable_helper.h"
#include "paddle/fluid/platform/init.h"
#include "paddle/phi/core/platform/profiler.h"
#include "paddle/phi/core/platform/timer.h"
// phi
#include "paddle/phi/kernels/declarations.h"
namespace paddle {
namespace operators {
namespace benchmark {
PD_DEFINE_string(op_config_list, "", "Path of op config file."); // NOLINT
PD_DEFINE_int32(specified_config_id, -1, "Test the specified op config.");
void OpTester::Init(const std::string &filename) {
Init(OpTesterConfig(filename));
}
void OpTester::Init(const OpTesterConfig &config) {
config_ = config;
auto &op_desc_info = framework::OpInfoMap::Instance();
// Initialize the OpDesc
if (op_desc_info.Has(config_.op_type)) {
type_ = config_.op_type;
CreateOpDesc();
CreateInputVarDesc();
CreateOutputVarDesc();
} else {
PADDLE_THROW(common::errors::NotFound(
"Operator '%s' is not registered in OpTester.", config_.op_type));
}
if (config_.device_id >= 0) {
place_ = ::phi::GPUPlace(config_.device_id);
} else {
place_ = ::phi::CPUPlace();
}
framework::InitDevices();
scope_ = std::make_unique<::paddle::framework::Scope>();
op_ = framework::OpRegistry::CreateOp(op_desc_);
CreateVariables(scope_.get());
}
void OpTester::Run() {
if (config_.print_debug_string) {
LOG(INFO) << DebugString();
}
// Warm up
RunImpl();
platform::Timer timer;
if (config_.profile) {
if (phi::is_cpu_place(place_)) {
platform::EnableProfiler(platform::ProfilerState::kCPU);
} else {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
platform::EnableProfiler(platform::ProfilerState::kAll);
platform::SetDeviceId(config_.device_id);
#else
PADDLE_THROW(common::errors::PermissionDenied(
"'GPUPlace' is not supported in CPU only device."));
#endif
}
timer.Start();
for (int i = config_.repeat; i > 0; --i) {
RunImpl();
}
timer.Pause();
platform::DisableProfiler(platform::EventSortingKey::kDefault,
"op_tester_profiler");
} else {
timer.Start();
for (int i = config_.repeat; i > 0; --i) {
RunImpl();
}
timer.Pause();
}
config_.runtime = timer.ElapsedMS() / config_.repeat;
LOG(INFO) << "=== Run " << config_.repeat
<< " times, latency: " << config_.runtime << " ms ===";
}
void OpTester::RunImpl() {
op_->Run(*scope_, place_);
phi::DeviceContextPool::Instance().Get(place_)->Wait();
scope_->DropKids();
}
std::vector<std::string> OpTester::GetOpProtoInputNames() {
std::vector<std::string> input_names;
const framework::proto::OpProto &proto =
framework::OpInfoMap::Instance().Get(type_).Proto();
for (int i = 0; i != proto.inputs_size(); ++i) {
const auto &input = proto.inputs(i);
input_names.push_back(input.name());
}
return input_names;
}
std::vector<std::string> OpTester::GetOpProtoOutputNames() {
std::vector<std::string> output_names;
const framework::proto::OpProto &proto =
framework::OpInfoMap::Instance().Get(type_).Proto();
for (int i = 0; i != proto.outputs_size(); ++i) {
const auto &output = proto.outputs(i);
output_names.push_back(output.name());
}
return output_names;
}
std::unordered_map<std::string, framework::proto::AttrType>
OpTester::GetOpProtoAttrNames() {
std::unordered_map<std::string, framework::proto::AttrType> attr_types;
const framework::proto::OpProto &proto =
framework::OpInfoMap::Instance().Get(type_).Proto();
const std::vector<std::string> skipped_attrs = {
framework::OpProtoAndCheckerMaker::OpRoleAttrName(),
framework::OpProtoAndCheckerMaker::OpRoleVarAttrName(),
framework::OpProtoAndCheckerMaker::OpNamescopeAttrName(),
framework::OpProtoAndCheckerMaker::OpCreationCallstackAttrName(),
framework::OpProtoAndCheckerMaker::OpWithQuantAttrName()};
for (int i = 0; i != proto.attrs_size(); ++i) {
const auto &attr = proto.attrs(i);
if (!Has(skipped_attrs, attr.name())) {
VLOG(4) << "attr: " << attr.name() << ", type: " << attr.type();
attr_types[attr.name()] = attr.type();
}
}
return attr_types;
}
framework::proto::VarType::Type OpTester::TransToVarType(std::string str) {
if (str == "int32") {
return framework::proto::VarType::INT32;
} else if (str == "int64") {
return framework::proto::VarType::INT64;
} else if (str == "fp32") {
return framework::proto::VarType::FP32;
} else if (str == "fp64") {
return framework::proto::VarType::FP64;
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported dtype %s in OpTester.", str.c_str()));
}
}
void OpTester::CreateInputVarDesc() {
std::vector<std::string> input_names = GetOpProtoInputNames();
for (auto &name : input_names) {
const OpInputConfig *input = config_.GetInput(name);
PADDLE_ENFORCE_NOT_NULL(
input,
common::errors::NotFound(
"The input %s of operator %s is not correctly provided.",
name,
config_.op_type));
std::string var_name = config_.op_type + "." + name;
framework::VarDesc *var = Var(var_name);
// Need to support more type
var->SetType(framework::proto::VarType::DENSE_TENSOR);
var->SetPersistable(false);
var->SetDataType(TransToVarType(input->dtype));
var->SetShape(input->dims);
op_desc_.SetInput(name, {var_name});
inputs_[var_name] = *input;
}
}
void OpTester::CreateOutputVarDesc() {
std::vector<std::string> output_names = GetOpProtoOutputNames();
for (auto &name : output_names) {
std::string var_name = config_.op_type + "." + name;
framework::VarDesc *var = Var(var_name);
// Need to support more type
var->SetType(framework::proto::VarType::DENSE_TENSOR);
var->SetPersistable(false);
var->SetDataType(framework::proto::VarType::FP32);
op_desc_.SetOutput(name, {var_name});
}
}
void OpTester::CreateOpDesc() {
op_desc_.SetType(config_.op_type);
std::unordered_map<std::string, framework::proto::AttrType> attr_types =
GetOpProtoAttrNames();
for (auto item : config_.attrs) {
const std::string &name = item.first;
PADDLE_ENFORCE_NE(
attr_types.find(name),
attr_types.end(),
common::errors::NotFound(
"Operator %s does not have attribute %s.", type_, name));
const std::string &value_str = item.second;
const framework::proto::AttrType &type = attr_types[name];
switch (type) {
case framework::proto::AttrType::BOOLEAN:
break;
case framework::proto::AttrType::INT: {
int value = StringTo<int>(value_str);
op_desc_.SetAttr(name, {value});
} break;
case framework::proto::AttrType::FLOAT: {
float value = StringTo<float>(value_str);
op_desc_.SetAttr(name, {value});
} break;
case framework::proto::AttrType::STRING: {
op_desc_.SetAttr(name, {value_str});
} break;
case framework::proto::AttrType::BOOLEANS:
case framework::proto::AttrType::INTS:
case framework::proto::AttrType::FLOATS:
case framework::proto::AttrType::STRINGS:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported STRINGS type in OpTester yet."));
break;
case framework::proto::AttrType::LONG: {
int64_t value = StringTo<int64_t>(value_str);
op_desc_.SetAttr(name, value);
} break;
case framework::proto::AttrType::LONGS:
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported attr type %d in OpTester.", type));
}
}
}
framework::VarDesc *OpTester::Var(const std::string &name) {
auto it = vars_.find(name);
if (it != vars_.end()) {
return it->second.get();
}
auto *var = new framework::VarDesc(name);
vars_[name].reset(var);
return var;
}
template <typename T>
void OpTester::SetupTensor(phi::DenseTensor *tensor,
const std::vector<int64_t> &shape,
T lower,
T upper,
const std::string &initializer,
const std::string &filename) {
static unsigned int seed = 100;
std::mt19937 rng(seed++);
std::uniform_real_distribution<double> uniform_dist(0, 1);
T *ptr = tensor->mutable_data<T>(common::make_ddim(shape), place_);
phi::DenseTensor cpu_tensor;
T *cpu_ptr = nullptr;
if (!phi::is_cpu_place(place_)) {
cpu_ptr =
cpu_tensor.mutable_data<T>(common::make_ddim(shape), phi::CPUPlace());
} else {
cpu_ptr = ptr;
}
if (initializer == "random") {
for (int i = 0; i < cpu_tensor.numel(); ++i) {
cpu_ptr[i] = static_cast<T>(uniform_dist(rng) * (upper - lower) + lower);
}
} else if (initializer == "natural") {
for (int i = 0; i < cpu_tensor.numel(); ++i) {
cpu_ptr[i] = static_cast<T>(lower + i);
}
} else if (initializer == "zeros") {
for (int i = 0; i < cpu_tensor.numel(); ++i) {
cpu_ptr[i] = static_cast<T>(0);
}
} else if (initializer == "file") {
std::ifstream is(filename);
for (int i = 0; i < cpu_tensor.numel(); ++i) {
T value;
is >> value;
cpu_ptr[i] = static_cast<T>(value);
}
is.close();
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported initializer %s in OpTester.", initializer.c_str()));
}
if (!phi::is_cpu_place(place_)) {
::paddle::framework::TensorCopySync(cpu_tensor, place_, tensor);
}
}
void OpTester::CreateVariables(framework::Scope *scope) {
for (auto &item : vars_) {
auto &var = item.second;
if (var->Name() == framework::kEmptyVarName) {
continue;
}
auto *ptr = scope->Var(var->Name());
framework::InitializeVariable(ptr, var->GetType());
if (var->Persistable()) {
VLOG(3) << "Create Variable " << var->Name()
<< " global, which pointer is " << ptr;
} else {
VLOG(3) << "Create Variable " << var->Name()
<< " locally, which pointer is " << ptr;
}
}
for (auto &item : inputs_) {
// Allocate memory for input tensor
auto &var_name = item.first;
VLOG(3) << "Allocate memory for tensor " << var_name;
auto &var_desc = vars_[var_name];
std::vector<int64_t> shape = var_desc->GetShape();
auto *var = scope->Var(var_name);
auto *tensor = var->GetMutable<phi::DenseTensor>();
const auto &data_type = var_desc->GetDataType();
if (data_type == framework::proto::VarType::INT32) {
SetupTensor<int>(
tensor, shape, 0, 1, item.second.initializer, item.second.filename);
} else if (data_type == framework::proto::VarType::INT64) {
SetupTensor<int64_t>(
tensor, shape, 0, 1, item.second.initializer, item.second.filename);
} else if (data_type == framework::proto::VarType::FP32) {
SetupTensor<float>(tensor,
shape,
static_cast<float>(0.0),
static_cast<float>(1.0),
item.second.initializer,
item.second.filename);
} else if (data_type == framework::proto::VarType::FP64) {
SetupTensor<double>(tensor,
shape,
static_cast<double>(0.0),
static_cast<double>(1.0),
item.second.initializer,
item.second.filename);
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported dtype %d in OpTester.", data_type));
}
VLOG(3) << "Set lod for tensor " << var_name;
std::vector<std::vector<size_t>> &lod_vec = item.second.lod;
phi::LegacyLoD lod;
for (auto &item : lod_vec) {
lod.push_back(item);
}
tensor->set_lod(lod);
}
}
static std::string GenSpaces(int count) {
std::stringstream ss;
for (int i = 0; i < count; ++i) {
ss << " ";
}
return ss.str();
}
std::string OpTester::DebugString() {
std::stringstream ss;
int count = 0;
for (auto &item : vars_) {
auto &var = item.second;
ss << GenSpaces(count++) << "vars {\n";
ss << GenSpaces(count) << "name: \"" << var->Name() << "\"\n";
ss << GenSpaces(count++) << "type: {\n";
ss << GenSpaces(count) << "type: DENSE_TENSOR\n";
ss << GenSpaces(count++) << "lod_tensor {\n";
ss << GenSpaces(count++) << "tensor {\n";
const auto &data_type = var->GetDataType();
if (data_type == framework::proto::VarType::INT32) {
ss << GenSpaces(count) << "data_type: INT32\n";
} else if (data_type == framework::proto::VarType::INT64) {
ss << GenSpaces(count) << "data_type: INT64\n";
} else if (data_type == framework::proto::VarType::FP32) {
ss << GenSpaces(count) << "data_type: FP32\n";
} else if (data_type == framework::proto::VarType::FP64) {
ss << GenSpaces(count) << "data_type: FP64\n";
}
std::vector<int64_t> shape = var->GetShape();
for (auto d : shape) {
ss << GenSpaces(count) << "dims: " << d << "\n";
}
ss << GenSpaces(--count) << "}\n";
ss << GenSpaces(--count) << "}\n";
ss << GenSpaces(--count) << "}\n";
ss << GenSpaces(count) << "persistable: " << var->Persistable() << "\n";
ss << GenSpaces(--count) << "}\n";
}
ss << GenSpaces(count++) << "ops {\n";
for (auto &name : op_desc_.InputNames()) {
ss << GenSpaces(count++) << "inputs {\n";
ss << GenSpaces(count) << "parameters: \"" << name << "\"\n";
ss << GenSpaces(count) << "arguments: \"" << op_desc_.Input(name)[0]
<< "\"\n";
ss << GenSpaces(--count) << "}\n";
}
for (auto &name : op_desc_.OutputNames()) {
ss << GenSpaces(count++) << "outputs {\n";
ss << GenSpaces(count) << "parameters: \"" << name << "\"\n";
ss << GenSpaces(count) << "arguments: \"" << op_desc_.Output(name)[0]
<< "\"\n";
ss << GenSpaces(--count) << "}\n";
}
ss << GenSpaces(count) << "type: " << op_desc_.Type() << "\n";
for (auto &name : op_desc_.AttrNames()) {
ss << GenSpaces(count++) << "attrs {\n";
const auto &attr_type = op_desc_.GetAttrType(name);
const auto &attr = op_desc_.GetAttr(name);
ss << GenSpaces(count) << "name: \"" << name << "\"\n";
switch (attr_type) {
case framework::proto::AttrType::BOOLEAN: {
ss << GenSpaces(count) << "type: BOOLEAN\n";
ss << GenSpaces(count) << "b: " << PADDLE_GET_CONST(bool, attr) << "\n";
} break;
case framework::proto::AttrType::INT: {
ss << GenSpaces(count) << "type: INT\n";
ss << GenSpaces(count) << "i: " << PADDLE_GET_CONST(int, attr) << "\n";
} break;
case framework::proto::AttrType::FLOAT: {
ss << GenSpaces(count) << "type: FLOAT\n";
ss << GenSpaces(count) << "f: " << PADDLE_GET_CONST(float, attr)
<< "\n";
} break;
case framework::proto::AttrType::STRING: {
ss << GenSpaces(count) << "type: STRING\n";
ss << GenSpaces(count) << "s: \"" << PADDLE_GET_CONST(std::string, attr)
<< "\"\n";
} break;
case framework::proto::AttrType::BOOLEANS: {
ss << GenSpaces(count) << "type: BOOLEANS\n";
ss << GenSpaces(count) << "bools: "
<< "\n";
} break;
case framework::proto::AttrType::INTS: {
ss << GenSpaces(count) << "type: INTS\n";
ss << GenSpaces(count) << "ints: "
<< "\n";
} break;
case framework::proto::AttrType::FLOATS: {
ss << GenSpaces(count) << "type: FLOATS\n";
ss << GenSpaces(count) << "floats: "
<< "\n";
} break;
case framework::proto::AttrType::STRINGS: {
ss << GenSpaces(count) << "type: STRINGS\n";
ss << GenSpaces(count) << "strings: "
<< "\n";
} break;
case framework::proto::AttrType::LONG: {
ss << GenSpaces(count) << "type: LONG\n";
ss << GenSpaces(count) << "l: " << PADDLE_GET_CONST(int64_t, attr)
<< "\n";
} break;
case framework::proto::AttrType::LONGS: {
ss << GenSpaces(count) << "type: LONGS\n";
ss << GenSpaces(count) << "longs: "
<< "\n";
} break;
default:
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported attr type %d in OpTester.", attr_type));
}
ss << GenSpaces(--count) << "}\n";
}
ss << GenSpaces(--count) << "}\n";
return ss.str();
}
TEST(op_tester, base) {
if (!FLAGS_op_config_list.empty()) {
std::ifstream fin(FLAGS_op_config_list, std::ios::in | std::ios::binary);
PADDLE_ENFORCE_EQ(
static_cast<bool>(fin),
true,
common::errors::InvalidArgument("OpTester cannot open file %s",
FLAGS_op_config_list.c_str()));
std::vector<OpTesterConfig> op_configs;
while (!fin.eof()) {
VLOG(4) << "Reading config " << op_configs.size() << "...";
OpTesterConfig config;
bool result = config.Init(fin);
if (result) {
op_configs.push_back(config);
}
}
if (FLAGS_specified_config_id >= 0 &&
FLAGS_specified_config_id < static_cast<int>(op_configs.size())) {
OpTester tester;
tester.Init(op_configs[FLAGS_specified_config_id]);
tester.Run();
} else {
for (auto &op_config : op_configs) {
OpTester tester;
tester.Init(op_config);
tester.Run();
}
}
} else {
OpTester tester;
OpTesterConfig config;
config.op_type = "elementwise_add";
config.inputs.resize(2);
config.inputs[0].name = "X";
config.inputs[0].dims = {64, 64};
config.inputs[1].name = "Y";
config.inputs[1].dims = {64, 1};
tester.Init(config);
tester.Run();
}
}
} // namespace benchmark
} // namespace operators
} // namespace paddle
+79
View File
@@ -0,0 +1,79 @@
/* Copyright (c) 2016 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. */
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "paddle/common/ddim.h"
#include "paddle/fluid/framework/op_desc.h"
#include "paddle/fluid/framework/operator.h"
#include "test/cpp/fluid/benchmark/op_tester_config.h"
namespace paddle {
namespace operators {
namespace benchmark {
class OpTester {
public:
OpTester() {}
void Init(const std::string &filename);
void Init(const OpTesterConfig &config);
void Run();
std::string DebugString();
private:
std::vector<std::string> GetOpProtoInputNames();
std::vector<std::string> GetOpProtoOutputNames();
std::unordered_map<std::string, framework::proto::AttrType>
GetOpProtoAttrNames();
framework::proto::VarType::Type TransToVarType(std::string str);
void CreateInputVarDesc();
void CreateOutputVarDesc();
void CreateOpDesc();
framework::VarDesc *Var(const std::string &name);
void CreateVariables(framework::Scope *scope);
template <typename T>
void SetupTensor(phi::DenseTensor *input,
const std::vector<int64_t> &shape,
T lower,
T upper,
const std::string &initializer,
const std::string &filename);
void RunImpl();
private:
OpTesterConfig config_;
std::string type_;
framework::OpDesc op_desc_;
std::unordered_map<std::string, std::unique_ptr<framework::VarDesc>> vars_;
std::unordered_map<std::string, OpInputConfig> inputs_;
std::unique_ptr<framework::OperatorBase> op_;
phi::Place place_;
std::unique_ptr<framework::Scope> scope_;
};
} // namespace benchmark
} // namespace operators
} // namespace paddle
@@ -0,0 +1,25 @@
/* Copyright (c) 2016 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 "test/cpp/fluid/benchmark/op_tester_config.h"
#include <fstream>
#include "paddle/fluid/platform/enforce.h"
namespace paddle {
namespace operators {
namespace benchmark {} // namespace benchmark
} // namespace operators
} // namespace paddle
+299
View File
@@ -0,0 +1,299 @@
/* Copyright (c) 2016 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. */
#pragma once
#include <istream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
namespace paddle {
namespace operators {
namespace benchmark {
struct OpInputConfig {
OpInputConfig() {}
explicit OpInputConfig(std::istream& is);
void ParseDType(std::istream& is);
void ParseInitializer(std::istream& is);
void ParseDims(std::istream& is);
void ParseLoD(std::istream& is);
std::string name;
std::string dtype{"fp32"}; // int32/int, int64/long, fp32/float, fp64/double
std::string initializer{"random"}; // random, natural, zeros, file
std::string filename{""};
std::vector<int64_t> dims;
std::vector<std::vector<size_t>> lod;
};
struct OpTesterConfig {
OpTesterConfig() {}
explicit OpTesterConfig(const std::string& filename);
bool Init(std::istream& is);
bool ParseAttrs(std::istream& is);
const OpInputConfig* GetInput(const std::string& name);
std::string op_type;
std::vector<OpInputConfig> inputs;
std::unordered_map<std::string, std::string> attrs;
int device_id{-1}; // CPU: -1
int repeat{1};
int profile{0};
int print_debug_string{0};
double runtime{0.0};
};
static bool Has(const std::vector<std::string>& vec, const std::string& item) {
for (size_t i = 0; i < vec.size(); ++i) {
if (vec[i] == item) {
return true;
}
}
return false;
}
template <typename T>
T StringTo(const std::string& str) {
std::istringstream is(str);
T value;
is >> value;
return value;
}
static const char kStartSeparator[] = "{";
static const char kEndSeparator[] = "}";
static const char kSepBetweenItems[] = ";";
static bool StartWith(const std::string& str, const std::string& substr) {
return str.find(substr) == 0;
}
static bool EndWith(const std::string& str, const std::string& substr) {
return str.rfind(substr) == (str.length() - substr.length());
}
static void EraseEndSep(std::string* str,
std::string substr = kSepBetweenItems) {
if (EndWith(*str, substr)) {
str->erase(str->length() - substr.length(), str->length());
}
}
OpInputConfig::OpInputConfig(std::istream& is) {
std::string sep;
is >> sep;
if (sep == kStartSeparator) {
while (sep != kEndSeparator) {
is >> sep;
if (sep == "name" || sep == "name:") {
is >> name;
EraseEndSep(&name);
} else if (sep == "dtype" || sep == "dtype:") {
ParseDType(is);
} else if (sep == "initializer" || sep == "initializer:") {
ParseInitializer(is);
} else if (sep == "dims" || sep == "dims:") {
ParseDims(is);
} else if (sep == "lod" || sep == "lod:") {
ParseLoD(is);
} else if (sep == "filename") {
is >> filename;
EraseEndSep(&filename);
}
}
}
}
void OpInputConfig::ParseDType(std::istream& is) {
std::string dtype_str;
is >> dtype_str;
EraseEndSep(&dtype_str);
if (dtype_str == "int32" || dtype_str == "int") {
dtype = "int32";
} else if (dtype_str == "int64" || dtype_str == "long") {
dtype = "int64";
} else if (dtype_str == "fp32" || dtype_str == "float") {
dtype = "fp32";
} else if (dtype_str == "fp64" || dtype_str == "double") {
dtype = "fp64";
} else {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported dtype %s in OpInputConfig.", dtype_str.c_str()));
}
VLOG(4) << "dtype of input " << name << " is: " << dtype;
}
void OpInputConfig::ParseInitializer(std::istream& is) {
std::string initializer_str;
is >> initializer_str;
EraseEndSep(&initializer_str);
const std::vector<std::string> supported_initializers = {
"random", "natural", "zeros", "file"};
if (!Has(supported_initializers, initializer_str)) {
PADDLE_THROW(common::errors::Unimplemented(
"Unsupported initializer %s in OpInputConfig.",
initializer_str.c_str()));
}
initializer = initializer_str;
VLOG(4) << "initializer of input " << name << " is: " << initializer;
}
void OpInputConfig::ParseDims(std::istream& is) {
std::string dims_str;
is >> dims_str;
dims.clear();
std::string token;
std::istringstream token_stream(dims_str);
while (std::getline(token_stream, token, 'x')) {
dims.push_back(std::stoi(token));
}
}
void OpInputConfig::ParseLoD(std::istream& is) {
std::string lod_str;
std::string start_sep =
std::string(kStartSeparator) + std::string(kStartSeparator);
std::string end_sep = std::string(kEndSeparator) + std::string(kEndSeparator);
std::string sep;
is >> sep;
if (StartWith(sep, start_sep)) {
lod_str += sep;
while (!EndWith(sep, end_sep)) {
is >> sep;
lod_str += sep;
}
}
EraseEndSep(&lod_str);
PADDLE_ENFORCE_GE(
lod_str.length(),
4U,
common::errors::InvalidArgument(
"The length of lod string should be "
"equal to or larger than 4. But length of lod string is %zu.",
lod_str.length()));
VLOG(4) << "lod: " << lod_str << ", length: " << lod_str.length();
// Parse the lod_str
lod.clear();
for (size_t i = 1; i < lod_str.length() - 1;) {
if (lod_str[i] == '{') {
std::vector<size_t> level;
while (lod_str[i] != '}') {
++i;
std::string number;
while (lod_str[i] >= '0' && lod_str[i] <= '9') {
number += lod_str[i];
++i;
}
level.push_back(StringTo<size_t>(number));
}
lod.push_back(level);
} else if (lod_str[i] == '}') {
++i;
}
}
}
OpTesterConfig::OpTesterConfig(const std::string& filename) {
std::ifstream fin(filename, std::ios::in | std::ios::binary);
PADDLE_ENFORCE_EQ(
static_cast<bool>(fin),
true,
common::errors::InvalidArgument("OpTesterConfig cannot open file %s.",
filename.c_str()));
Init(fin);
}
bool OpTesterConfig::Init(std::istream& is) {
std::string sep;
is >> sep;
if (sep == kStartSeparator) {
while (sep != kEndSeparator) {
is >> sep;
if (sep == "op_type" || sep == "op_type:") {
is >> op_type;
} else if (sep == "device_id" || sep == "device_id:") {
is >> device_id;
} else if (sep == "repeat" || sep == "repeat:") {
is >> repeat;
} else if (sep == "profile" || sep == "profile:") {
is >> profile;
} else if (sep == "print_debug_string" || sep == "print_debug_string:") {
is >> print_debug_string;
} else if (sep == "input" || sep == "input:") {
OpInputConfig input_config(is);
inputs.push_back(input_config);
} else if (sep == "attrs" || sep == "attrs:") {
ParseAttrs(is);
} else {
if (sep != kEndSeparator) {
return false;
}
}
}
} else {
return false;
}
return true;
}
bool OpTesterConfig::ParseAttrs(std::istream& is) {
std::string sep;
is >> sep;
if (sep == kStartSeparator) {
while (true) {
std::string key;
is >> key;
if (key == kEndSeparator) {
break;
}
std::string value;
is >> value;
EraseEndSep(&key, ":");
EraseEndSep(&value);
VLOG(4) << "attrs: " << key << ", " << value;
attrs[key] = value;
}
}
return true;
}
const OpInputConfig* OpTesterConfig::GetInput(const std::string& name) {
for (size_t i = 0; i < inputs.size(); ++i) {
if (inputs[i].name == name) {
return &inputs[i];
}
}
return nullptr;
}
} // namespace benchmark
} // namespace operators
} // namespace paddle
@@ -0,0 +1,7 @@
paddle_test(conditional_block_op_test SRCS conditional_block_op_test.cc)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(conditional_block_op_test)
endif()
@@ -0,0 +1,73 @@
/* Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/operators/controlflow/conditional_block_op.h"
#include "gtest/gtest.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/scope.h"
using DenseTensorArray = phi::TensorArray;
using Scope = paddle::framework::Scope;
using Variable = paddle::framework::Variable;
using Place = phi::Place;
TEST(ConditionalBlockGrad, NoNeedRunDenseTensorArray) {
Place place = phi::CPUPlace();
Scope scope;
Variable* cond_var = scope.Var("condition");
phi::DenseTensor* cond_tensor = cond_var->GetMutable<phi::DenseTensor>();
phi::DDim cond_dims = common::make_ddim({1});
bool* cond_data = cond_tensor->mutable_data<bool>(cond_dims, place);
cond_data[0] = false;
Variable* input_var = scope.Var("input_lod_tensor_array");
DenseTensorArray* input_tensors = input_var->GetMutable<phi::TensorArray>();
for (int i = 0; i < 5; ++i) {
phi::DDim in_dims = common::make_ddim({i + 1, i + 2});
phi::DenseTensor lod_tensor;
float* in_data = lod_tensor.mutable_data<float>(in_dims, place);
for (int j = 0; j < (i + 1) * (i + 2); ++j) {
in_data[j] = static_cast<float>(j);
}
input_tensors->push_back(lod_tensor);
}
Variable* input_grad_var = scope.Var("input_lod_tensor_array@GRAD");
DenseTensorArray* grad_tensors =
input_grad_var->GetMutable<phi::TensorArray>();
grad_tensors->resize(5);
paddle::framework::AttributeMap attrs;
attrs.insert({"is_scalar_condition", true});
auto conditional_grad_op = paddle::framework::OpRegistry::CreateOp(
"conditional_block_grad",
{{"Input", {"input_lod_tensor_array"}}, {"Cond", {"condition"}}},
{{"Input@GRAD", {"input_lod_tensor_array@GRAD"}}},
attrs);
conditional_grad_op->Run(scope, place);
const DenseTensorArray& out_tensors = input_grad_var->Get<phi::TensorArray>();
for (int i = 0; i < 5; ++i) {
phi::DDim out_dims = out_tensors[i].dims();
EXPECT_EQ(common::make_ddim({i + 1, i + 2}), out_dims);
const float* out_data = out_tensors[i].data<float>();
for (int j = 0; j < (i + 1) * (i + 2); ++j) {
EXPECT_EQ(0, out_data[j]);
}
}
}
+104
View File
@@ -0,0 +1,104 @@
/* Copyright (c) 2016 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. */
#ifndef _WIN32
#include <unistd.h>
#endif
#include <string>
#include <thread> // NOLINT
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/phi/kernels/funcs/math_function.h"
#include "paddle/utils/string/printf.h"
namespace f = paddle::framework;
namespace p = paddle::platform;
USE_OP_ITSELF(dropout);
void Compare(f::Scope* scope, const p::DeviceContext& ctx) {
// init
auto var = scope->Var("X");
auto tensor = var->GetMutable<phi::DenseTensor>();
tensor->Resize({10, 10});
std::vector<float> init;
for (int64_t i = 0; i < 10 * 10; ++i) {
init.push_back(1.0);
}
paddle::framework::TensorFromVector(init, ctx, tensor);
auto place = ctx.GetPlace();
auto out_var = scope->Var("Out");
auto out_tensor = out_var->GetMutable<phi::DenseTensor>();
out_tensor->Resize({10, 10});
out_tensor->mutable_data<float>(place); // allocate
auto mask_var = scope->Var("Mask");
auto mask_tensor = mask_var->GetMutable<phi::DenseTensor>();
mask_tensor->Resize({10, 10});
mask_tensor->mutable_data<float>(place); // allocate
// run
f::AttributeMap attrs;
float dropout_prob = 0.5;
attrs.insert({"fix_seed", 1});
attrs.insert({"seed", 3});
attrs.insert({"dropout_prob", dropout_prob});
auto dropout_op = f::OpRegistry::CreateOp(
"dropout", {{"X", {"X"}}}, {{"Out", {"Out"}}, {"Mask", {"Mask"}}}, attrs);
dropout_op->Run(*scope, place);
std::vector<float> out_vec;
paddle::framework::TensorToVector(*out_tensor, ctx, &out_vec);
std::vector<float> std_out = {
0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1,
1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1,
1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,
1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1};
EXPECT_EQ(out_vec.size(), std_out.size());
for (uint32_t i = 0; i < out_vec.size(); i++) {
EXPECT_EQ(out_vec[i], std_out[i]);
}
}
// TODO(wyi): Due to
// https://github.com/PaddlePaddle/Paddle/issues/9507, I temporarily
// disable this test to remove the prevention of the merge of
// unrelated PRs.
/*
TEST(Dropout, CPUDense) {
f::Scope scope;
phi::CPUPlace place;
phi::CPUContext ctx(place);
Compare(scope, ctx);
}
TEST(Dropout, GPUDense) {
f::Scope scope;
phi::GPUPlace place;
phi::GPUContext ctx(place);
Compare(scope, ctx);
}
*/
+12
View File
@@ -0,0 +1,12 @@
nv_test(
test_elementwise_add_op_inplace
SRCS test_elementwise_add_op_inplace.cc
DEPS executor op_registry elementwise_add_op scope phi common)
cc_test(
test_elementwise_div_grad_grad
SRCS test_elementwise_div_grad_grad.cc
DEPS executor op_registry elementwise_div_op scope phi common)
cc_test(
test_elementwise_add_grad_grad
SRCS test_elementwise_add_grad_grad.cc
DEPS executor op_registry elementwise_add_op scope phi common)
@@ -0,0 +1,83 @@
// Copyright (c) 2019 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/common/ddim.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_registry.h"
#include "test/cpp/fluid/elementwise/test_elementwise_op_grad_grad.h"
USE_OP_ITSELF(elementwise_add);
PD_DECLARE_KERNEL(add_double_grad, CPU, ALL_LAYOUT);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
PD_DECLARE_KERNEL(add_double_grad, GPU, ALL_LAYOUT);
#endif
namespace paddle {
namespace operators {
template <typename T>
class TestElementwiseAddGradGradWithoutDDX
: public TestElementwiseOpGradGrad<T> {
public:
TestElementwiseAddGradGradWithoutDDX(const phi::Place &place,
const phi::DDim &dims)
: TestElementwiseOpGradGrad<T>("elementwise_add_grad_grad",
place,
dims,
{"Y", "DOut", "DDY"},
{"DDOut"}) {}
using TestElementwiseOpGradGrad<T>::feed_datas_;
using TestElementwiseOpGradGrad<T>::expected_outs_;
using TestElementwiseOpGradGrad<T>::dims_;
void ComputeExpectedOuts() override {
size_t numel = static_cast<size_t>(common::product(dims_));
std::vector<T> dy(numel);
std::vector<T> ddout(numel);
for (size_t i = 0; i < numel; ++i) {
// ddOut = ddX + ddY = ddY if ddX empty
ddout[i] = feed_datas_["DDY"][i];
}
expected_outs_["DDOut"] = ddout;
}
std::unique_ptr<framework::OperatorBase> CreateTestOp() override {
auto op = framework::OpRegistry::CreateOp(
this->op_type_,
{{"Y", {"Y"}}, {"DOut", {"DOut"}}, {"DDY", {"DDY"}}},
{{"DDOut", {"DDOut"}}},
{{"use_onednn", false}, {"axis", 0}});
return op;
}
};
TEST(test_elementwise_add_grad_grad_without_ddx, cpu_place) {
phi::DDim dims({32, 64});
phi::CPUPlace p;
TestElementwiseAddGradGradWithoutDDX<float> test(p, dims);
ASSERT_TRUE(test.Check());
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
TEST(test_elementwise_add_grad_grad_without_ddx, gpu_place) {
phi::DDim dims({32, 64});
phi::GPUPlace p(0);
TestElementwiseAddGradGradWithoutDDX<float> test(p, dims);
ASSERT_TRUE(test.Check());
}
#endif
} // namespace operators
} // namespace paddle
@@ -0,0 +1,157 @@
// Copyright (c) 2019 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 <random>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/platform/enforce.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/platform/device_context.h"
USE_OP_ITSELF(elementwise_add);
PD_DECLARE_KERNEL(add, CPU, ALL_LAYOUT);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
PD_DECLARE_KERNEL(add, KPS, ALL_LAYOUT);
#endif
namespace paddle {
namespace operators {
static void Memcpy(void *dst, const void *src, size_t n, bool copy_to_gpu) {
if (copy_to_gpu) {
#ifdef PADDLE_WITH_CUDA
PADDLE_ENFORCE_GPU_SUCCESS(cudaMemcpy(dst, src, n, cudaMemcpyHostToDevice));
#elif defined(PADDLE_WITH_HIP)
PADDLE_ENFORCE_GPU_SUCCESS(hipMemcpy(dst, src, n, hipMemcpyHostToDevice));
#else
PADDLE_THROW(
common::errors::InvalidArgument("Check your paddle version, current "
"version is not compiled with cuda"));
#endif
} else {
std::memcpy(dst, src, n);
}
}
template <typename T>
bool TestMain(const phi::Place &place, const phi::DDim &dims, bool inplace) {
framework::Scope scope;
auto *x = scope.Var("x")->GetMutable<phi::DenseTensor>();
auto *y = scope.Var("y")->GetMutable<phi::DenseTensor>();
auto *z = scope.Var("z")->GetMutable<phi::DenseTensor>();
x->Resize(dims);
y->Resize(dims);
z->Resize(dims);
size_t numel = static_cast<size_t>(common::product(dims));
auto x_ptr = x->mutable_data<T>(place);
auto y_ptr = y->mutable_data<T>(place);
auto z_ptr = z->mutable_data<T>(place);
std::uniform_real_distribution<T> dist(static_cast<T>(10.0),
static_cast<T>(20.0));
std::mt19937 engine;
std::vector<T> x_data(numel), y_data(numel), z_data(numel);
std::vector<T> sum_result(numel);
for (size_t i = 0; i < numel; ++i) {
x_data[i] = dist(engine);
y_data[i] = dist(engine);
sum_result[i] = x_data[i] + y_data[i];
z_data[i] = -1.0; // set some data that is not existed
}
auto bytes = sizeof(T) * numel;
bool is_gpu_place = phi::is_gpu_place(place);
Memcpy(x_ptr, x_data.data(), bytes, is_gpu_place);
Memcpy(y_ptr, y_data.data(), bytes, is_gpu_place);
Memcpy(z_ptr, z_data.data(), bytes, is_gpu_place);
const char *out_name = inplace ? "x" : "z";
auto op = framework::OpRegistry::CreateOp("elementwise_add",
{{"X", {"x"}}, {"Y", {"y"}}},
{{"Out", {out_name}}},
{});
op->Run(scope, place);
phi::DeviceContextPool::Instance().Get(place)->Wait();
phi::DenseTensor cpu_out;
auto &out_tensor = scope.FindVar(out_name)->Get<phi::DenseTensor>();
PADDLE_ENFORCE_EQ(
scope.kids().empty(),
true,
common::errors::InvalidArgument("The scope can not have the child scopes,"
"please check your code."));
if (inplace) {
PADDLE_ENFORCE_EQ(
&out_tensor,
x,
common::errors::InvalidArgument(
"The output tensor should be same as input x in inplace mode,"
" but now is not same."));
} else {
PADDLE_ENFORCE_EQ(
&out_tensor,
z,
common::errors::InvalidArgument(
"The output tensor should be same as output z in normal mode,"
" but now is not same."));
}
if (is_gpu_place) {
framework::TensorCopySync(out_tensor, phi::CPUPlace(), &cpu_out);
} else {
cpu_out = out_tensor;
}
auto *out_ptr = cpu_out.data<T>();
bool is_equal = std::equal(out_ptr, out_ptr + numel, sum_result.data());
return is_equal;
}
TEST(test_elementwise_add_inplace, cpu_place) {
phi::DDim dims({32, 64});
phi::CPUPlace p;
ASSERT_TRUE(TestMain<float>(p, dims, true));
}
TEST(test_elementwise_add_not_inplace, cpu_place) {
phi::DDim dims({32, 64});
phi::CPUPlace p;
ASSERT_TRUE(TestMain<float>(p, dims, false));
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
TEST(test_elementwise_add_inplace, gpu_place) {
phi::DDim dims({32, 64});
phi::GPUPlace p(0);
ASSERT_TRUE(TestMain<float>(p, dims, true));
}
TEST(test_elementwise_add_not_inplace, gpu_place) {
phi::DDim dims({32, 64});
phi::GPUPlace p(0);
ASSERT_TRUE(TestMain<float>(p, dims, false));
}
#endif
} // namespace operators
} // namespace paddle
@@ -0,0 +1,112 @@
// Copyright (c) 2019 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 <algorithm>
#include <cstdlib>
#include <memory>
#include <random>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/platform/enforce.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/core/platform/device_context.h"
#include "test/cpp/fluid/elementwise/test_elementwise_op_grad_grad.h"
USE_OP_ITSELF(elementwise_div);
PD_DECLARE_KERNEL(divide_double_grad, CPU, ALL_LAYOUT);
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
PD_DECLARE_KERNEL(divide_double_grad, GPU, ALL_LAYOUT);
#endif
namespace paddle {
namespace operators {
template <typename T>
class TestElementwiseDivGradGradWithDout : public TestElementwiseOpGradGrad<T> {
public:
TestElementwiseDivGradGradWithDout(const phi::Place &place,
const phi::DDim &dims)
: TestElementwiseOpGradGrad<T>(
"elementwise_div_grad_grad",
place,
dims,
{"Y", "Out", "Out@GRAD", "DDX", "DDY", "DX"},
{"Y@GRAD", "DDOut", "DOut"}) {}
using TestElementwiseOpGradGrad<T>::feed_datas_;
using TestElementwiseOpGradGrad<T>::expected_outs_;
using TestElementwiseOpGradGrad<T>::dims_;
void ComputeExpectedOuts() override {
size_t numel = static_cast<size_t>(common::product(dims_));
std::vector<T> dy(numel);
std::vector<T> ddout(numel);
std::vector<T> dout(numel);
for (size_t i = 0; i < numel; ++i) {
// dY(Y@GRAD) = Out * dX * ddY / Y - dX * ddX / Y
dy[i] = (feed_datas_["DX"][i] / feed_datas_["Y"][i]) *
(feed_datas_["Out"][i] * feed_datas_["DDY"][i] -
feed_datas_["DDX"][i]);
// ddOut = ddX / Y - Out * ddY / Y = (ddX - Out * ddY) / Y
ddout[i] = (feed_datas_["DDX"][i] -
feed_datas_["Out"][i] * feed_datas_["DDY"][i]) /
(feed_datas_["Y"][i]);
// dOut = - DX * DDy
dout[i] = -feed_datas_["DX"][i] * feed_datas_["DDY"][i];
}
expected_outs_["Y@GRAD"] = dy;
expected_outs_["DDOut"] = ddout;
expected_outs_["DOut"] = dout;
}
std::unique_ptr<framework::OperatorBase> CreateTestOp() override {
auto op = framework::OpRegistry::CreateOp(
this->op_type_,
{{"Y", {"Y"}},
{"Out", {"Out"}},
{"Out@GRAD", {"Out@GRAD"}},
{"DDX", {"DDX"}},
{"DDY", {"DDY"}},
{"DX", {"DX"}}},
{{"Y@GRAD", {"Y@GRAD"}}, {"DDOut", {"DDOut"}}, {"DOut", {"DOut"}}},
{{"use_onednn", false}, {"axis", 0}});
return op;
}
};
TEST(test_elementwise_div_grad_grad, cpu_place) {
phi::DDim dims({32, 64});
phi::CPUPlace p;
TestElementwiseDivGradGradWithDout<float> test(p, dims);
ASSERT_TRUE(test.Check());
}
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
TEST(test_elementwise_div_grad_grad, gpu_place) {
phi::DDim dims({32, 64});
phi::GPUPlace p(0);
TestElementwiseDivGradGradWithDout<float> test(p, dims);
ASSERT_TRUE(test.Check());
}
#endif
} // namespace operators
} // namespace paddle
@@ -0,0 +1,176 @@
// Copyright (c) 2019 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.
#pragma once
#include <algorithm>
#include <cstdlib>
#include <map>
#include <memory>
#include <random>
#include <string>
#include <vector>
#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/platform/enforce.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/memory/memory.h"
#include "paddle/phi/core/platform/device_context.h"
namespace paddle {
namespace operators {
// currently, this test class only support same dims
template <typename T>
class TestElementwiseOpGradGrad {
public:
TestElementwiseOpGradGrad(const std::string &op_type,
const phi::Place &place,
const phi::DDim &dims,
const std::vector<std::string> &inputs,
const std::vector<std::string> &outputs)
: op_type_(op_type),
place_(place),
dims_(dims),
inputs_(inputs),
outputs_(outputs) {}
void InitVarInScope(std::string var_name) {
in_out_tensors_[var_name] =
scope_.Var(var_name)->template GetMutable<phi::DenseTensor>();
in_out_tensors_[var_name]->Resize(dims_);
in_out_tensors_[var_name]->template mutable_data<T>(place_);
}
void InitFeedData(std::string var_name, size_t size) {
// generate random data
std::uniform_real_distribution<T> dist(static_cast<T>(10.0),
static_cast<T>(20.0));
std::mt19937 engine;
std::vector<T> data(size);
for (size_t i = 0; i < size; ++i) {
data[i] = dist(engine);
}
feed_datas_[var_name] = data;
}
void Setup() {
size_t numel = static_cast<size_t>(common::product(dims_));
// init vars in scope and feed inputs
for (auto in_name : inputs_) {
InitVarInScope(in_name);
InitFeedData(in_name, numel);
}
for (auto out_name : outputs_) {
InitVarInScope(out_name);
}
// feeding: copy data to tensor, out tensor don't need init
auto bytes = sizeof(T) * numel;
for (auto &in_name : inputs_) {
auto dst = in_out_tensors_[in_name]->template data<T>();
auto src = feed_datas_[in_name].data();
auto src_place = phi::CPUPlace();
if (phi::is_cpu_place(place_)) {
auto dst_place = place_;
memory::Copy(dst_place, dst, src_place, src, bytes);
} else if (phi::is_gpu_place(place_)) {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
auto dst_place = place_;
memory::Copy(dst_place, dst, src_place, src, bytes, nullptr);
#else
PADDLE_THROW(common::errors::InvalidArgument(
"Check your paddle version, current version is not compiled with "
"cuda"));
#endif
}
}
// calculate expected outputs
ComputeExpectedOuts();
}
bool Check() {
Setup();
auto op = CreateTestOp();
op->Run(scope_, place_);
phi::DeviceContextPool::Instance().Get(place_)->Wait();
phi::DenseTensor cpu_out;
PADDLE_ENFORCE_EQ(scope_.kids().empty(),
true,
common::errors::InvalidArgument(
"The scope can not have the child scopes,"
"please check your code."));
// get outputs from scope and compare them with expected_outs
bool all_equal = true;
for (auto &out_name : outputs_) {
auto &out_tensor =
scope_.FindVar(out_name)->template Get<phi::DenseTensor>();
if (phi::is_gpu_place(place_)) {
framework::TensorCopySync(out_tensor, phi::CPUPlace(), &cpu_out);
} else {
cpu_out = out_tensor;
}
auto *out_ptr = cpu_out.data<T>();
size_t numel = static_cast<size_t>(common::product(dims_));
bool is_equal;
if (op_type_ == "elementwise_div_grad_grad") {
is_equal = std::equal(out_ptr,
out_ptr + numel,
expected_outs_[out_name].data(),
[](const float &l, const float &r) {
return fabs(l - r) < 0.0005;
});
} else {
#ifdef PADDLE_WITH_HIP
is_equal = std::equal(
out_ptr,
out_ptr + numel,
expected_outs_[out_name].data(),
[](const float &l, const float &r) { return fabs(l - r) < 1e-8; });
#else
is_equal = std::equal(
out_ptr, out_ptr + numel, expected_outs_[out_name].data());
#endif
}
if (!is_equal) {
all_equal = false;
break;
}
}
return all_equal;
}
virtual std::unique_ptr<framework::OperatorBase> CreateTestOp() = 0;
virtual void ComputeExpectedOuts() = 0;
virtual ~TestElementwiseOpGradGrad() {}
protected:
std::string op_type_;
phi::Place place_;
phi::DDim dims_;
std::vector<std::string> inputs_;
std::vector<std::string> outputs_;
std::map<std::string, phi::DenseTensor *> in_out_tensors_;
std::map<std::string, std::vector<T>> feed_datas_;
std::map<std::string, std::vector<T>> expected_outs_;
framework::Scope scope_;
};
} // namespace operators
} // namespace paddle
+230
View File
@@ -0,0 +1,230 @@
if(WIN32)
remove_definitions(-DPADDLE_DLL_EXPORT)
endif()
add_subdirectory(details)
add_subdirectory(ir)
paddle_test(data_type_test SRCS data_type_test.cc)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(data_type_test)
endif()
nv_test(
tensor_test
SRCS tensor_test.cc
DEPS tensor)
if(WITH_GPU)
nv_test(
tensor_util_test
SRCS tensor_util_test.cc tensor_util_test.cu
DEPS tensor dlpack_tensor)
elseif(WITH_ROCM)
hip_test(
tensor_util_test
SRCS tensor_util_test.cc tensor_util_test.cu
DEPS tensor dlpack_tensor)
else()
nv_test(
tensor_util_test
SRCS tensor_util_test.cc
DEPS tensor dlpack_tensor)
endif()
nv_test(
copy_same_tensor_test
SRCS copy_same_tensor_test.cc
DEPS tensor)
paddle_test(eigen_test SRCS eigen_test.cc)
paddle_test(lod_tensor_test SRCS lod_tensor_test.cc DEPS common)
if(WITH_GPU)
nv_test(
lod_tensor_gpu_test
SRCS lod_tensor_test.cu
DEPS lod_tensor)
elseif(WITH_ROCM)
hip_test(
lod_tensor_gpu_test
SRCS lod_tensor_test.cu
DEPS lod_tensor)
endif()
paddle_test(reader_test SRCS reader_test.cc)
paddle_test(threadpool_test SRCS threadpool_test.cc DEPS common)
paddle_test(var_type_traits_test SRCS var_type_traits_test.cc)
paddle_test(device_worker_test SRCS device_worker_test.cc)
paddle_test(scope_test SRCS scope_test.cc)
paddle_test(variable_test SRCS variable_test.cc)
if(WITH_GPU)
nv_test(
data_device_transform_test
SRCS data_device_transform_test.cu
DEPS operator op_registry phi common scope)
elseif(WITH_ROCM)
hip_test(
data_device_transform_test
SRCS data_device_transform_test.cu
DEPS operator op_registry phi common scope)
endif()
if(WITH_GPU)
nv_test(
data_type_transform_test
SRCS data_type_transform_test.cc data_type_transform_test.cu
DEPS data_type_transform)
elseif(WITH_ROCM)
hip_test(
data_type_transform_test
SRCS data_type_transform_test.cc data_type_transform_test.cu
DEPS data_type_transform)
elseif(WITH_XPU)
paddle_test(data_type_transform_test SRCS data_type_transform_test.cc)
else()
paddle_test(data_type_transform_test SRCS data_type_transform_test.cc)
endif()
paddle_test(data_layout_transform_test SRCS data_layout_transform_test.cc)
paddle_test(attribute_test SRCS attribute_test.cc)
paddle_test(program_desc_test SRCS program_desc_test.cc)
paddle_test(op_desc_test SRCS op_desc_test.cc)
cc_test(
op_version_registry_test
SRCS op_version_registry_test.cc
DEPS op_version_registry)
cc_test(
op_proto_maker_test
SRCS op_proto_maker_test.cc
DEPS op_proto_maker)
cc_test(
no_need_buffer_vars_inference_test
SRCS no_need_buffer_vars_inference_test.cc
DEPS no_need_buffer_vars_inference layer)
cc_test(
operator_test
SRCS operator_test.cc
DEPS operator op_registry phi)
cc_test(
operator_exception_test
SRCS operator_exception_test.cc
DEPS operator op_registry phi)
cc_test(
version_test
SRCS version_test.cc
DEPS version)
cc_test(
op_call_stack_test
SRCS op_call_stack_test.cc
DEPS op_call_stack)
cc_test(
program_utils_test
SRCS program_utils_test.cc
DEPS proto_desc program_utils)
if(WITH_GPU)
nv_test(
op_registry_test
SRCS op_registry_test.cc
DEPS op_registry)
elseif(WITH_ROCM)
hip_test(
op_registry_test
SRCS op_registry_test.cc
DEPS op_registry)
endif()
cc_test(
dist_multi_trainer_test
SRCS dist_multi_trainer_test.cc
DEPS conditional_block_op executor gloo_wrapper)
cc_test(
prune_test
SRCS prune_test.cc
DEPS op_info prune phi)
cc_test(
var_type_inference_test
SRCS var_type_inference_test.cc
DEPS op_registry proto_desc)
cc_test(
selected_rows_utils_test
SRCS selected_rows_utils_test.cc
DEPS selected_rows_utils)
cc_test(
op_kernel_type_test
SRCS op_kernel_type_test.cc
DEPS phi common framework_proto op_kernel_type)
cc_test(tuple_test SRCS tuple_test.cc)
cc_test(inlined_vector_test SRCS inlined_vector_test.cc)
cc_test(
dlpack_tensor_test
SRCS dlpack_tensor_test.cc
DEPS dlpack_tensor glog)
cc_test(
infershape_utils_test
SRCS infershape_utils_test.cc
DEPS operator phi)
if(WITH_TESTING AND TEST selected_rows_utils_test)
set_tests_properties(selected_rows_utils_test PROPERTIES TIMEOUT 120)
endif()
cc_test(scope_guard_test SRCS scope_guard_test.cc)
cc_test(
phi_utils_test
SRCS phi_utils_test.cc
DEPS phi_utils)
cc_test(convert_utils_test SRCS convert_utils_test.cc)
cc_test(
test_fs
SRCS io/test_fs.cc
DEPS framework_io string_helper)
if(WITH_CRYPTO)
cc_test(
aes_cipher_test
SRCS io/aes_cipher_test.cc
DEPS framework_io)
cc_test(
cipher_utils_test
SRCS io/cipher_utils_test.cc
DEPS framework_io)
endif()
cc_test(
test_fleet_cc
SRCS fleet/test_fleet.cc
DEPS fleet_wrapper gloo_wrapper framework_io string_helper)
cc_test(
workqueue_test
SRCS new_executor/workqueue_test.cc
DEPS standalone_executor)
+363
View File
@@ -0,0 +1,363 @@
// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/attribute.h"
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/framework/var_desc.h"
#include "paddle/phi/common/scalar.h"
#include "paddle/utils/any.h"
TEST(Attribute, GetAttrValueToAny) {
paddle::framework::Attribute x_int(100);
auto rlt_int = paddle::framework::GetAttrValue(x_int);
EXPECT_EQ(paddle::any_cast<int>(rlt_int), 100);
float float_value = 3.14;
paddle::framework::Attribute x_float(float_value);
auto rlt_float = paddle::framework::GetAttrValue(x_float);
EXPECT_NEAR(paddle::any_cast<float>(rlt_float), 3.14, 1e-6);
std::string str_value("test");
paddle::framework::Attribute x_str(str_value);
auto rlt_str = paddle::framework::GetAttrValue(x_str);
EXPECT_EQ(paddle::any_cast<std::string>(rlt_str), "test");
std::vector<int> vec_int_var(2, 100);
paddle::framework::Attribute x_vec_int = vec_int_var;
auto rlt_vec_int = paddle::framework::GetAttrValue(x_vec_int);
auto vec_int = paddle::any_cast<std::vector<int>>(rlt_vec_int);
EXPECT_EQ(vec_int.size(), 2UL);
EXPECT_EQ(vec_int[0], 100);
EXPECT_EQ(vec_int[1], 100);
std::vector<float> vec_float_var(2, 3.14);
paddle::framework::Attribute x_vec_float = vec_float_var;
auto rlt_vec_float = paddle::framework::GetAttrValue(x_vec_float);
auto vec_float = paddle::any_cast<std::vector<float>>(rlt_vec_float);
EXPECT_EQ(vec_float.size(), 2UL);
EXPECT_NEAR(vec_float[0], 3.14, 1e-6);
EXPECT_NEAR(vec_float[1], 3.14, 1e-6);
std::vector<std::string> vec_str_var(2, "test");
paddle::framework::Attribute x_vec_str = vec_str_var;
auto rlt_vec_str = paddle::framework::GetAttrValue(x_vec_str);
auto vec_str = paddle::any_cast<std::vector<std::string>>(rlt_vec_str);
EXPECT_EQ(vec_str.size(), 2UL);
EXPECT_EQ(vec_str[0], "test");
EXPECT_EQ(vec_str[1], "test");
paddle::framework::Attribute x_bool(true);
auto rlt_bool = paddle::framework::GetAttrValue(x_bool);
EXPECT_EQ(paddle::any_cast<bool>(rlt_bool), true);
std::vector<bool> vec_bool_var(2, true);
paddle::framework::Attribute x_vec_bool = vec_bool_var;
auto rlt_vec_bool = paddle::framework::GetAttrValue(x_vec_bool);
auto vec_bool = paddle::any_cast<std::vector<bool>>(rlt_vec_bool);
EXPECT_EQ(vec_bool.size(), 2UL);
EXPECT_EQ(vec_bool[0], true);
EXPECT_EQ(vec_bool[1], true);
paddle::framework::VarDesc var_desc("axis");
paddle::framework::Attribute var_attr(&var_desc);
auto rlt_var_attr = paddle::framework::GetAttrValue(var_attr);
auto var_desc_ptr =
paddle::any_cast<paddle::framework::VarDesc *>(rlt_var_attr);
EXPECT_NE(var_desc_ptr, nullptr);
EXPECT_EQ(var_desc_ptr->Name(), var_desc.Name());
paddle::framework::VarDesc var2_desc("prob");
std::vector<paddle::framework::VarDesc *> vars_desc{&var_desc, &var2_desc};
paddle::framework::Attribute vars_attr(vars_desc);
auto rlt_vars_attr = paddle::framework::GetAttrValue(vars_attr);
auto rlt_vars_desc =
paddle::any_cast<std::vector<paddle::framework::VarDesc *>>(
rlt_vars_attr);
EXPECT_EQ(rlt_vars_desc.size(), vars_desc.size());
EXPECT_EQ(rlt_vars_desc[0]->Name(), vars_desc[0]->Name());
EXPECT_EQ(rlt_vars_desc[1]->Name(), vars_desc[1]->Name());
paddle::framework::ProgramDesc prog;
paddle::framework::proto::BlockDesc proto_block;
paddle::framework::BlockDesc block_desc(&prog, &proto_block);
paddle::framework::Attribute x_block_desc(&block_desc);
auto rlt_block_desc = paddle::framework::GetAttrValue(x_block_desc);
auto block_desc_ptr =
paddle::any_cast<paddle::framework::BlockDesc *>(rlt_block_desc);
EXPECT_NE(block_desc_ptr, nullptr);
std::vector<paddle::framework::BlockDesc *> vec_block_desc_var;
vec_block_desc_var.emplace_back(&block_desc);
paddle::framework::Attribute x_vec_block_desc(vec_block_desc_var);
auto rlt_vec_block_desc = paddle::framework::GetAttrValue(x_vec_block_desc);
auto vec_block_desc =
paddle::any_cast<std::vector<paddle::framework::BlockDesc *>>(
rlt_vec_block_desc);
EXPECT_EQ(vec_block_desc.size(), 1UL);
EXPECT_NE(vec_block_desc[0], nullptr);
int64_t int64_value = 100;
paddle::framework::Attribute x_int64(int64_value);
auto rlt_int64 = paddle::framework::GetAttrValue(x_int64);
EXPECT_EQ(paddle::any_cast<int64_t>(rlt_int64), 100);
std::vector<int64_t> vec_int64_var(2, 100);
paddle::framework::Attribute x_vec_int64 = vec_int64_var;
auto rlt_vec_int64 = paddle::framework::GetAttrValue(x_vec_int64);
auto vec_int64 = paddle::any_cast<std::vector<int64_t>>(rlt_vec_int64);
EXPECT_EQ(vec_int64.size(), 2UL);
EXPECT_EQ(vec_int64[0], 100);
EXPECT_EQ(vec_int64[1], 100);
std::vector<double> vec_double_var(2, 3.14);
paddle::framework::Attribute x_vec_double = vec_double_var;
auto rlt_vec_double = paddle::framework::GetAttrValue(x_vec_double);
auto vec_double = paddle::any_cast<std::vector<double>>(rlt_vec_double);
EXPECT_EQ(vec_double.size(), 2UL);
EXPECT_NEAR(vec_double[0], 3.14, 1e-6);
EXPECT_NEAR(vec_double[1], 3.14, 1e-6);
double x_double_val = 42.1;
paddle::framework::Attribute x_double(x_double_val);
ASSERT_EQ(AttrTypeID(x_double), paddle::framework::proto::FLOAT64);
EXPECT_NEAR(
paddle::any_cast<double>(paddle::framework::GetAttrValue(x_double)),
42.1,
1e-6);
paddle::framework::Attribute x_scalar = paddle::experimental::Scalar(42.1);
ASSERT_EQ(AttrTypeID(x_scalar), paddle::framework::proto::SCALAR);
EXPECT_EQ(paddle::any_cast<paddle::experimental::Scalar>(
paddle::framework::GetAttrValue(x_scalar)),
paddle::experimental::Scalar(42.1));
std::vector<paddle::experimental::Scalar> scalars =
paddle::experimental::WrapAsScalars(std::vector<int64_t>{1, 2, 3});
paddle::framework::Attribute x_scalars(scalars);
ASSERT_EQ(AttrTypeID(x_scalars), paddle::framework::proto::SCALARS);
auto x_extracted =
paddle::any_cast<std::vector<paddle::experimental::Scalar>>(
paddle::framework::GetAttrValue(x_scalars));
EXPECT_EQ(x_extracted.size(), 3UL);
EXPECT_EQ(x_extracted.at(0), scalars.at(0));
EXPECT_EQ(x_extracted.at(1), scalars.at(1));
EXPECT_EQ(x_extracted.at(2), scalars.at(2));
}
TEST(Attribute, ProtoAttrToAttribute_double) {
paddle::framework::proto::OpDesc::Attr proto_attr_double;
proto_attr_double.set_name("anon");
proto_attr_double.set_type(paddle::framework::proto::FLOAT64);
proto_attr_double.set_float64(42.1);
paddle::framework::Attribute attr_double =
paddle::framework::GetAttrValue(proto_attr_double);
ASSERT_EQ(AttrTypeID(attr_double), paddle::framework::proto::FLOAT64);
}
TEST(Attribute, ProtoAttrToAttribute_scalar) {
paddle::framework::proto::OpDesc::Attr proto_attr_scalar;
proto_attr_scalar.set_name("anon");
proto_attr_scalar.set_type(paddle::framework::proto::SCALAR);
auto s_bool = paddle::experimental::Scalar(static_cast<bool>(true));
auto s_int8 = paddle::experimental::Scalar(static_cast<int8_t>(42.1));
auto s_int16 = paddle::experimental::Scalar(static_cast<int16_t>(42.1));
auto s_int32 = paddle::experimental::Scalar(static_cast<int32_t>(42.1));
auto s_int64 = paddle::experimental::Scalar(static_cast<int64_t>(42.1));
auto s_uint8 = paddle::experimental::Scalar(static_cast<uint8_t>(42.1));
auto s_uint16 = paddle::experimental::Scalar(static_cast<uint16_t>(42.1));
auto s_uint32 = paddle::experimental::Scalar(static_cast<uint32_t>(42.1));
auto s_uint64 = paddle::experimental::Scalar(static_cast<uint64_t>(42.1));
auto s_float16 =
paddle::experimental::Scalar(static_cast<phi::float16>(42.1));
auto s_bfloat16 =
paddle::experimental::Scalar(static_cast<phi::bfloat16>(42.1));
auto s_float = paddle::experimental::Scalar(static_cast<float>(42.1));
auto s_double = paddle::experimental::Scalar(static_cast<double>(42.1));
auto s_cfloat = paddle::experimental::Scalar(std::complex<float>(42.1, 42.1));
auto s_cdouble =
paddle::experimental::Scalar(std::complex<double>(42.1, 42.1));
auto proto_scalar_bool = new paddle::framework::proto::Scalar;
*proto_scalar_bool = paddle::framework::MakeScalarProto(s_bool);
proto_attr_scalar.set_allocated_scalar(proto_scalar_bool);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_int8 = new paddle::framework::proto::Scalar;
*proto_scalar_int8 = paddle::framework::MakeScalarProto(s_int8);
proto_attr_scalar.set_allocated_scalar(proto_scalar_int8);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_int16 = new paddle::framework::proto::Scalar;
*proto_scalar_int16 = paddle::framework::MakeScalarProto(s_int16);
proto_attr_scalar.set_allocated_scalar(proto_scalar_int16);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_int32 = new paddle::framework::proto::Scalar;
*proto_scalar_int32 = paddle::framework::MakeScalarProto(s_int32);
proto_attr_scalar.set_allocated_scalar(proto_scalar_int32);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_int64 = new paddle::framework::proto::Scalar;
*proto_scalar_int64 = paddle::framework::MakeScalarProto(s_int64);
proto_attr_scalar.set_allocated_scalar(proto_scalar_int64);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_uint8 = new paddle::framework::proto::Scalar;
*proto_scalar_uint8 = paddle::framework::MakeScalarProto(s_uint8);
proto_attr_scalar.set_allocated_scalar(proto_scalar_uint8);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_uint16 = new paddle::framework::proto::Scalar;
*proto_scalar_uint16 = paddle::framework::MakeScalarProto(s_uint16);
proto_attr_scalar.set_allocated_scalar(proto_scalar_uint16);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_uint32 = new paddle::framework::proto::Scalar;
*proto_scalar_uint32 = paddle::framework::MakeScalarProto(s_uint32);
proto_attr_scalar.set_allocated_scalar(proto_scalar_uint32);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_uint64 = new paddle::framework::proto::Scalar;
*proto_scalar_uint64 = paddle::framework::MakeScalarProto(s_uint64);
proto_attr_scalar.set_allocated_scalar(proto_scalar_uint64);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_float16 = new paddle::framework::proto::Scalar;
*proto_scalar_float16 = paddle::framework::MakeScalarProto(s_float16);
proto_attr_scalar.set_allocated_scalar(proto_scalar_float16);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_bfloat16 = new paddle::framework::proto::Scalar;
*proto_scalar_bfloat16 = paddle::framework::MakeScalarProto(s_bfloat16);
proto_attr_scalar.set_allocated_scalar(proto_scalar_bfloat16);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_float = new paddle::framework::proto::Scalar;
*proto_scalar_float = paddle::framework::MakeScalarProto(s_float);
proto_attr_scalar.set_allocated_scalar(proto_scalar_float);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_double = new paddle::framework::proto::Scalar;
*proto_scalar_double = paddle::framework::MakeScalarProto(s_double);
proto_attr_scalar.set_allocated_scalar(proto_scalar_double);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_cfloat = new paddle::framework::proto::Scalar;
*proto_scalar_cfloat = paddle::framework::MakeScalarProto(s_cfloat);
proto_attr_scalar.set_allocated_scalar(proto_scalar_cfloat);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
auto proto_scalar_cdouble = new paddle::framework::proto::Scalar;
*proto_scalar_cdouble = paddle::framework::MakeScalarProto(s_cdouble);
proto_attr_scalar.set_allocated_scalar(proto_scalar_cdouble);
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalar)),
paddle::framework::proto::SCALAR);
}
TEST(Attribute, ProtoAttrToAttribute_scalars) {
paddle::framework::proto::OpDesc::Attr proto_attr_scalars;
proto_attr_scalars.set_name("anon");
proto_attr_scalars.set_type(paddle::framework::proto::SCALARS);
std::vector<paddle::experimental::Scalar> scalars;
scalars.reserve(10);
for (int i = 0; i < 10; i++) {
scalars.emplace_back(i);
}
std::vector<paddle::framework::proto::Scalar> proto_scalars;
proto_scalars.reserve(scalars.size());
for (const auto &item : scalars) {
proto_scalars.emplace_back(paddle::framework::MakeScalarProto(item));
}
paddle::framework::VectorToRepeated(proto_scalars,
proto_attr_scalars.mutable_scalars());
ASSERT_EQ(AttrTypeID(paddle::framework::GetAttrValue(proto_attr_scalars)),
paddle::framework::proto::SCALARS);
}
TEST(Attribute, MakeScalarFromAttribute) {
using paddle::framework::MakeScalarFromAttribute;
auto s_bool = true;
auto s_int32 = static_cast<int32_t>(42.1);
auto s_int64 = static_cast<int64_t>(42.1);
auto s_float = static_cast<float>(42.1);
auto s_double = static_cast<double>(42.1);
auto s_scalar = paddle::experimental::Scalar(42.1);
ASSERT_EQ(MakeScalarFromAttribute(paddle::framework::Attribute(s_bool)),
paddle::experimental::Scalar(s_bool));
ASSERT_EQ(MakeScalarFromAttribute(paddle::framework::Attribute(s_int32)),
paddle::experimental::Scalar(s_int32));
ASSERT_EQ(MakeScalarFromAttribute(paddle::framework::Attribute(s_int64)),
paddle::experimental::Scalar(s_int64));
ASSERT_EQ(MakeScalarFromAttribute(paddle::framework::Attribute(s_float)),
paddle::experimental::Scalar(s_float));
ASSERT_EQ(MakeScalarFromAttribute(paddle::framework::Attribute(s_double)),
paddle::experimental::Scalar(s_double));
ASSERT_EQ(MakeScalarFromAttribute(paddle::framework::Attribute(s_scalar)),
s_scalar);
}
TEST(Attribute, MakeScalarsFromAttribute) {
using paddle::framework::MakeScalarsFromAttribute;
std::vector<bool> v_bool(4, true);
std::vector<int> v_int(4, 42);
std::vector<int64_t> v_int64(4, 42);
std::vector<float> v_float(4, 42.1);
std::vector<double> v_double(4, 42.1);
std::vector<paddle::experimental::Scalar> v_scalar(
4, paddle::experimental::Scalar(std::complex<float>(42.1, 42.1)));
ASSERT_EQ(MakeScalarsFromAttribute(paddle::framework::Attribute(v_bool))[0],
paddle::experimental::Scalar(v_bool[0]));
ASSERT_EQ(MakeScalarsFromAttribute(paddle::framework::Attribute(v_int))[0],
paddle::experimental::Scalar(v_int[0]));
ASSERT_EQ(MakeScalarsFromAttribute(paddle::framework::Attribute(v_int64))[0],
paddle::experimental::Scalar(v_int64[0]));
ASSERT_EQ(MakeScalarsFromAttribute(paddle::framework::Attribute(v_float))[0],
paddle::experimental::Scalar(v_float[0]));
ASSERT_EQ(MakeScalarsFromAttribute(paddle::framework::Attribute(v_double))[0],
paddle::experimental::Scalar(v_double[0]));
ASSERT_EQ(MakeScalarsFromAttribute(paddle::framework::Attribute(v_scalar))[0],
v_scalar[0]);
}
@@ -0,0 +1,161 @@
/* Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/convert_utils.h"
#include "gtest/gtest.h"
#include "paddle/common/enforce.h"
namespace phi {
namespace tests {
TEST(ConvertUtils, DataType) {
// enum -> proto
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::FLOAT64),
paddle::framework::proto::VarType::FP64,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::FLOAT64 to "
"paddle::framework::proto::VarType::FP64"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::FLOAT32),
paddle::framework::proto::VarType::FP32,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::FLOAT32 to "
"paddle::framework::proto::VarType::FP32"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::UINT8),
paddle::framework::proto::VarType::UINT8,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::UINT8 to "
"paddle::framework::proto::VarType::UINT8"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::INT8),
paddle::framework::proto::VarType::INT8,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::INT8 to "
"paddle::framework::proto::VarType::INT8"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::INT32),
paddle::framework::proto::VarType::INT32,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::INT32 to "
"paddle::framework::proto::VarType::INT32"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::INT64),
paddle::framework::proto::VarType::INT64,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::INT64 to "
"paddle::framework::proto::VarType::INT64"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::INT16),
paddle::framework::proto::VarType::INT16,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::INT16 to "
"paddle::framework::proto::VarType::INT16"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::BOOL),
paddle::framework::proto::VarType::BOOL,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::BOOL to "
"paddle::framework::proto::VarType::BOOL"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::COMPLEX64),
paddle::framework::proto::VarType::COMPLEX64,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::COMPLEX64 to "
"paddle::framework::proto::VarType::COMPLEX64"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::COMPLEX128),
paddle::framework::proto::VarType::COMPLEX128,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::COMPLEX128 to "
"paddle::framework::proto::VarType::COMPLEX128"));
PADDLE_ENFORCE_EQ(
paddle::framework::TransToProtoVarType(paddle::DataType::FLOAT16),
paddle::framework::proto::VarType::FP16,
::common::errors::InvalidArgument(
"Failed to convert paddle::DataType::FLOAT16 to "
"paddle::framework::proto::VarType::FP16"));
// proto -> enum
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::FP64),
paddle::DataType::FLOAT64,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::FP64 to "
"paddle::DataType::FLOAT64"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::FP32),
paddle::DataType::FLOAT32,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::FP32 to "
"paddle::DataType::FLOAT32"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::INT64),
paddle::DataType::INT64,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::INT64 to "
"paddle::DataType::INT64"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::INT32),
paddle::DataType::INT32,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::INT32 to "
"paddle::DataType::INT32"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::INT8),
paddle::DataType::INT8,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::INT8 to "
"paddle::DataType::INT8"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::UINT8),
paddle::DataType::UINT8,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::UINT8 to "
"paddle::DataType::UINT8"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::INT16),
paddle::DataType::INT16,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::INT16 to "
"paddle::DataType::INT16"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::BOOL),
paddle::DataType::BOOL,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::BOOL to "
"paddle::DataType::BOOL"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::COMPLEX64),
paddle::DataType::COMPLEX64,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::COMPLEX64 to "
"paddle::DataType::COMPLEX64"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::COMPLEX128),
paddle::DataType::COMPLEX128,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::COMPLEX128 to "
"paddle::DataType::COMPLEX128"));
PADDLE_ENFORCE_EQ(
phi::TransToPhiDataType(paddle::framework::proto::VarType::FP16),
paddle::DataType::FLOAT16,
::common::errors::InvalidArgument(
"Failed to convert paddle::framework::proto::VarType::FP16 to "
"paddle::DataType::FLOAT16"));
}
} // namespace tests
} // namespace phi
@@ -0,0 +1,101 @@
// Copyright (c) 2019 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 <sys/types.h>
#include <random>
#include "gtest/gtest.h"
#include "paddle/common/ddim.h"
#include "paddle/common/flags.h"
#include "paddle/fluid/framework/tensor.h"
#include "paddle/fluid/framework/tensor_util.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/platform/device_context.h"
PD_DECLARE_bool(use_system_allocator);
namespace paddle {
namespace framework {
static std::vector<phi::Place> CreatePlaceList() {
std::vector<phi::Place> places;
places.emplace_back(phi::CPUPlace());
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
places.emplace_back(phi::GPUPlace(0));
#endif
return places;
}
template <typename T>
static bool CopySameTensorTestMain(const DDim &dims,
const phi::Place &src_place,
const phi::Place &dst_place,
bool sync_copy) {
FLAGS_use_system_allocator = true; // force to use system allocator
// Step 1: create a cpu tensor and initialize it with random value;
phi::DenseTensor src_cpu_tensor;
{
src_cpu_tensor.Resize(dims);
auto *src_ptr_cpu = src_cpu_tensor.mutable_data<T>(phi::CPUPlace());
int64_t num = src_cpu_tensor.numel();
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<T> dist(-1000, 1000);
for (int64_t i = 0; i < num; ++i) {
src_ptr_cpu[i] = dist(gen);
}
}
// Step 2: copy the source tensor to dst place
phi::DenseTensor dst_cpu_tensor;
{
phi::DenseTensor src_tensor;
TensorCopySync(src_cpu_tensor, src_place, &src_tensor);
// The source tensor and dst_tensor is the same
if (sync_copy) {
TensorCopySync(src_tensor, dst_place, &src_tensor);
} else {
paddle::framework::TensorCopy(src_tensor, dst_place, &src_tensor);
phi::DeviceContextPool::Instance().Get(src_place)->Wait();
phi::DeviceContextPool::Instance().Get(dst_place)->Wait();
}
// Get the result cpu tensor
TensorCopySync(src_tensor, phi::CPUPlace(), &dst_cpu_tensor);
}
const void *ground_truth_ptr = src_cpu_tensor.data();
const void *result_ptr = dst_cpu_tensor.data();
size_t byte_num = common::product(dims) * sizeof(T);
return std::memcmp(ground_truth_ptr, result_ptr, byte_num) == 0;
}
TEST(test_tensor_copy, test_copy_same_tensor) {
using DataType = float;
auto dims = common::make_ddim({3, 4, 5});
auto places = CreatePlaceList();
for (auto &src_p : places) {
for (auto &dst_p : places) {
ASSERT_TRUE(CopySameTensorTestMain<DataType>(dims, src_p, dst_p, true));
ASSERT_TRUE(CopySameTensorTestMain<DataType>(dims, src_p, dst_p, false));
}
}
}
} // namespace framework
} // namespace paddle
@@ -0,0 +1,172 @@
/* Copyright (c) 2016 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/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/op_info.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/phi_utils.h"
#include "paddle/fluid/framework/scope.h"
#include "paddle/fluid/platform/init.h"
#include "paddle/phi/core/platform/device_context.h"
#include "paddle/phi/kernels/funcs/elementwise_base.h"
#include "paddle/phi/kernels/funcs/math_function.h"
namespace paddle {
namespace framework {
template <typename T>
struct AddFunctor {
inline HOSTDEVICE T operator()(T a, T b) const { return a + b; }
};
class OpKernelTestProtoAndCheckerMaker : public OpProtoAndCheckerMaker {
public:
void Make() {
AddInput("input", "input1 of test op");
AddOutput("output", "output of test op");
AddAttr<bool>("use_gpu", "force to use gpu kernel").SetDefault(false);
AddComment("This is test op");
}
};
class TestOpWithKernel : public OperatorWithKernel {
public:
using OperatorWithKernel::OperatorWithKernel;
protected:
void InferShape(framework::InferShapeContext* ctx) const override {}
phi::KernelKey GetExpectedKernelType(
const ExecutionContext& ctx) const override {
if (Attr<bool>("use_gpu")) {
VLOG(3) << "force use gpu kernel";
return phi::KernelKey(phi::Backend::GPU,
phi::DataLayout::ALL_LAYOUT,
phi::DataType::FLOAT32);
} else {
VLOG(3) << "use default kernel";
return phi::KernelKey(proto::VarType::FP32,
ctx.Input<phi::DenseTensor>("input")->place());
}
}
};
template <typename DeviceContext, typename T>
class TestKernel : public OpKernel<float> {
public:
void Compute(const ExecutionContext& ctx) const {
std::cout << ctx.DebugString() << std::endl;
const phi::DenseTensor* input = ctx.Input<phi::DenseTensor>("input");
std::cout << "input place:" << input->place() << std::endl;
auto* output = ctx.Output<phi::DenseTensor>("output");
output->Resize(input->dims());
output->template mutable_data<T>(ctx.GetPlace());
phi::funcs::TransformFunctor<AddFunctor<T>, T, DeviceContext> functor(
*input,
*input,
output,
ctx.template device_context<DeviceContext>(),
AddFunctor<T>());
functor.Run();
}
};
} // namespace framework
} // namespace paddle
REGISTER_OP_WITHOUT_GRADIENT(
test_op,
paddle::framework::TestOpWithKernel,
paddle::framework::OpKernelTestProtoAndCheckerMaker);
REGISTER_OP_CPU_KERNEL(test_op,
paddle::framework::TestKernel<phi::CPUContext, float>);
REGISTER_OP_CUDA_KERNEL(test_op,
paddle::framework::TestKernel<phi::GPUContext, float>);
static void BuildVar(const std::string& param_name,
std::initializer_list<const char*> arguments,
paddle::framework::proto::OpDesc::Var* var) {
var->set_parameter(param_name);
for (auto& arg_name : arguments) {
*var->mutable_arguments()->Add() = arg_name;
}
}
TEST(Operator, CPUtoGPU) {
paddle::framework::InitDevices();
paddle::framework::Scope scope;
phi::CPUPlace cpu_place;
// create an op to run on CPU
paddle::framework::proto::OpDesc cpu_op_desc;
cpu_op_desc.set_type("test_op");
BuildVar("input", {"IN1"}, cpu_op_desc.add_inputs());
BuildVar("output", {"OUT1"}, cpu_op_desc.add_outputs());
auto cpu_op = paddle::framework::OpRegistry::CreateOp(cpu_op_desc);
// prepare input
auto* in_t = scope.Var("IN1")->GetMutable<phi::DenseTensor>();
auto* src_ptr = in_t->mutable_data<float>({2, 3}, phi::CPUPlace());
for (int i = 0; i < 2 * 3; ++i) {
src_ptr[i] = static_cast<float>(i);
}
// get output
auto* output = scope.Var("OUT1");
cpu_op->Run(scope, cpu_place);
auto* output_ptr = output->Get<phi::DenseTensor>().data<float>();
for (int i = 0; i < 2 * 3; ++i) {
ASSERT_EQ(output_ptr[i], static_cast<float>(i) * 2);
}
// create an op to run on GPU
paddle::framework::proto::OpDesc gpu_op_desc;
gpu_op_desc.set_type("test_op");
BuildVar("input", {"OUT1"}, gpu_op_desc.add_inputs());
BuildVar("output", {"OUT2"}, gpu_op_desc.add_outputs());
auto attr = gpu_op_desc.mutable_attrs()->Add();
attr->set_name("use_gpu");
attr->set_type(paddle::framework::proto::AttrType::BOOLEAN);
attr->set_b(true);
auto gpu_op = paddle::framework::OpRegistry::CreateOp(gpu_op_desc);
phi::GPUPlace cuda_place(0);
// get output
auto* output2 = scope.Var("OUT2");
gpu_op->Run(scope, cuda_place);
VLOG(3) << "after gpu_op run";
// auto* output2_ptr = output2->Get<phi::DenseTensor>().data<float>();
phi::DeviceContextPool& pool = phi::DeviceContextPool::Instance();
auto dev_ctx = pool.Get(cuda_place);
phi::DenseTensor output_tensor;
paddle::framework::TensorCopy(output2->Get<phi::DenseTensor>(),
phi::CPUPlace(),
*dev_ctx,
&output_tensor);
dev_ctx->Wait();
float* output2_ptr = output_tensor.data<float>();
for (int i = 0; i < 2 * 3; ++i) {
ASSERT_EQ(output2_ptr[i], static_cast<float>(i) * 4);
}
}
+346
View File
@@ -0,0 +1,346 @@
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/data_feed.h"
#include <fcntl.h>
#include <chrono> // NOLINT
#include <fstream>
#include <iostream>
#include <map>
#include <mutex> // NOLINT
#include <set>
#include <thread> // NOLINT
#include <utility>
#include <vector>
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/text_format.h"
#include "gtest/gtest.h"
#include "paddle/fluid/framework/data_feed_factory.h"
#include "paddle/fluid/framework/lod_tensor.h"
#include "paddle/fluid/framework/scope.h"
paddle::framework::DataFeedDesc load_datafeed_param_from_file(
const char* filename) {
paddle::framework::DataFeedDesc data_feed_desc;
int file_descriptor = open(filename, O_RDONLY);
PADDLE_ENFORCE_NE(
file_descriptor,
-1,
common::errors::Unavailable(
"Cannot open file %s c load datafeed param from file.", filename));
google::protobuf::io::FileInputStream fileInput(file_descriptor);
google::protobuf::TextFormat::Parse(&fileInput, &data_feed_desc);
close(file_descriptor);
return data_feed_desc;
}
const std::vector<std::string> load_filelist_from_file(const char* filename) {
std::vector<std::string> filelist;
std::ifstream fin(filename);
PADDLE_ENFORCE_EQ(
fin.good(),
true,
common::errors::Unavailable(
"Cannot open file %s when load filelist from file.", filename));
std::string line;
while (getline(fin, line)) {
filelist.push_back(line);
}
fin.close();
return filelist;
}
void GenerateFileForTest(const char* protofile, const char* filelist) {
std::ofstream w_protofile(protofile);
w_protofile << "name: \"MultiSlotDataFeed\"\n"
"batch_size: 2\n"
"multi_slot_desc {\n"
" slots {\n"
" name: \"uint64_sparse_slot\"\n"
" type: \"uint64\"\n"
" is_dense: false\n"
" is_used: true\n"
" }\n"
" slots {\n"
" name: \"float_sparse_slot\"\n"
" type: \"float\"\n"
" is_dense: false\n"
" is_used: true\n"
" }\n"
" slots {\n"
" name: \"uint64_dense_slot\"\n"
" type: \"uint64\"\n"
" is_dense: true\n"
" is_used: true\n"
" }\n"
" slots {\n"
" name: \"float_dense_slot\"\n"
" type: \"float\"\n"
" is_dense: true\n"
" is_used: true\n"
" }\n"
" slots {\n"
" name: \"not_used_slot\"\n"
" type: \"uint64\"\n"
" is_dense: false\n"
" is_used: false\n"
" }\n"
"}";
w_protofile.close();
std::ofstream w_filelist(filelist);
int total_file = 4;
for (int i = 0; i < total_file; ++i) {
std::string filename = "TestMultiSlotDataFeed.data." + std::to_string(i);
w_filelist << filename;
if (i + 1 != total_file) {
w_filelist << std::endl;
}
std::ofstream w_datafile(filename.c_str());
w_datafile << "3 3978 620 82 1 1926.08 1 1926 1 6.02 1 1996\n"
"2 1300 2983353 1 985.211 1 8 1 0.618 1 12\n"
"1 19260827 2 3.14 2.718 1 27 1 2.236 1 28\n";
w_datafile.close();
}
w_filelist.close();
}
class MultiTypeSet {
public:
MultiTypeSet() {
uint64_set_.clear();
float_set_.clear();
}
~MultiTypeSet() {}
void AddValue(uint64_t v) { uint64_set_.insert(v); }
void AddValue(float v) { float_set_.insert(v); }
const std::set<uint64_t>& GetUint64Set() const { return uint64_set_; }
const std::set<float>& GetFloatSet() const { return float_set_; }
private:
std::set<uint64_t> uint64_set_;
std::set<float> float_set_;
};
void GetElemSetFromReader(std::vector<MultiTypeSet>* reader_elem_set,
const paddle::framework::DataFeedDesc& data_feed_desc,
const std::vector<std::string>& filelist,
const int thread_num) {
int used_slot_num = 0;
for (auto i = 0; i < data_feed_desc.multi_slot_desc().slots_size(); ++i) {
if (data_feed_desc.multi_slot_desc().slots(i).is_used()) {
++used_slot_num;
}
}
reader_elem_set->resize(used_slot_num);
std::vector<std::thread> threads;
std::vector<std::shared_ptr<paddle::framework::DataFeed>> readers;
readers.resize(thread_num);
for (int i = 0; i < thread_num; ++i) {
readers[i] = paddle::framework::DataFeedFactory::CreateDataFeed(
data_feed_desc.name());
readers[i]->Init(data_feed_desc);
}
readers[0]->SetFileList(filelist);
std::mutex mu;
for (int idx = 0; idx < thread_num; ++idx) {
threads.emplace_back(std::thread([&, idx] {
std::unique_ptr<paddle::framework::Scope> scope(
new paddle::framework::Scope());
const auto& multi_slot_desc = data_feed_desc.multi_slot_desc();
std::map<std::string, const phi::DenseTensor*> lodtensor_targets;
for (int i = 0; i < multi_slot_desc.slots_size(); ++i) {
const auto& slot = multi_slot_desc.slots(i);
if (slot.is_used()) {
const auto& name = slot.name();
readers[idx]->AddFeedVar(scope->Var(name), name);
lodtensor_targets[name] =
&scope->FindVar(name)->Get<phi::DenseTensor>();
}
}
readers[idx]->Start();
while (readers[idx]->Next()) {
int index = 0;
for (int k = 0; k < multi_slot_desc.slots_size(); ++k) {
const auto& slot = multi_slot_desc.slots(k);
if (!slot.is_used()) {
continue;
}
const phi::DenseTensor* tens = lodtensor_targets[slot.name()];
if (slot.is_dense()) { // dense branch
if (slot.type() == "uint64") {
const int64_t* data = tens->data<int64_t>();
int batch_size = tens->dims()[0];
int dim = tens->dims()[1];
for (int i = 0; i < batch_size; ++i) {
for (int j = 0; j < dim; ++j) {
std::lock_guard<std::mutex> lock(mu);
(*reader_elem_set)[index].AddValue(
(uint64_t)data[i * dim + j]);
}
}
} else if (slot.type() == "float") {
const float* data = tens->data<float>();
int batch_size = tens->dims()[0];
int dim = tens->dims()[1];
for (int i = 0; i < batch_size; ++i) {
for (int j = 0; j < dim; ++j) {
std::lock_guard<std::mutex> lock(mu);
(*reader_elem_set)[index].AddValue(data[i * dim + j]);
}
}
} else {
PADDLE_THROW(
common::errors::InvalidArgument("Error type in proto file."));
}
} else { // sparse branch
if (slot.type() == "uint64") {
const int64_t* data = tens->data<int64_t>();
for (size_t i = 0; i < tens->NumElements(); ++i) {
std::pair<size_t, size_t> element = tens->lod_element(0, i);
for (size_t j = element.first; j < element.second; ++j) {
std::lock_guard<std::mutex> lock(mu);
(*reader_elem_set)[index].AddValue((uint64_t)data[j]);
}
}
} else if (slot.type() == "float") {
const float* data = tens->data<float>();
for (size_t i = 0; i < tens->NumElements(); ++i) {
std::pair<size_t, size_t> element = tens->lod_element(0, i);
for (size_t j = element.first; j < element.second; ++j) {
std::lock_guard<std::mutex> lock(mu);
(*reader_elem_set)[index].AddValue(data[j]);
}
}
} else {
PADDLE_THROW(
common::errors::InvalidArgument("Error type in proto file."));
}
} // end sparse branch
++index;
} // end slots loop
} // end while Next()
})); // end anonymous function
}
for (auto& th : threads) {
th.join();
}
}
void CheckIsUnorderedSame(const std::vector<MultiTypeSet>& s1,
const std::vector<MultiTypeSet>& s2) {
EXPECT_EQ(s1.size(), s2.size());
for (size_t i = 0; i < s1.size(); ++i) {
// check for uint64
const std::set<uint64_t>& uint64_s1 = s1[i].GetUint64Set();
const std::set<uint64_t>& uint64_s2 = s2[i].GetUint64Set();
EXPECT_EQ(uint64_s1.size(), uint64_s2.size());
auto uint64_it1 = uint64_s1.begin();
auto uint64_it2 = uint64_s2.begin();
while (uint64_it1 != uint64_s1.end()) {
EXPECT_EQ(*uint64_it1, *uint64_it2);
++uint64_it1;
++uint64_it2;
}
// check for float
const std::set<float>& float_s1 = s1[i].GetFloatSet();
const std::set<float>& float_s2 = s2[i].GetFloatSet();
EXPECT_EQ(float_s1.size(), float_s2.size());
auto float_it1 = float_s1.begin();
auto float_it2 = float_s2.begin();
while (float_it1 != float_s1.end()) {
EXPECT_EQ(*float_it1, *float_it2);
++float_it1;
++float_it2;
}
}
}
void GetElemSetFromFile(std::vector<MultiTypeSet>* file_elem_set,
const paddle::framework::DataFeedDesc& data_feed_desc,
const std::vector<std::string>& filelist) {
int used_slot_num = 0;
for (auto i = 0; i < data_feed_desc.multi_slot_desc().slots_size(); ++i) {
if (data_feed_desc.multi_slot_desc().slots(i).is_used()) {
++used_slot_num;
}
}
file_elem_set->resize(used_slot_num);
for (const auto& file : filelist) {
std::ifstream fin(file.c_str());
PADDLE_ENFORCE_EQ(
fin.good(),
true,
common::errors::Unavailable(
"Can not open %s when get element set from file.", file.c_str()));
while (1) {
bool end_flag = false;
int index = 0;
for (auto i = 0; i < data_feed_desc.multi_slot_desc().slots_size(); ++i) {
int num;
if (fin >> num) {
auto slot = data_feed_desc.multi_slot_desc().slots(i);
auto type = slot.type();
if (type == "uint64") {
while (num--) {
uint64_t feasign;
fin >> feasign;
if (slot.is_used()) {
(*file_elem_set)[index].AddValue(feasign);
}
}
} else if (type == "float") {
while (num--) {
float feasign;
fin >> feasign;
if (slot.is_used()) {
(*file_elem_set)[index].AddValue(feasign);
}
}
} else {
PADDLE_THROW(
common::errors::InvalidArgument("Error type in proto file."));
}
if (slot.is_used()) {
++index;
}
} else {
end_flag = true;
break;
}
}
if (end_flag) {
break;
}
}
fin.close();
}
}
TEST(DataFeed, MultiSlotUnitTest) {
const char* protofile = "data_feed_desc.prototxt";
const char* filelist_name = "filelist.txt";
GenerateFileForTest(protofile, filelist_name);
const std::vector<std::string> filelist =
load_filelist_from_file(filelist_name);
paddle::framework::DataFeedDesc data_feed_desc =
load_datafeed_param_from_file(protofile);
std::vector<MultiTypeSet> reader_elem_set;
std::vector<MultiTypeSet> file_elem_set;
// GetElemSetFromReader(&reader_elem_set, data_feed_desc, filelist, 4);
// GetElemSetFromFile(&file_elem_set, data_feed_desc, filelist);
// CheckIsUnorderedSame(reader_elem_set, file_elem_set);
}
@@ -0,0 +1,63 @@
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/data_layout_transform.h"
#include "gtest/gtest.h"
#include "paddle/phi/common/bfloat16.h"
TEST(DataTransform, DataLayoutFunction) {
auto place = phi::CPUPlace();
phi::DenseTensor in = phi::DenseTensor();
phi::DenseTensor out = phi::DenseTensor();
in.mutable_data<double>(common::make_ddim({2, 3, 1, 2}), place);
in.set_layout(phi::DataLayout::NHWC);
auto kernel_nhwc =
phi::KernelKey(place, phi::DataLayout::NHWC, phi::DataType::FLOAT32);
auto kernel_nchw =
phi::KernelKey(place, phi::DataLayout::NCHW, phi::DataType::FLOAT32);
paddle::framework::TransDataLayout(kernel_nhwc, kernel_nchw, in, &out, place);
EXPECT_TRUE(out.layout() == phi::DataLayout::NCHW);
EXPECT_TRUE(out.dims() == common::make_ddim({2, 2, 3, 1}));
paddle::framework::TransDataLayout(kernel_nchw, kernel_nhwc, in, &out, place);
EXPECT_TRUE(in.layout() == phi::DataLayout::NHWC);
EXPECT_TRUE(in.dims() == common::make_ddim({2, 3, 1, 2}));
}
#ifdef PADDLE_WITH_DNNL
TEST(DataTransformBf16, GetDataFromTensorDNNL) {
auto place = phi::CPUPlace();
phi::DenseTensor in = phi::DenseTensor();
in.mutable_data<phi::dtype::bfloat16>(common::make_ddim({2, 3, 1, 2}), place);
void* in_data =
phi::funcs::GetDataFromTensor(in, dnnl::memory::data_type::bf16);
EXPECT_EQ(in_data, phi::funcs::to_void_cast(in.data<phi::dtype::bfloat16>()));
}
TEST(DataTransformInt32, GetDataFromTensorDNNL) {
auto place = phi::CPUPlace();
phi::DenseTensor in = phi::DenseTensor();
in.mutable_data<int32_t>(common::make_ddim({2, 3, 1, 2}), place);
void* in_data =
phi::funcs::GetDataFromTensor(in, dnnl::memory::data_type::s32);
EXPECT_EQ(in_data, phi::funcs::to_void_cast(in.data<int32_t>()));
}
#endif
@@ -0,0 +1,65 @@
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/data_type.h"
#include <string>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/convert_utils.h"
#include "paddle/fluid/framework/tensor.h"
#include "paddle/phi/common/place.h"
TEST(DataType, float16) {
using phi::CPUPlace;
using phi::dtype::float16;
namespace f = paddle::framework;
f::proto::VarType::Type dtype = f::proto::VarType::FP16;
phi::DenseTensor tensor;
CPUPlace cpu;
tensor.mutable_data(cpu, phi::TransToPhiDataType(dtype));
// test fp16 tensor
EXPECT_EQ(f::TransToProtoVarType(tensor.dtype()),
f::ToDataType(typeid(float16)));
// test fp16 size
EXPECT_EQ(f::SizeOfType(dtype), 2u);
// test debug info
std::string type = "::phi::dtype::float16";
EXPECT_STREQ(f::DataTypeToString(dtype).c_str(), type.c_str());
}
TEST(DataType, bfloat16) {
using phi::CPUPlace;
using phi::dtype::bfloat16;
namespace f = paddle::framework;
f::proto::VarType::Type dtype = f::proto::VarType::BF16;
phi::DenseTensor tensor;
CPUPlace cpu;
tensor.mutable_data(cpu, phi::TransToPhiDataType(dtype));
// test bf16 tensor
EXPECT_EQ(f::TransToProtoVarType(tensor.dtype()),
f::ToDataType(typeid(bfloat16)));
// test bf16 size
EXPECT_EQ(f::SizeOfType(dtype), 2u);
// test debug info
std::string type = "::phi::dtype::bfloat16";
EXPECT_STREQ(f::DataTypeToString(dtype).c_str(), type.c_str());
}
@@ -0,0 +1,398 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/data_type_transform.h"
#include "gtest/gtest.h"
TEST(DataTypeTransform, CPUTransform) {
auto place = phi::CPUPlace();
auto kernel_fp16 = phi::KernelKey(
place, phi::DataLayout::ALL_LAYOUT, phi::DataType::FLOAT16);
auto kernel_bf16 = phi::KernelKey(
place, phi::DataLayout::ALL_LAYOUT, phi::DataType::BFLOAT16);
auto kernel_fp32 = phi::KernelKey(
place, phi::DataLayout::ALL_LAYOUT, phi::DataType::FLOAT32);
auto kernel_fp64 = phi::KernelKey(
place, phi::DataLayout::ALL_LAYOUT, phi::DataType::FLOAT64);
auto kernel_int32 =
phi::KernelKey(place, phi::DataLayout::ALL_LAYOUT, phi::DataType::INT32);
auto kernel_int64 =
phi::KernelKey(place, phi::DataLayout::ALL_LAYOUT, phi::DataType::INT64);
auto kernel_bool =
phi::KernelKey(place, phi::DataLayout::ALL_LAYOUT, phi::DataType::BOOL);
// data type transform from float32
{
phi::DenseTensor in;
phi::DenseTensor out;
float* ptr = in.mutable_data<float>(common::make_ddim({2, 3}), place);
int data_number = 2 * 3;
for (int i = 0; i < data_number; ++i) {
ptr[i] = i / 3; // NOLINT
}
paddle::framework::TransDataType(kernel_fp32, kernel_fp64, in, &out);
double* out_data_double = out.data<double>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_double[i], static_cast<double>(i / 3)); // NOLINT
}
paddle::framework::TransDataType(kernel_fp32, kernel_int32, in, &out);
int* out_data_int = out.data<int>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int[i], static_cast<int>(i / 3));
}
}
// data type transform from/to float16
{
phi::DenseTensor in;
phi::DenseTensor out;
phi::dtype::float16* ptr =
in.mutable_data<phi::dtype::float16>(common::make_ddim({2, 3}), place);
int data_number = 2 * 3;
for (int i = 0; i < data_number; ++i) {
ptr[i] = i;
}
// transform from float16 to other data types
paddle::framework::TransDataType(kernel_fp16, kernel_fp32, in, &out);
float* out_data_float = out.data<float>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_float[i], static_cast<float>(ptr[i]));
}
paddle::framework::TransDataType(kernel_fp16, kernel_fp64, in, &out);
double* out_data_double = out.data<double>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_double[i], static_cast<double>(ptr[i]));
}
paddle::framework::TransDataType(kernel_fp16, kernel_int32, in, &out);
int* out_data_int = out.data<int>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int[i], static_cast<int>(ptr[i]));
}
paddle::framework::TransDataType(kernel_fp16, kernel_int64, in, &out);
int64_t* out_data_int64 = out.data<int64_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int64[i], static_cast<int64_t>(ptr[i]));
}
paddle::framework::TransDataType(kernel_fp16, kernel_bool, in, &out);
bool* out_data_bool = out.data<bool>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_bool[i], static_cast<bool>(ptr[i]));
}
// transform float to float16
float* in_data_float =
in.mutable_data<float>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_float[i] = static_cast<float>(i);
}
paddle::framework::TransDataType(kernel_fp32, kernel_fp16, in, &out);
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::float16>(in_data_float[i]).x);
}
// transform double to float16
double* in_data_double =
in.mutable_data<double>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_double[i] = i;
}
paddle::framework::TransDataType(kernel_fp64, kernel_fp16, in, &out);
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x,
static_cast<phi::dtype::float16>(in_data_double[i]).x);
}
// transform int to float16
int* in_data_int = in.mutable_data<int>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_int[i] = i;
}
paddle::framework::TransDataType(kernel_int32, kernel_fp16, in, &out);
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::float16>(in_data_int[i]).x);
}
// transform int64 to float16
int64_t* in_data_int64 =
in.mutable_data<int64_t>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_int64[i] = i;
}
paddle::framework::TransDataType(kernel_int64, kernel_fp16, in, &out);
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::float16>(in_data_int64[i]).x);
}
// transform bool to float16
bool* in_data_bool =
in.mutable_data<bool>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_bool[i] = i;
}
paddle::framework::TransDataType(kernel_bool, kernel_fp16, in, &out);
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::float16>(in_data_bool[i]).x);
}
}
// data type transform from/to bfloat16
{
phi::DenseTensor in;
phi::DenseTensor out;
phi::dtype::bfloat16* ptr =
in.mutable_data<phi::dtype::bfloat16>(common::make_ddim({2, 3}), place);
int data_number = 2 * 3;
for (int i = 0; i < data_number; ++i) {
ptr[i] = i;
}
// transform from bfloat16 to other data types
paddle::framework::TransDataType(kernel_bf16, kernel_fp32, in, &out);
float* out_data_float = out.data<float>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_float[i], static_cast<float>(ptr[i]));
}
paddle::framework::TransDataType(kernel_bf16, kernel_fp64, in, &out);
double* out_data_double = out.data<double>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_double[i], static_cast<double>(ptr[i]));
}
paddle::framework::TransDataType(kernel_bf16, kernel_int32, in, &out);
int* out_data_int = out.data<int>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int[i], static_cast<int>(ptr[i]));
}
paddle::framework::TransDataType(kernel_bf16, kernel_int64, in, &out);
int64_t* out_data_int64 = out.data<int64_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int64[i], static_cast<int64_t>(ptr[i]));
}
paddle::framework::TransDataType(kernel_bf16, kernel_bool, in, &out);
bool* out_data_bool = out.data<bool>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_bool[i], static_cast<bool>(ptr[i]));
}
// transform float to bfloat16
float* in_data_float =
in.mutable_data<float>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_float[i] = static_cast<float>(i);
}
paddle::framework::TransDataType(kernel_fp32, kernel_bf16, in, &out);
ptr = out.data<phi::dtype::bfloat16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x,
static_cast<phi::dtype::bfloat16>(in_data_float[i]).x);
}
// transform double to bfloat16
double* in_data_double =
in.mutable_data<double>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_double[i] = i;
}
paddle::framework::TransDataType(kernel_fp64, kernel_bf16, in, &out);
ptr = out.data<phi::dtype::bfloat16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x,
static_cast<phi::dtype::bfloat16>(in_data_double[i]).x);
}
// transform int to bfloat16
int* in_data_int = in.mutable_data<int>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_int[i] = i;
}
paddle::framework::TransDataType(kernel_int32, kernel_bf16, in, &out);
ptr = out.data<phi::dtype::bfloat16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::bfloat16>(in_data_int[i]).x);
}
// transform int64 to bfloat16
int64_t* in_data_int64 =
in.mutable_data<int64_t>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_int64[i] = i;
}
paddle::framework::TransDataType(kernel_int64, kernel_bf16, in, &out);
ptr = out.data<phi::dtype::bfloat16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x,
static_cast<phi::dtype::bfloat16>(in_data_int64[i]).x);
}
// transform bool to bfloat16
bool* in_data_bool =
in.mutable_data<bool>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_bool[i] = i;
}
paddle::framework::TransDataType(kernel_bool, kernel_bf16, in, &out);
ptr = out.data<phi::dtype::bfloat16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::bfloat16>(in_data_bool[i]).x);
}
}
// data type transform from/to int32
{
phi::DenseTensor in;
phi::DenseTensor out;
int32_t* ptr = in.mutable_data<int32_t>(common::make_ddim({2, 3}), place);
int data_number = 2 * 3;
for (int i = 0; i < data_number; ++i) {
ptr[i] = i;
}
// transform from int32 to other data types
paddle::framework::TransDataType(kernel_int32, kernel_fp32, in, &out);
float* out_data_float = out.data<float>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_float[i], static_cast<float>(ptr[i]));
}
paddle::framework::TransDataType(kernel_int32, kernel_fp64, in, &out);
double* out_data_double = out.data<double>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_double[i], static_cast<double>(ptr[i]));
}
paddle::framework::TransDataType(kernel_int32, kernel_bf16, in, &out);
phi::dtype::bfloat16* out_data_bf16 = out.data<phi::dtype::bfloat16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_bf16[i], static_cast<phi::dtype::bfloat16>(ptr[i]));
}
paddle::framework::TransDataType(kernel_int32, kernel_int64, in, &out);
int64_t* out_data_int64 = out.data<int64_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int64[i], static_cast<int64_t>(ptr[i]));
}
paddle::framework::TransDataType(kernel_int32, kernel_bool, in, &out);
bool* out_data_bool = out.data<bool>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_bool[i], static_cast<bool>(ptr[i]));
}
// transform float to int32
float* in_data_float =
in.mutable_data<float>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_float[i] = static_cast<float>(i);
}
paddle::framework::TransDataType(kernel_fp32, kernel_int32, in, &out);
ptr = out.data<int32_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i], static_cast<int32_t>(in_data_float[i]));
}
// transform double to int32
double* in_data_double =
in.mutable_data<double>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_double[i] = i;
}
paddle::framework::TransDataType(kernel_fp64, kernel_int32, in, &out);
ptr = out.data<int32_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i], static_cast<int32_t>(in_data_double[i]));
}
// transform bfloat16 to int32
phi::dtype::bfloat16* in_data_bf16 =
in.mutable_data<phi::dtype::bfloat16>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_bf16[i] = i;
}
paddle::framework::TransDataType(kernel_bf16, kernel_int32, in, &out);
ptr = out.data<int32_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i], static_cast<int32_t>(in_data_bf16[i]));
}
// transform int64 to int32
int64_t* in_data_int64 =
in.mutable_data<int64_t>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_int64[i] = i;
}
paddle::framework::TransDataType(kernel_int64, kernel_int32, in, &out);
ptr = out.data<int32_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i], static_cast<int32_t>(in_data_int64[i]));
}
// transform bool to int32
bool* in_data_bool =
in.mutable_data<bool>(common::make_ddim({2, 3}), place);
for (int i = 0; i < data_number; ++i) {
in_data_bool[i] = i;
}
paddle::framework::TransDataType(kernel_bool, kernel_int32, in, &out);
ptr = out.data<int32_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i], static_cast<int32_t>(in_data_bool[i]));
}
}
}
@@ -0,0 +1,250 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/data_type_transform.h"
#include "gtest/gtest.h"
#include "paddle/fluid/framework/tensor_util.h"
TEST(DataTypeTransform, GPUTransform) {
auto cpu_place = phi::CPUPlace();
auto gpu_place = phi::GPUPlace(0);
phi::GPUContext context(gpu_place);
context.SetAllocator(paddle::memory::allocation::AllocatorFacade::Instance()
.GetAllocator(gpu_place, context.stream())
.get());
context.PartialInitWithAllocator();
auto kernel_fp16 = phi::KernelKey(
gpu_place, phi::DataLayout::ALL_LAYOUT, phi::DataType::FLOAT16);
auto kernel_fp32 = phi::KernelKey(
gpu_place, phi::DataLayout::ALL_LAYOUT, phi::DataType::FLOAT32);
auto kernel_fp64 = phi::KernelKey(
gpu_place, phi::DataLayout::ALL_LAYOUT, phi::DataType::FLOAT64);
auto kernel_int32 = phi::KernelKey(
gpu_place, phi::DataLayout::ALL_LAYOUT, phi::DataType::INT32);
auto kernel_int64 = phi::KernelKey(
gpu_place, phi::DataLayout::ALL_LAYOUT, phi::DataType::INT64);
auto kernel_bool = phi::KernelKey(
gpu_place, phi::DataLayout::ALL_LAYOUT, phi::DataType::BOOL);
// data type transform from float32
{
phi::DenseTensor in;
phi::DenseTensor in_gpu;
phi::DenseTensor out_gpu;
phi::DenseTensor out;
float* in_ptr =
in.mutable_data<float>(common::make_ddim({2, 3}), cpu_place);
float arr[6] = {0, 1, 2, 3, 4, 5};
int data_number = sizeof(arr) / sizeof(arr[0]);
memcpy(in_ptr, arr, sizeof(arr));
paddle::framework::TensorCopy(in, gpu_place, context, &in_gpu);
context.Wait();
paddle::framework::TransDataType(
kernel_fp32, kernel_fp64, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
double* out_data_double = out.data<double>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_double[i], static_cast<double>(arr[i]));
}
paddle::framework::TransDataType(
kernel_fp32, kernel_int32, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
int* out_data_int = out.data<int>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int[i], static_cast<int>(arr[i]));
}
}
// data type transform from/to float16
{
phi::DenseTensor in;
phi::DenseTensor in_gpu;
phi::DenseTensor out_gpu;
phi::DenseTensor out;
phi::dtype::float16* ptr = in.mutable_data<phi::dtype::float16>(
common::make_ddim({2, 3}), cpu_place);
phi::dtype::float16 arr[6] = {phi::dtype::float16(0),
phi::dtype::float16(1),
phi::dtype::float16(2),
phi::dtype::float16(3),
phi::dtype::float16(4),
phi::dtype::float16(5)};
int data_number = sizeof(arr) / sizeof(arr[0]);
memcpy(ptr, arr, sizeof(arr));
paddle::framework::TensorCopy(in, gpu_place, context, &in_gpu);
context.Wait();
// transform from float16 to other data types
paddle::framework::TransDataType(
kernel_fp16, kernel_fp32, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
float* out_data_float = out.data<float>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_float[i], static_cast<float>(ptr[i]));
}
paddle::framework::TransDataType(
kernel_fp16, kernel_fp64, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
double* out_data_double = out.data<double>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_double[i], static_cast<double>(ptr[i]));
}
paddle::framework::TransDataType(
kernel_fp16, kernel_int32, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
int* out_data_int = out.data<int>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int[i], static_cast<int>(ptr[i]));
}
paddle::framework::TransDataType(
kernel_fp16, kernel_int64, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
int64_t* out_data_int64 = out.data<int64_t>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_int64[i], static_cast<int64_t>(ptr[i]));
}
paddle::framework::TransDataType(
kernel_fp16, kernel_bool, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
bool* out_data_bool = out.data<bool>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(out_data_bool[i], static_cast<bool>(ptr[i]));
}
// transform float to float16
float* in_data_float =
in.mutable_data<float>(common::make_ddim({2, 3}), cpu_place);
for (int i = 0; i < data_number; ++i) {
in_data_float[i] = i;
}
paddle::framework::TensorCopy(in, gpu_place, context, &in_gpu);
context.Wait();
paddle::framework::TransDataType(
kernel_fp32, kernel_fp16, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::float16>(in_data_float[i]).x);
}
// transform double to float16
double* in_data_double =
in.mutable_data<double>(common::make_ddim({2, 3}), cpu_place);
for (int i = 0; i < data_number; ++i) {
in_data_double[i] = i;
}
paddle::framework::TensorCopy(in, gpu_place, context, &in_gpu);
context.Wait();
paddle::framework::TransDataType(
kernel_fp64, kernel_fp16, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x,
static_cast<phi::dtype::float16>(in_data_double[i]).x);
}
// transform int to float16
int* in_data_int =
in.mutable_data<int>(common::make_ddim({2, 3}), cpu_place);
for (int i = 0; i < data_number; ++i) {
in_data_int[i] = i;
}
paddle::framework::TensorCopy(in, gpu_place, context, &in_gpu);
context.Wait();
paddle::framework::TransDataType(
kernel_int32, kernel_fp16, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::float16>(in_data_int[i]).x);
}
// transform int64 to float16
int64_t* in_data_int64 =
in.mutable_data<int64_t>(common::make_ddim({2, 3}), cpu_place);
for (int i = 0; i < data_number; ++i) {
in_data_int64[i] = i;
}
paddle::framework::TensorCopy(in, gpu_place, context, &in_gpu);
context.Wait();
paddle::framework::TransDataType(
kernel_int64, kernel_fp16, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::float16>(in_data_int64[i]).x);
}
// transform bool to float16
bool* in_data_bool =
in.mutable_data<bool>(common::make_ddim({2, 3}), cpu_place);
for (int i = 0; i < data_number; ++i) {
in_data_bool[i] = i;
}
paddle::framework::TensorCopy(in, gpu_place, context, &in_gpu);
context.Wait();
paddle::framework::TransDataType(
kernel_bool, kernel_fp16, in_gpu, &out_gpu);
paddle::framework::TensorCopy(out_gpu, cpu_place, context, &out);
context.Wait();
ptr = out.data<phi::dtype::float16>();
for (int i = 0; i < data_number; ++i) {
EXPECT_EQ(ptr[i].x, static_cast<phi::dtype::float16>(in_data_bool[i]).x);
}
}
}
@@ -0,0 +1,12 @@
paddle_test(exception_holder_test SRCS exception_holder_test.cc)
cc_test(
build_strategy_test
SRCS build_strategy_test.cc
DEPS build_strategy op_registry op_proto_maker graph string_helper)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(exception_holder_test)
endif()
@@ -0,0 +1,148 @@
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/details/exception_holder.h"
#include "gtest/gtest.h"
#include "paddle/phi/core/memory/allocation/allocator.h"
namespace paddle {
namespace framework {
namespace details {
TEST(ExceptionHolderTester, TestEnforceNotMetCatch) {
ExceptionHolder exception_holder;
try {
throw platform::EnforceNotMet("enforce not met test", "test_file", 0);
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_TRUE(exception_holder.IsCaught());
ASSERT_EQ(exception_holder.Type(), "EnforceNotMet");
bool catch_enforce_not_met = false;
try {
exception_holder.ReThrow();
} catch (platform::EnforceNotMet& ex) {
catch_enforce_not_met = true;
} catch (...) {
catch_enforce_not_met = false;
}
ASSERT_TRUE(catch_enforce_not_met);
}
TEST(ExceptionHolderTester, TestBadAllocCatch) {
ExceptionHolder exception_holder;
try {
throw memory::allocation::BadAlloc("bad alloc test", "test_file", 0);
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_TRUE(exception_holder.IsCaught());
ASSERT_EQ(exception_holder.Type(), "BadAlloc");
bool catch_bad_alloc = false;
try {
exception_holder.ReThrow();
} catch (memory::allocation::BadAlloc& ex) {
catch_bad_alloc = true;
} catch (...) {
catch_bad_alloc = false;
}
ASSERT_TRUE(catch_bad_alloc);
}
TEST(ExceptionHolderTester, TestBaseExceptionCatch) {
ExceptionHolder exception_holder;
try {
throw std::exception();
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_TRUE(exception_holder.IsCaught());
ASSERT_EQ(exception_holder.Type(), "BaseException");
bool catch_base_exception = false;
try {
exception_holder.ReThrow();
} catch (std::exception& ex) {
catch_base_exception = true;
} catch (...) {
catch_base_exception = false;
}
ASSERT_TRUE(catch_base_exception);
}
TEST(ExceptionHolderTester, TestExceptionReplace) {
ExceptionHolder exception_holder;
try {
throw platform::EnforceNotMet("enforce not met test", "test_file", 0);
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_TRUE(exception_holder.IsCaught());
ASSERT_EQ(exception_holder.Type(), "EnforceNotMet");
try {
throw std::exception();
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_TRUE(exception_holder.IsCaught());
ASSERT_EQ(exception_holder.Type(), "EnforceNotMet");
try {
throw memory::allocation::BadAlloc("bad alloc test", "test_file", 0);
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_TRUE(exception_holder.IsCaught());
ASSERT_EQ(exception_holder.Type(), "EnforceNotMet");
try {
throw platform::EOFException("eof test", "test_file", 0);
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_EQ(exception_holder.Type(), "EnforceNotMet");
exception_holder.Clear();
try {
throw memory::allocation::BadAlloc("bad alloc test", "test_file", 0);
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_TRUE(exception_holder.IsCaught());
ASSERT_EQ(exception_holder.Type(), "BadAlloc");
try {
throw platform::EnforceNotMet("enforce not met test", "test_file", 0);
} catch (...) {
exception_holder.Catch(std::current_exception());
}
ASSERT_TRUE(exception_holder.IsCaught());
ASSERT_EQ(exception_holder.Type(), "BadAlloc");
}
} // namespace details
} // namespace framework
} // namespace paddle
@@ -0,0 +1,111 @@
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/device_worker.h"
#include <gtest/gtest.h>
#include "paddle/fluid/framework/lod_tensor.h"
namespace paddle {
namespace framework {
TEST(DenseTensor, PrintDenseTensor) {
phi::DenseTensor tensor1;
tensor1.Resize({2});
tensor1.mutable_data<float>(phi::CPUPlace());
tensor1.data<float>()[0] = 0.2;
tensor1.data<float>()[1] = 0.5;
std::string res = PrintDenseTensor(&tensor1, -1, 2);
ASSERT_EQ(res, "access violation");
res = PrintDenseTensor(&tensor1, 0, 2);
ASSERT_EQ(res, "0.2,0.5");
phi::DenseTensor tensor2;
tensor2.Resize({2});
tensor2.mutable_data<int64_t>(phi::CPUPlace());
tensor2.data<int64_t>()[0] = 1;
tensor2.data<int64_t>()[1] = 2;
res = PrintDenseTensor(&tensor2, -1, 2);
ASSERT_EQ(res, "access violation");
res = PrintDenseTensor(&tensor2, 0, 2);
ASSERT_EQ(res, "1,2");
phi::DenseTensor tensor3;
tensor3.Resize({2});
tensor3.mutable_data<double>(phi::CPUPlace());
tensor3.data<double>()[0] = 0.1;
tensor3.data<double>()[1] = 0.2;
res = PrintDenseTensor(&tensor3, 0, 2);
ASSERT_EQ(res, "0.1,0.2");
phi::DenseTensor tensor4;
tensor4.Resize({2});
tensor4.mutable_data<double>(phi::CPUPlace());
tensor4.data<double>()[0] = 0.1;
tensor4.data<double>()[1] = 0.2;
res = "";
PrintDenseTensor(&tensor4, 0, 2, res);
// ASSERT_EQ(res, "0.1,0.2");
phi::DenseTensor tensor5;
tensor5.Resize({2});
tensor5.mutable_data<int64_t>(phi::CPUPlace());
tensor5.data<int64_t>()[0] = 1;
tensor5.data<int64_t>()[1] = 2;
res = "";
PrintDenseTensor(&tensor5, -1, 2, res);
ASSERT_EQ(res, "access violation");
res = "";
PrintDenseTensor(&tensor5, 0, 2, res);
ASSERT_EQ(res, "1,2");
phi::DenseTensor tensor6;
tensor6.Resize({2});
tensor6.mutable_data<float>(phi::CPUPlace());
tensor6.data<float>()[0] = 0.2;
tensor6.data<float>()[1] = 0.5;
res = "";
PrintDenseTensor(&tensor6, -1, 2, res);
// ASSERT_EQ(res, "access violation");
res = "";
PrintDenseTensor(&tensor6, 0, 2, res);
// ASSERT_EQ(res, "0.2,0.5");
}
TEST(DenseTensor, GetTensorBound) {
LegacyLoD lod{{0, 2}};
phi::DenseTensor tensor;
tensor.set_lod(lod);
tensor.Resize({2, 1});
tensor.mutable_data<float>(phi::CPUPlace());
tensor.data<float>()[0] = 0;
tensor.data<float>()[1] = 1;
std::pair<int64_t, int64_t> res = GetTensorBound(&tensor, 0);
ASSERT_EQ(res.first, 0);
ASSERT_EQ(res.second, 2);
}
TEST(DenseTensor, CheckValidOutput) {
LegacyLoD lod{{0, 1, 2}};
phi::DenseTensor tensor;
tensor.set_lod(lod);
tensor.Resize({2, 1});
tensor.mutable_data<float>(phi::CPUPlace());
tensor.data<float>()[0] = 0;
tensor.data<float>()[1] = 1;
ASSERT_TRUE(CheckValidOutput(&tensor, 2));
}
} // namespace framework
} // namespace paddle
@@ -0,0 +1,175 @@
// Copyright (c) 2018 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/common/flags.h"
#include "paddle/fluid/framework/trainer.h"
#ifdef PADDLE_WITH_GLOO
#include "paddle/fluid/framework/fleet/gloo_wrapper.h"
#endif
#if defined _WIN32 || defined __APPLE__
#else
#define _LINUX
#endif
COMMON_DECLARE_bool(enable_exit_when_partial_worker);
namespace paddle {
namespace framework {
TEST(DisMultiTrainerTest, test1) {
#ifdef _LINUX
std::shared_ptr<DistMultiTrainer> tmp1 = std::make_shared<DistMultiTrainer>();
TrainerDesc t;
t.set_class_name("DistMultiTrainer");
t.set_device_worker_name("DownpourWorker");
t.set_thread_num(1);
auto* m = t.mutable_downpour_param()->add_program_config();
m->set_program_id("123");
std::string str;
str += "name: \"MultiSlotDataFeed\"\nbatch_size: 2\nmulti_slot_desc {\n";
str += "slots {\nname: \"words\"\ntype: \"uint64\"\nis_dense: false\n";
str += "is_used: true\n}\nslots {\nname: \"label\"\ntype: \"uint64\"\n";
str += "is_dense: false\nis_used: true\n}\n}\n";
std::shared_ptr<MultiSlotDataset> dataset =
std::make_shared<MultiSlotDataset>();
dataset->SetFileList(std::vector<std::string>());
dataset->SetThreadNum(1);
dataset->SetTrainerNum(1);
dataset->SetDataFeedDesc(str);
dataset->CreateReaders();
Scope root_scope;
tmp1->SetScope(&root_scope);
tmp1->Initialize(t, dataset.get());
ProgramDesc p;
tmp1->InitOtherEnv(p);
tmp1->Finalize();
#endif
}
TEST(DisMultiTrainerTest, testforgpugraph) {
#ifdef _LINUX
TrainerDesc t;
t.set_class_name("MultiTrainer");
t.set_device_worker_name("HogwildWorker");
t.set_thread_num(1);
auto* m = t.mutable_downpour_param()->add_program_config();
m->set_program_id("123");
std::string str;
str += "name: \"MultiSlotDataFeed\"\nbatch_size: 2\nmulti_slot_desc {\n";
str += "slots {\nname: \"words\"\ntype: \"uint64\"\nis_dense: false\n";
str += "is_used: true\n}\nslots {\nname: \"label\"\ntype: \"uint64\"\n";
str += "is_dense: false\nis_used: true\n}\n}\n";
std::shared_ptr<MultiSlotDataset> dataset =
std::make_shared<MultiSlotDataset>();
dataset->SetFileList(std::vector<std::string>());
dataset->SetThreadNum(1);
dataset->SetTrainerNum(1);
dataset->SetDataFeedDesc(str);
dataset->CreateReaders();
dataset->SetGpuGraphMode(true);
dataset->GetMemoryDataSize();
dataset->SetPassId(2);
dataset->GetPassID();
dataset->GetEpochFinish();
#endif
}
TEST(DisMultiTrainerTest, test2) {
#ifdef _LINUX
FLAGS_enable_exit_when_partial_worker = true;
std::shared_ptr<MultiTrainer> tmp1 = std::make_shared<MultiTrainer>();
TrainerDesc t;
t.set_class_name("MultiTrainer");
t.set_device_worker_name("HogwildWorker");
t.set_thread_num(1);
auto* m = t.mutable_downpour_param()->add_program_config();
m->set_program_id("123");
std::string str;
// str += "name: \"MultiSlotDataFeed\"\nbatch_size: 2\nmulti_slot_desc {\n";
str +=
"name: \"SlotRecordInMemoryDataFeed\"\nbatch_size: 2\nmulti_slot_desc "
"{\n";
str += "slots {\nname: \"words\"\ntype: \"uint64\"\nis_dense: false\n";
str += "is_used: true\n}\nslots {\nname: \"label\"\ntype: \"uint64\"\n";
str += "is_dense: false\nis_used: true\n}\n}\n";
str += "graph_config {\n";
str += "gpu_graph_training: true\n}";
// std::shared_ptr<MultiSlotDataset> dataset =
// std::make_shared<MultiSlotDataset>();
std::shared_ptr<SlotRecordDataset> dataset =
std::make_shared<SlotRecordDataset>();
dataset->SetFileList(std::vector<std::string>());
dataset->SetThreadNum(1);
dataset->SetTrainerNum(1);
dataset->SetDataFeedDesc(str);
dataset->CreateChannel();
dataset->CreateReaders();
Scope root_scope;
tmp1->SetScope(&root_scope);
tmp1->Initialize(t, dataset.get());
tmp1->SetDebug(false);
ProgramDesc p;
tmp1->InitOtherEnv(p);
tmp1->Run();
tmp1->Finalize();
#endif
}
TEST(DisMultiTrainerTest, test3) {
#ifdef _LINUX
FLAGS_enable_exit_when_partial_worker = true;
std::shared_ptr<MultiTrainer> tmp1 = std::make_shared<MultiTrainer>();
TrainerDesc t;
t.set_class_name("MultiTrainer");
t.set_device_worker_name("HogwildWorker");
t.set_thread_num(1);
auto* m = t.mutable_downpour_param()->add_program_config();
m->set_program_id("123");
std::string str;
// str += "name: \"MultiSlotDataFeed\"\nbatch_size: 2\nmulti_slot_desc {\n";
str +=
"name: \"SlotRecordInMemoryDataFeed\"\nbatch_size: 2\nmulti_slot_desc "
"{\n";
str += "slots {\nname: \"words\"\ntype: \"uint64\"\nis_dense: false\n";
str += "is_used: true\n}\nslots {\nname: \"label\"\ntype: \"uint64\"\n";
str += "is_dense: false\nis_used: true\n}\n}\n";
str += "graph_config {\n";
str += "gpu_graph_training: true\n}";
// std::shared_ptr<MultiSlotDataset> dataset =
// std::make_shared<MultiSlotDataset>();
std::shared_ptr<SlotRecordDataset> dataset =
std::make_shared<SlotRecordDataset>();
dataset->SetFileList(std::vector<std::string>());
dataset->SetThreadNum(1);
dataset->SetTrainerNum(1);
dataset->SetDataFeedDesc(str);
dataset->CreateChannel();
dataset->SetGpuGraphMode(true);
dataset->CreateReaders();
auto readers = dataset->GetReaders();
readers[0]->SetGpuGraphMode(true);
Scope root_scope;
tmp1->SetScope(&root_scope);
tmp1->Initialize(t, dataset.get());
tmp1->SetDebug(true);
ProgramDesc p;
tmp1->InitOtherEnv(p);
// tmp1->Run();
tmp1->Finalize();
#endif
}
} // namespace framework
} // namespace paddle
@@ -0,0 +1,205 @@
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/dlpack_tensor.h"
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "paddle/fluid/framework/data_type.h"
#include "paddle/phi/common/place.h"
#include "paddle/phi/core/platform/device/gpu/gpu_info.h"
namespace paddle {
namespace framework {
template <typename T>
void TestMain(const phi::Place &place) {
DDim dims{4, 5, 6, 7};
phi::DenseTensor tensor;
tensor.Resize(dims);
void *p = tensor.mutable_data<T>(place);
::DLManagedTensor *dl_managed_tensor = paddle::framework::ToDLPack(tensor);
::DLTensor &dl_tensor = dl_managed_tensor->dl_tensor;
PADDLE_ENFORCE_EQ(
p,
dl_tensor.data,
common::errors::InvalidArgument("Tensor data pointer should be "
"equal to DLPack "
"tensor data pointer, but got "
"tensor data pointer: %p, "
"DLPack tensor data pointer: %p",
p,
dl_tensor.data));
if (phi::is_cpu_place(place)) {
PADDLE_ENFORCE_EQ(
kDLCPU,
dl_tensor.device.device_type,
common::errors::InvalidArgument("Device type should be kDLCPU, "
"but got %d",
dl_tensor.device.device_type));
PADDLE_ENFORCE_EQ(
0,
dl_tensor.device.device_id,
common::errors::InvalidArgument("Device ID should be 0,"
"but got %d",
dl_tensor.device.device_id));
} else if (phi::is_gpu_place(place)) {
PADDLE_ENFORCE_EQ(kDLCUDA,
dl_tensor.device.device_type,
common::errors::InvalidArgument(
"Device type should be kDLCUDA, but got %d",
dl_tensor.device.device_type));
PADDLE_ENFORCE_EQ(
place.device,
dl_tensor.device.device_id,
common::errors::InvalidArgument("Device ID should be %d, "
"but got %d",
place.device,
dl_tensor.device.device_id));
} else if (phi::is_cuda_pinned_place(place)) {
PADDLE_ENFORCE_EQ(
kDLCUDAHost,
dl_tensor.device.device_type,
common::errors::InvalidArgument("Device type should be kDLCUDAHost, "
"but got %d",
dl_tensor.device.device_type));
PADDLE_ENFORCE_EQ(
0,
dl_tensor.device.device_id,
common::errors::InvalidArgument("Device ID should be 0, "
"but got %d",
dl_tensor.device.device_id));
} else {
PADDLE_ENFORCE_EQ(
false, true, common::errors::InvalidArgument("Unsupported place type"));
}
PADDLE_ENFORCE_EQ(
dims.size(),
dl_tensor.ndim,
common::errors::InvalidArgument("Dimension size should be equal to %d,"
"but got %d",
dims.size(),
dl_tensor.ndim));
for (auto i = 0; i < dims.size(); ++i) {
PADDLE_ENFORCE_EQ(
dims[i],
dl_tensor.shape[i],
common::errors::InvalidArgument("Dimension at index %d should be %d, "
"but got %d",
i,
dims[i],
dl_tensor.shape[i]));
}
std::vector<int64_t> expect_strides(dims.size());
expect_strides[dims.size() - 1] = 1;
for (int i = static_cast<int>(dims.size()) - 2; i >= 0; --i) {
expect_strides[i] = expect_strides[i + 1] * dims[i + 1];
}
for (auto i = 0; i < dims.size(); ++i) {
PADDLE_ENFORCE_EQ(
expect_strides[i],
dl_tensor.strides[i],
common::errors::InvalidArgument("Stride at index %d should be %d, "
"but got %d",
i,
expect_strides[i],
dl_tensor.strides[i]));
}
PADDLE_ENFORCE_EQ(static_cast<uint64_t>(0),
dl_tensor.byte_offset,
common::errors::InvalidArgument("Byte offset should be 0, "
"but got %d",
dl_tensor.byte_offset));
PADDLE_ENFORCE_EQ(
dl_tensor.dtype.lanes,
1,
common::errors::InvalidArgument(
"Lanes should be %d, but got %d", 1, dl_tensor.dtype.lanes));
PADDLE_ENFORCE_EQ(
sizeof(T) * 8,
dl_tensor.dtype.bits,
common::errors::InvalidArgument("Data type bits should be %d, "
"but got %d",
sizeof(T) * 8,
dl_tensor.dtype.bits));
}
template <typename T>
void TestToDLManagedTensor(const phi::Place &place) {
DDim dims{6, 7};
phi::DenseTensor tensor;
tensor.Resize(dims);
tensor.mutable_data<T>(place);
::DLManagedTensor *dl_managed_tensor = paddle::framework::ToDLPack(tensor);
PADDLE_ENFORCE_NOT_NULL(
dl_managed_tensor->manager_ctx,
common::errors::InvalidArgument("Manager context should not be nullptr"));
for (auto i = 0; i < dims.size(); ++i) {
PADDLE_ENFORCE_EQ(
dims[i],
dl_managed_tensor->dl_tensor.shape[i],
common::errors::InvalidArgument("Dimension at index %d should be %d, "
"but got %d",
i,
dims[i],
dl_managed_tensor->dl_tensor.shape[i]));
}
PADDLE_ENFORCE_EQ(dl_managed_tensor->dl_tensor.strides[0] == 7,
true,
common::errors::InvalidArgument(
"Stride at index 0 should be 7, but got %d",
dl_managed_tensor->dl_tensor.strides[0]));
PADDLE_ENFORCE_EQ(dl_managed_tensor->dl_tensor.strides[1] == 1,
true,
common::errors::InvalidArgument(
"Stride at index 1 should be 1, but got %d",
dl_managed_tensor->dl_tensor.strides[1]));
dl_managed_tensor->deleter(dl_managed_tensor);
}
template <typename T>
void TestMainLoop() {
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
std::vector<phi::Place> places{
phi::CPUPlace(), phi::GPUPlace(0), phi::GPUPinnedPlace()};
if (platform::GetGPUDeviceCount() > 1) {
places.emplace_back(phi::GPUPlace(1));
}
#else
std::vector<phi::Place> places{phi::CPUPlace()};
#endif
for (auto &p : places) {
TestMain<T>(p);
TestToDLManagedTensor<T>(p);
}
}
TEST(dlpack, test_all) {
#define TestCallback(cpp_type, proto_type) TestMainLoop<cpp_type>()
_ForEachDataType_(TestCallback);
}
} // namespace framework
} // namespace paddle
+138
View File
@@ -0,0 +1,138 @@
// Copyright (c) 2018 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/fluid/framework/eigen.h"
#include "paddle/phi/common/place.h"
#include "paddle/common/ddim.h"
namespace paddle {
namespace framework {
TEST(EigenDim, From) {
EigenDim<3>::Type ed = EigenDim<3>::From(common::make_ddim({1, 2, 3}));
ASSERT_EQ(1, ed[0]);
ASSERT_EQ(2, ed[1]);
ASSERT_EQ(3, ed[2]);
}
TEST(Eigen, DenseTensor) {
phi::DenseTensor t;
float* p =
t.mutable_data<float>(common::make_ddim({1, 2, 3}), phi::CPUPlace());
for (int i = 0; i < 1 * 2 * 3; i++) {
p[i] = static_cast<float>(i);
}
EigenTensor<float, 3>::Type et = EigenTensor<float, 3>::From(t);
ASSERT_EQ(1, et.dimension(0));
ASSERT_EQ(2, et.dimension(1));
ASSERT_EQ(3, et.dimension(2));
for (int i = 0; i < 1; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 3; k++) {
ASSERT_NEAR((i * 2 + j) * 3 + k, et(i, j, k), 1e-6f);
}
}
}
}
TEST(Eigen, ScalarFrom) {
phi::DenseTensor t;
int* p = t.mutable_data<int>(common::make_ddim({1}), phi::CPUPlace());
*p = static_cast<int>(100);
EigenScalar<int>::Type es = EigenScalar<int>::From(t);
ASSERT_EQ(0, es.dimension(0));
ASSERT_EQ(100, es(0));
}
TEST(Eigen, VectorFrom) {
phi::DenseTensor t;
float* p = t.mutable_data<float>(common::make_ddim({6}), phi::CPUPlace());
for (int i = 0; i < 6; i++) {
p[i] = static_cast<float>(i);
}
EigenVector<float>::Type ev = EigenVector<float>::From(t);
ASSERT_EQ(6, ev.dimension(0));
for (int i = 0; i < 6; i++) {
ASSERT_NEAR(i, ev(i), 1e-6f);
}
}
TEST(Eigen, VectorFlatten) {
phi::DenseTensor t;
float* p =
t.mutable_data<float>(common::make_ddim({1, 2, 3}), phi::CPUPlace());
for (int i = 0; i < 1 * 2 * 3; i++) {
p[i] = static_cast<float>(i);
}
EigenVector<float>::Type ev = EigenVector<float>::Flatten(t);
ASSERT_EQ(1 * 2 * 3, ev.dimension(0));
for (int i = 0; i < 1 * 2 * 3; i++) {
ASSERT_NEAR(i, ev(i), 1e-6f);
}
}
TEST(Eigen, Matrix) {
phi::DenseTensor t;
float* p = t.mutable_data<float>(common::make_ddim({2, 3}), phi::CPUPlace());
for (int i = 0; i < 2 * 3; i++) {
p[i] = static_cast<float>(i);
}
EigenMatrix<float>::Type em = EigenMatrix<float>::From(t);
ASSERT_EQ(2, em.dimension(0));
ASSERT_EQ(3, em.dimension(1));
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
ASSERT_NEAR(i * 3 + j, em(i, j), 1e-6f);
}
}
}
TEST(Eigen, MatrixReshape) {
phi::DenseTensor t;
float* p = t.mutable_data<float>({2, 3, 6, 4}, phi::CPUPlace());
for (int i = 0; i < 2 * 3 * 6 * 4; ++i) {
p[i] = static_cast<float>(i);
}
EigenMatrix<float>::Type em = EigenMatrix<float>::Reshape(t, 2);
ASSERT_EQ(2 * 3, em.dimension(0));
ASSERT_EQ(6 * 4, em.dimension(1));
for (int i = 0; i < 2 * 3; i++) {
for (int j = 0; j < 6 * 4; j++) {
ASSERT_NEAR(i * 6 * 4 + j, em(i, j), 1e-6f);
}
}
}
} // namespace framework
} // namespace paddle
@@ -0,0 +1,79 @@
// Copyright (c) 2019 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/fluid/framework/fleet/fleet_wrapper.h"
#include "paddle/fluid/framework/fleet/gloo_wrapper.h"
#include "paddle/utils/string/string_helper.h"
#if defined _WIN32 || defined __APPLE__
#else
#define _LINUX
#endif
TEST(TEST_GLOO, store_1) {
#ifdef _LINUX
#ifdef PADDLE_WITH_GLOO
#else
auto store = gloo::rendezvous::HdfsStore("./test_gllo_store");
store.set("1", std::vector<char>{'t', 'e', 's', 't'});
store.get("1");
try {
store.get("2");
} catch (...) {
VLOG(3) << "catch expected error of not found";
}
store.wait(std::vector<std::string>{"test"});
store.wait(std::vector<std::string>{"test"}, std::chrono::milliseconds(0));
store.SetTimeoutSeconds(100000);
store.EncodeName("1");
store.TmpPath("1");
store.ObjectPath("1");
std::vector<bool> status(1, false);
store.Check(std::vector<std::string>{"test"}, &status);
auto gw = paddle::framework::GlooWrapper();
gw.SetTimeoutSeconds(1000, 1000);
gw.SetRank(0);
gw.SetSize(1);
gw.SetPrefix("");
gw.SetIface("lo");
gw.SetHdfsStore("", "", "");
gw.Init();
gw.SetHttpStore("", 8099, "");
gw.Init();
gw.Rank();
gw.Size();
gw.Barrier();
std::vector<double> input;
gw.AllReduce(input);
int64_t t;
gw.AllGather(t);
#endif
#endif
}
TEST(TEST_FLEET, fleet_1) {
auto fleet = paddle::framework::FleetWrapper::GetInstance();
#ifdef PADDLE_WITH_PSLIB
#else
fleet->RunServer("", 0);
fleet->SaveModelOneTable(0, "", 0);
fleet->SaveModelOneTablePrefix(0, "", 0, "");
fleet->Confirm();
fleet->Revert();
paddle::string::erase_spaces("1 2");
#endif
}
@@ -0,0 +1,280 @@
/* Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/infershape_utils.h"
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/attribute.h"
#include "paddle/fluid/framework/op_info.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/phi/backends/cpu/cpu_context.h"
#include "paddle/phi/core/compat/op_utils.h"
#include "paddle/phi/core/dense_tensor.h"
#include "paddle/phi/core/infermeta_utils.h"
#include "paddle/phi/core/kernel_registry.h"
namespace paddle {
namespace framework {
void TestInferMeta(bool bool_attr,
int int_attr,
int64_t int64_attr,
float float_attr,
const std::string& str_attr,
const std::vector<bool>& vec_bool_attr,
const std::vector<int>& vec_int_attr,
const std::vector<int64_t>& vec_int64_attr,
const std::vector<float>& vec_float_attr,
const std::vector<double>& vec_double_attr,
const std::vector<std::string>& vec_str_attr) {
ASSERT_EQ(bool_attr, true);
ASSERT_EQ(int_attr, 10);
ASSERT_EQ(int64_attr, 100);
ASSERT_NEAR(float_attr, 3.14, 1e-6);
ASSERT_EQ(str_attr, "test");
ASSERT_EQ(vec_bool_attr.at(0), true);
ASSERT_EQ(vec_bool_attr.at(1), true);
ASSERT_EQ(vec_int_attr.at(0), 10);
ASSERT_EQ(vec_int_attr.at(1), 10);
ASSERT_EQ(vec_int64_attr.at(0), 100L);
ASSERT_EQ(vec_int64_attr.at(1), 100L);
ASSERT_NEAR(vec_float_attr.at(0), 3.14, 1e-6);
ASSERT_NEAR(vec_float_attr.at(1), 3.14, 1e-6);
ASSERT_NEAR(vec_double_attr.at(0), 3.1415, 1e-6);
ASSERT_NEAR(vec_double_attr.at(1), 3.1415, 1e-6);
ASSERT_EQ(vec_str_attr.at(0), "test_vec");
ASSERT_EQ(vec_str_attr.at(1), "test_vec");
}
class InferShapeUtilsTestOpMaker : public OpProtoAndCheckerMaker {
public:
void Make() override {
AddAttr<bool>("bool", "bool attr of test op");
AddAttr<int>("int", "int attr of test op");
AddAttr<int64_t>("int64", "int64 attr of test op");
AddAttr<float>("float", "float attr of test op");
AddAttr<std::string>("string", "string attr of test op");
AddAttr<std::vector<bool>>("vec_bool", "vec_bool attr of test op");
AddAttr<std::vector<int>>("vec_int", "vec_int attr of test op");
AddAttr<std::vector<int64_t>>("vec_int64", "vec_int attr of test op");
AddAttr<std::vector<float>>("vec_float", "vec_int attr of test op");
AddAttr<std::vector<double>>("vec_double", "vec_int attr of test op");
AddAttr<std::vector<std::string>>("vec_str", "vec_int attr of test op");
AddComment("This is test op");
}
};
class InferShapeUtilsTestOp : public OperatorWithKernel {
public:
using OperatorWithKernel::OperatorWithKernel;
phi::KernelKey GetExpectedKernelType(
const ExecutionContext& ctx) const override {
return phi::KernelKey(proto::VarType::FP32, ctx.GetPlace());
}
};
phi::KernelSignature InferShapeUtilsTestOpArgumentMapping(
const phi::ArgumentMappingContext& ctx) {
return phi::KernelSignature("infer_shape_utils_test",
{},
{"bool",
"int",
"int64",
"float",
"string",
"vec_bool",
"vec_int",
"vec_int64",
"vec_float",
"vec_double",
"vec_str"},
{});
}
template <typename T, typename Context>
void InferShapeUtilsTestKernel(const Context& dev_ctx,
const phi::DenseTensor& x,
bool attr1,
int attr2,
int64_t attr3,
float attr4,
const std::string& attr5,
const std::vector<bool>& attr6,
const std::vector<int>& attr7,
const std::vector<int64_t>& attr8,
const std::vector<float>& attr9,
const std::vector<double>& attr10,
const std::vector<std::string>& attr11,
phi::DenseTensor* out) {
VLOG(6) << "Come into InferShapeUtilsTestKernel";
}
void TestOutputInferMeta(const phi::MetaTensor& x, phi::MetaTensor* out) {
ASSERT_EQ(x.dtype(), phi::DataType::FLOAT32);
}
class InferShapeUtilsTestOutputOpMaker : public OpProtoAndCheckerMaker {
public:
void Make() override {
AddInput("X", "input of test op");
AddOutput("Out", "output of test op");
AddComment("This is test op");
}
};
class InferShapeUtilsTestOutputOp : public OperatorWithKernel {
public:
using OperatorWithKernel::OperatorWithKernel;
phi::KernelKey GetExpectedKernelType(
const ExecutionContext& ctx) const override {
return phi::KernelKey(proto::VarType::FP32, ctx.GetPlace());
}
};
phi::KernelSignature TestSparseOutputOpArgumentMapping(
const phi::ArgumentMappingContext& ctx) {
if (ctx.IsSparseCooTensorOutput("Out")) {
return phi::KernelSignature(
"test_sparse_coo_tensor_output", {"X"}, {}, {"Out"});
}
return phi::KernelSignature("test_output", {"X"}, {}, {"Out"});
}
template <typename T, typename Context>
void InferShapeUtilsTestOutputKernel(const Context& dev_ctx,
const phi::DenseTensor& x,
phi::SparseCooTensor* out) {
VLOG(6) << "Come into InferShapeUtilsTestOutputKernel";
}
} // namespace framework
} // namespace paddle
DECLARE_INFER_SHAPE_FUNCTOR(infer_shape_utils_test,
InferShapeUtilsTestInferShapeFunctor,
PD_INFER_META(paddle::framework::TestInferMeta));
REGISTER_OPERATOR(infer_shape_utils_test,
paddle::framework::InferShapeUtilsTestOp,
paddle::framework::InferShapeUtilsTestOpMaker,
InferShapeUtilsTestInferShapeFunctor);
PD_REGISTER_KERNEL(infer_shape_utils_test,
CPU,
ALL_LAYOUT,
paddle::framework::InferShapeUtilsTestKernel,
int) {}
DECLARE_INFER_SHAPE_FUNCTOR(
infer_shape_utils_test_output,
InferShapeUtilsTestOutputInferShapeFunctor,
PD_INFER_META(paddle::framework::TestOutputInferMeta));
REGISTER_OPERATOR(infer_shape_utils_test_output,
paddle::framework::InferShapeUtilsTestOutputOp,
paddle::framework::InferShapeUtilsTestOutputOpMaker,
InferShapeUtilsTestOutputInferShapeFunctor);
PD_REGISTER_KERNEL(test_sparse_coo_tensor_output,
CPU,
ALL_LAYOUT,
paddle::framework::InferShapeUtilsTestOutputKernel,
int) {}
TEST(InferShapeUtilsTest, ALL) {
paddle::framework::ProgramDesc prog;
paddle::framework::proto::BlockDesc proto_block;
paddle::framework::BlockDesc block_desc(&prog, &proto_block);
auto* op = block_desc.AppendOp();
op->SetType("infer_shape_utils_test");
paddle::framework::Attribute bool_attr(true);
op->SetAttr("bool", bool_attr);
paddle::framework::Attribute int_attr(10);
op->SetAttr("int", int_attr);
int64_t int64_val = 100;
paddle::framework::Attribute int64_attr(int64_val);
op->SetAttr("int64", int64_attr);
float float_value = 3.14;
paddle::framework::Attribute float_attr(float_value);
op->SetAttr("float", float_attr);
std::string str_value("test");
paddle::framework::Attribute str_attr(str_value);
op->SetAttr("string", str_attr);
std::vector<bool> vec_bool(2, true);
paddle::framework::Attribute vec_bool_attr = vec_bool;
op->SetAttr("vec_bool", vec_bool_attr);
std::vector<int> vec_int(2, 10);
paddle::framework::Attribute vec_int_attr = vec_int;
op->SetAttr("vec_int", vec_int_attr);
std::vector<int64_t> vec_int64(2, 100);
paddle::framework::Attribute vec_int64_attr = vec_int64;
op->SetAttr("vec_int64", vec_int64_attr);
std::cout << "after set vec_int64" << std::endl;
std::vector<float> vec_float(2, 3.14);
paddle::framework::Attribute vec_float_attr = vec_float;
op->SetAttr("vec_float", vec_float_attr);
std::vector<double> vec_double(2, 3.1415);
paddle::framework::Attribute vec_double_attr = vec_double;
op->SetAttr("vec_double", vec_double_attr);
std::vector<std::string> vec_str(2, "test_vec");
paddle::framework::Attribute vec_str_attr = vec_str;
op->SetAttr("vec_str", vec_str_attr);
phi::OpUtilsMap::Instance().InsertArgumentMappingFn(
"infer_shape_utils_test",
paddle::framework::InferShapeUtilsTestOpArgumentMapping);
op->InferShape(block_desc);
}
TEST(InferShapeUtilsTestOutput, ALL) {
paddle::framework::ProgramDesc prog;
paddle::framework::proto::BlockDesc proto_block;
paddle::framework::BlockDesc block_desc(&prog, &proto_block);
auto* op = block_desc.AppendOp();
op->SetType("infer_shape_utils_test_output");
auto* x = block_desc.Var("x");
x->SetType(paddle::framework::proto::VarType::DENSE_TENSOR);
x->SetDataType(paddle::framework::proto::VarType::FP32);
op->SetInput("X", {"x"});
auto* out = block_desc.Var("out");
out->SetType(paddle::framework::proto::VarType::SPARSE_COO);
op->SetOutput("Out", {"out"});
phi::OpUtilsMap::Instance().InsertArgumentMappingFn(
"infer_shape_utils_test_output",
paddle::framework::TestSparseOutputOpArgumentMapping);
op->InferShape(block_desc);
}
@@ -0,0 +1,136 @@
// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/inlined_vector.h"
#include <cstdlib>
#include <ctime>
#include "glog/logging.h"
#include "gtest/gtest.h"
namespace paddle {
namespace framework {
template <typename T, size_t N>
static std::vector<T> ToStdVector(const framework::InlinedVector<T, N> &vec) {
std::vector<T> std_vec;
std_vec.reserve(vec.size());
for (size_t i = 0; i < vec.size(); ++i) {
std_vec.emplace_back(vec[i]);
}
return std_vec;
}
template <size_t N>
void InlinedVectorCheck(size_t n) {
std::srand(std::time(nullptr));
std::vector<int> std_vec;
framework::InlinedVector<int, N> vec;
for (size_t i = 0; i < n; ++i) {
int value = rand(); // NOLINT
std_vec.emplace_back(value);
vec.emplace_back(value);
PADDLE_ENFORCE_EQ(std_vec.size(),
vec.size(),
common::errors::InvalidArgument(
"The sizes of std_vec and vec should be equal, but "
"received std_vec.size() = %d and vec.size() = %d.",
std_vec.size(),
vec.size()));
PADDLE_ENFORCE_EQ(
std_vec.back(),
vec.back(),
common::errors::InvalidArgument(
"The last elements of std_vec and vec should be equal, but "
"received std_vec.back() = %d and vec.back() = %d.",
std_vec.back(),
vec.back()));
PADDLE_ENFORCE_EQ(vec.back(),
value,
common::errors::InvalidArgument(
"The last element of vec should be equal to value, "
"but received vec.back() = %d and value = %d.",
vec.back(),
value));
}
bool is_equal = (std_vec == ToStdVector(vec));
PADDLE_ENFORCE_EQ(
is_equal,
true,
common::errors::InvalidArgument(
"The std_vec and vec should be equal, but they are not."));
for (size_t i = 0; i < n; ++i) {
PADDLE_ENFORCE_EQ(std_vec.size(),
vec.size(),
common::errors::InvalidArgument(
"The sizes of std_vec and vec should be equal, but "
"received std_vec.size() = %d and vec.size() = %d.",
std_vec.size(),
vec.size()));
PADDLE_ENFORCE_EQ(
std_vec.back(),
vec.back(),
common::errors::InvalidArgument(
"The last elements of std_vec and vec should be equal, but "
"received std_vec.back() = %d and vec.back() = %d.",
std_vec.back(),
vec.back()));
std_vec.pop_back();
vec.pop_back();
PADDLE_ENFORCE_EQ(std_vec.size(),
vec.size(),
common::errors::InvalidArgument(
"The sizes of std_vec and vec should be equal, but "
"received std_vec.size() = %d and vec.size() = %d.",
std_vec.size(),
vec.size()));
}
PADDLE_ENFORCE_EQ(std_vec.size(),
vec.size(),
common::errors::InvalidArgument(
"The sizes of std_vec and vec should be equal, but "
"received std_vec.size() = %d and vec.size() = %d.",
std_vec.size(),
vec.size()));
PADDLE_ENFORCE_EQ(
vec.size(),
static_cast<size_t>(0),
common::errors::InvalidArgument(
"The size of vec should be 0, but received vec.size() = %d.",
vec.size()));
}
TEST(inlined_vector, inlined_vector) {
for (size_t i = 0; i < 20; ++i) {
InlinedVectorCheck<1>(i);
InlinedVectorCheck<10>(i);
InlinedVectorCheck<15>(i);
InlinedVectorCheck<20>(i);
InlinedVectorCheck<21>(i);
InlinedVectorCheck<25>(i);
}
}
} // namespace framework
} // namespace paddle
@@ -0,0 +1,119 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/io/crypto/aes_cipher.h"
#include <cryptopp/cryptlib.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <fstream>
#include <string>
#include "paddle/fluid/framework/io/crypto/cipher_utils.h"
namespace paddle {
namespace framework {
class AESTest : public ::testing::Test {
public:
std::string key;
void SetUp() override { key = CipherUtils::GenKey(256); }
static void GenConfigFile(const std::string& cipher_name);
};
void AESTest::GenConfigFile(const std::string& cipher_name) {
std::ofstream fout("aes_test.conf");
fout << "cipher_name : " << cipher_name << std::endl;
fout.close();
}
TEST_F(AESTest, security_string) {
std::vector<std::string> name_list({"AES_CTR_NoPadding",
"AES_CBC_PKCSPadding",
"AES_ECB_PKCSPadding",
"AES_GCM_NoPadding"});
const std::string plaintext("hello world.");
bool is_throw = false;
for (auto& i : name_list) {
AESTest::GenConfigFile(i);
try {
auto cipher = CipherFactory::CreateCipher("aes_test.conf");
std::string ciphertext = cipher->Encrypt(plaintext, AESTest::key);
std::string plaintext1 = cipher->Decrypt(ciphertext, AESTest::key);
EXPECT_EQ(plaintext, plaintext1);
} catch (CryptoPP::Exception& e) {
is_throw = true;
LOG(ERROR) << e.what();
}
EXPECT_FALSE(is_throw);
}
}
TEST_F(AESTest, security_vector) {
std::vector<std::string> name_list({"AES_CTR_NoPadding",
"AES_CBC_PKCSPadding",
"AES_ECB_PKCSPadding",
"AES_GCM_NoPadding"});
std::vector<int> input{1, 2, 3, 4};
bool is_throw = false;
for (auto& i : name_list) {
AESTest::GenConfigFile(i);
try {
auto cipher = CipherFactory::CreateCipher("aes_test.conf");
for (auto& i : input) {
std::string ciphertext =
cipher->Encrypt(std::to_string(i), AESTest::key);
std::string plaintext = cipher->Decrypt(ciphertext, AESTest::key);
int output = std::stoi(plaintext);
EXPECT_EQ(i, output);
}
} catch (CryptoPP::Exception& e) {
is_throw = true;
LOG(ERROR) << e.what();
}
EXPECT_FALSE(is_throw);
}
}
TEST_F(AESTest, encrypt_to_file) {
std::vector<std::string> name_list({"AES_CTR_NoPadding",
"AES_CBC_PKCSPadding",
"AES_ECB_PKCSPadding",
"AES_GCM_NoPadding"});
const std::string plaintext("hello world.");
std::string filename("aes_test.ciphertext");
bool is_throw = false;
for (auto& i : name_list) {
AESTest::GenConfigFile(i);
try {
auto cipher = CipherFactory::CreateCipher("aes_test.conf");
cipher->EncryptToFile(plaintext, AESTest::key, filename);
std::string plaintext1 = cipher->DecryptFromFile(AESTest::key, filename);
EXPECT_EQ(plaintext, plaintext1);
} catch (CryptoPP::Exception& e) {
is_throw = true;
LOG(ERROR) << e.what();
}
EXPECT_FALSE(is_throw);
}
}
} // namespace framework
} // namespace paddle
@@ -0,0 +1,77 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/io/crypto/cipher_utils.h"
#include <gtest/gtest.h>
#include <fstream>
#include <string>
namespace paddle {
namespace framework {
TEST(CipherUtils, load_config) {
std::string filename("cryptotest_config_file.conf");
std::ofstream fout(filename, std::ios::out);
fout << "# annotation test line:"
" must have two space along ':'."
<< std::endl;
std::vector<std::string> key_value;
key_value.emplace_back("key_str : ciphername");
key_value.emplace_back("key_int : 1");
key_value.emplace_back("key_bool : true");
key_value.emplace_back("key_bool1 : false");
key_value.emplace_back("key_bool2 : 0");
for (auto& i : key_value) {
fout << i << std::endl;
}
fout.close();
auto config = CipherUtils::LoadConfig(filename);
std::string out_str;
EXPECT_TRUE(CipherUtils::GetValue<std::string>(config, "key_str", &out_str));
EXPECT_EQ(out_str, std::string("ciphername"));
int out_int = 0;
EXPECT_TRUE(CipherUtils::GetValue<int>(config, "key_int", &out_int));
EXPECT_EQ(out_int, 1);
bool out_bool = false;
EXPECT_TRUE(CipherUtils::GetValue<bool>(config, "key_bool", &out_bool));
EXPECT_EQ(out_bool, true);
bool out_bool1 = false;
EXPECT_TRUE(CipherUtils::GetValue<bool>(config, "key_bool1", &out_bool1));
EXPECT_EQ(out_bool1, false);
bool out_bool2 = false;
EXPECT_TRUE(CipherUtils::GetValue<bool>(config, "key_bool2", &out_bool2));
EXPECT_EQ(out_bool2, false);
}
TEST(CipherUtils, gen_key) {
std::string filename("test_keyfile");
std::string key = CipherUtils::GenKey(256);
std::string key1 = CipherUtils::GenKeyToFile(256, filename);
EXPECT_NE(key, key1);
std::string key2 = CipherUtils::ReadKeyFromFile(filename);
EXPECT_EQ(key1, key2);
EXPECT_EQ(static_cast<int>(key.size()), 32);
}
} // namespace framework
} // namespace paddle
+62
View File
@@ -0,0 +1,62 @@
// Copyright (c) 2019 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 <fstream>
#include "paddle/fluid/framework/io/fs.h"
#if defined _WIN32 || defined __APPLE__
#else
#define _LINUX
#endif
TEST(FS, mv) {
#ifdef _LINUX
std::ofstream out("src.txt");
out.close();
paddle::framework::fs_mv("src.txt", "dest.txt");
paddle::framework::hdfs_mv("", "");
paddle::framework::localfs_mv("", "");
try {
paddle::framework::hdfs_mv("afs:/none", "afs:/none");
} catch (...) {
VLOG(3) << "test hdfs_mv, catch expected errors of unknown path";
}
try {
paddle::framework::fs_mv("afs:/none", "afs:/none");
} catch (...) {
VLOG(3) << "test hdfs_mv, catch expected errors of unknown path";
}
try {
paddle::framework::hdfs_mv("unknown:/none", "unknown:/none");
} catch (...) {
VLOG(3) << "test hdfs_mv, catch expected errors of unknown prefix";
}
try {
paddle::framework::dataset_hdfs_set_command(
"hadoop -D hadoop.job.ugi=anotherxxx fs -text");
int err_no = 0;
paddle::framework::hdfs_open_read("afs:/none.gz", &err_no, "", true);
paddle::framework::hdfs_open_read("afs:/none.gz", &err_no, "", false);
paddle::framework::hdfs_open_read("afs:/none", &err_no, "", true);
paddle::framework::hdfs_open_read("afs:/none", &err_no, "", false);
} catch (...) {
VLOG(3) << "test hdfs_open_read, catch expected errors of unknown path";
}
#endif
}
+215
View File
@@ -0,0 +1,215 @@
# Legacy IR Pass Tests
cc_test(
node_test
SRCS node_test.cc
DEPS node)
cc_test(
pass_test
SRCS pass_test.cc
DEPS graph pass graph_helper)
cc_test(
graph_test
SRCS graph_test.cc
DEPS graph graph_helper op_registry)
cc_test(
graph_helper_test
SRCS graph_helper_test.cc
DEPS graph graph_helper op_registry)
cc_test(
reference_count_pass_helper_test
SRCS reference_count_pass_helper_test.cc
DEPS reference_count_pass_helper node)
cc_test(
graph_to_program_pass_test
SRCS graph_to_program_pass_test.cc
DEPS graph_to_program_pass)
cc_test(
cost_model_test
SRCS cost_model_test.cc
DEPS cost_model op_registry)
cc_test(
test_graph_pattern_detector
SRCS graph_pattern_detector_test.cc
DEPS graph_pattern_detector)
cc_test(
test_op_compat_sensible_pass
SRCS op_compat_sensible_pass_test.cc
DEPS op_compat_sensible_pass)
# Fusion pass tests
cc_test(
test_fc_fuse_pass_cc
SRCS fc_fuse_pass_test.cc
DEPS fc_fuse_pass)
cc_test(
test_fc_lstm_fuse_pass_cc
SRCS fc_lstm_fuse_pass_test.cc
DEPS fc_lstm_fuse_pass)
cc_test(
test_fc_gru_fuse_pass_cc
SRCS fc_gru_fuse_pass_test.cc
DEPS fc_gru_fuse_pass)
cc_test(
test_seqpool_concat_fuse_pass
SRCS seqpool_concat_fuse_pass_test.cc
DEPS seqpool_concat_fuse_pass)
cc_test(
test_seqpool_cvm_concat_fuse_pass
SRCS seqpool_cvm_concat_fuse_pass_test.cc
DEPS seqpool_cvm_concat_fuse_pass)
cc_test(
test_repeated_fc_relu_fuse_pass_cc
SRCS repeated_fc_relu_fuse_pass_test.cc
DEPS repeated_fc_relu_fuse_pass)
cc_test(
test_is_test_pass
SRCS is_test_pass_test.cc
DEPS is_test_pass)
cc_test(
test_simplify_with_basic_ops_pass
SRCS simplify_with_basic_ops_pass_test.cc
DEPS simplify_with_basic_ops_pass)
cc_test(
test_fc_elementwise_layernorm_fuse_pass_cc
SRCS fc_elementwise_layernorm_fuse_pass_test.cc
DEPS fc_elementwise_layernorm_fuse_pass)
cc_test(
test_skip_layernorm_fuse_pass
SRCS skip_layernorm_fuse_pass_test.cc
DEPS skip_layernorm_fuse_pass)
cc_test(
test_multihead_matmul_fuse_pass
SRCS multihead_matmul_fuse_pass_test.cc
DEPS multihead_matmul_fuse_pass)
cc_test(
test_fused_multi_transformer_encoder_pass
SRCS fused_multi_transformer_encoder_pass_test.cc
DEPS fused_multi_transformer_encoder_pass)
cc_test(
test_fused_multi_transformer_decoder_pass
SRCS fused_multi_transformer_decoder_pass_test.cc
DEPS fused_multi_transformer_decoder_pass)
cc_test(
test_fuse_multi_transformer_layer_pass
SRCS fuse_multi_transformer_layer_pass_test.cc
DEPS fuse_multi_transformer_layer_pass)
cc_test(
test_conv_bn_fuse_pass_cc
SRCS conv_bn_fuse_pass_test.cc
DEPS conv_bn_fuse_pass)
cc_test(
test_adaptive_pool2d_convert_global_pass
SRCS adaptive_pool2d_convert_global_pass_test.cc
DEPS adaptive_pool2d_convert_global_pass)
cc_test(
test_generate_pass_cc
SRCS generate_pass_test.cc
DEPS generate_pass pass_desc_proto)
# Delete/Cleanup pass tests
cc_test(
test_delete_op_device_pass
SRCS delete_op_device_pass_test.cc
DEPS delete_op_device_pass)
cc_test(
test_delete_assign_op_pass_cc
SRCS delete_assign_op_pass_test.cc
DEPS delete_assign_op_pass)
cc_test(
test_identity_op_clean_pass_cc
SRCS identity_op_clean_pass_test.cc
DEPS identity_op_clean_pass)
cc_test(
test_delete_dropout_pass_cc
SRCS delete_dropout_op_pass_test.cc
DEPS delete_dropout_op_pass)
cc_test(
test_delete_dequant_weight_linear_op_pass
SRCS delete_weight_dequant_linear_op_pass_test.cc
DEPS delete_weight_dequant_linear_op_pass)
cc_test(
test_delete_cast_op_pass
SRCS delete_cast_op_pass_test.cc
DEPS delete_cast_op_pass)
cc_test(
test_relu6_fuse_pass
SRCS relu6_fuse_pass_test.cc
DEPS relu6_fuse_pass)
# GPU/ROCM specific tests
if(WITH_GPU OR WITH_ROCM)
cc_test(
test_embedding_eltwise_layernorm_fuse_pass
SRCS embedding_eltwise_layernorm_fuse_pass_test.cc
DEPS embedding_eltwise_layernorm_fuse_pass)
cc_test(
test_cudnn_placement_pass
SRCS cudnn_placement_pass_test.cc
DEPS cudnn_placement_pass)
endif()
# Non-Windows specific tests
if(NOT WIN32)
cc_test(
test_sync_batch_norm_pass
SRCS sync_batch_norm_pass_test.cc
DEPS sync_batch_norm_pass)
cc_test(
test_dense_fc_to_sparse_pass_cc
SRCS dense_fc_to_sparse_pass_test.cc
DEPS fc_fuse_pass dense_fc_to_sparse_pass)
cc_test(
test_dense_multihead_matmul_to_sparse_pass
SRCS dense_multihead_matmul_to_sparse_pass_test.cc
DEPS multihead_matmul_fuse_pass dense_multihead_matmul_to_sparse_pass)
endif()
# OneDNN specific tests
if(WITH_ONEDNN)
add_subdirectory(onednn)
endif()
# XPU specific tests
if(WITH_XPU)
add_subdirectory(xpu)
endif()
# fusion_group tests (only on Linux/GPU/ROCM)
if(NOT APPLE
AND NOT WIN32
AND (WITH_GPU OR WITH_ROCM))
add_subdirectory(fusion_group)
endif()
@@ -0,0 +1,66 @@
/* Copyright (c) 2020 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/fluid/framework/ir/adaptive_pool2d_convert_global_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/op_version_registry.h"
#include "paddle/fluid/platform/enforce.h"
namespace paddle::framework::ir {
TEST(AdaptivePool2dConvertGlobalPass, basic) {
Layers layers;
auto* x = layers.data("x", {1, 92, 28, 28});
AttributeMap attrs;
attrs["adaptive"] = true;
attrs["ksize"] = std::vector<int>{1, 1};
attrs["pooling_type"] =
std::string("avg"); // adaptive has no effect on max pooling
layers.pool2d(x, false, &attrs);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass =
PassRegistry::Instance().Get("adaptive_pool2d_convert_global_pass");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
VLOG(3) << DebugString(graph);
bool global_pooling = false;
for (auto* node : graph->Nodes()) {
if (node->IsOp() && node->Op()->Type() == "pool2d") {
if (node->Op()->HasAttr("global_pooling")) {
global_pooling =
PADDLE_GET_CONST(bool, node->Op()->GetAttr("global_pooling"));
}
}
}
PADDLE_ENFORCE_EQ(
global_pooling,
true,
common::errors::PreconditionNotMet(
"The attribute of pool2d global_pooling should be true after fuse"));
}
TEST(AdaptivePool2dConvertGlobalPass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("adaptive_pool2d_convert_global_pass"));
}
} // namespace paddle::framework::ir
USE_PASS(adaptive_pool2d_convert_global_pass);
@@ -0,0 +1,107 @@
// Copyright (c) 2018 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/fluid/framework/ir/conv_bn_fuse_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
class VarDesc;
} // namespace framework
} // namespace paddle
namespace paddle {
namespace framework {
namespace ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
auto* data = tensor->mutable_data<float>(phi::CPUPlace());
int64_t numel = tensor->numel();
for (int64_t i = 0; i < numel; ++i) {
data[i] = 0;
}
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope(param_scope, "bias_1", {3});
AddVarToScope(param_scope, "scale", {3});
AddVarToScope(param_scope, "mean", {3});
AddVarToScope(param_scope, "variance", {3});
AddVarToScope(param_scope, "filters", {3, 3, 2, 2});
return param_scope;
}
void TestMain(const std::string& conv_type) {
// inputs operator output
// ------------------------------------------------------------------
// (in, filters, bias_0) conv -> conv_out
// (conv_out, scale,
// bias_1, mean, variance) batch_norm -> (...)
Layers layers;
auto* in = layers.data("in", {1, 3, 20, 20});
auto* filters = layers.data("filters", {3, 3, 2, 2}, true);
auto* bias_0 = layers.data("bias_0", {3}, true);
VarDesc* conv_out = nullptr;
if (conv_type == "conv_transpose") {
conv_out = layers.conv2d_transpose(in, filters, bias_0);
} else {
conv_out = layers.conv2d(in, filters, bias_0);
}
conv_out->SetShape({1, 3, 20, 20});
auto* scale = layers.data("scale", {3}, true);
auto* bias_1 = layers.data("bias_1", {3}, true);
auto* mean = layers.data("mean", {3}, true);
auto* variance = layers.data("variance", {3}, true);
layers.batch_norm(conv_out, scale, bias_1, mean, variance);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
auto pass = PassRegistry::Instance().Get(conv_type + "_bn_fuse_pass");
int num_bn_nodes_before = GetNumOpNodes(graph, "batch_norm");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_bn_nodes_after = GetNumOpNodes(graph, "batch_norm");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(
num_bn_nodes_before,
1,
common::errors::InvalidArgument(
"Before conv_bn_fuse_pass, number of batch norm op(%d) must be 1.",
num_bn_nodes_before));
PADDLE_ENFORCE_EQ(
num_bn_nodes_after,
0,
common::errors::InvalidArgument(
"After conv_bn_fuse_pass, number of batch norm op(%d) must be 0.",
num_bn_nodes_after));
}
TEST(ConvBNFusePass, conv2d) { TestMain("conv"); }
TEST(ConvBNFusePass, conv2d_transpose) { TestMain("conv_transpose"); }
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(conv_bn_fuse_pass);
@@ -0,0 +1,212 @@
// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/ir/cost_model.h"
#include "gtest/gtest.h"
#include "paddle/common/errors.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/phi/api/profiler/event.h"
namespace paddle {
namespace framework {
// Register test op
class FakeTestOpMaker : public OpProtoAndCheckerMaker {
public:
void Make() override {
AddInput("X", "").AsDuplicable();
AddInput("Y", "").AsDuplicable();
AddOutput("Out", "").AsDuplicable();
AddComment("");
}
};
class FakeTestOp : public OperatorBase {
public:
FakeTestOp(const std::string &type,
const VariableNameMap &inputs,
const VariableNameMap &outputs,
const AttributeMap &attrs)
: OperatorBase(type, inputs, outputs, attrs) {}
private:
void RunImpl(const Scope &scope, const phi::Place &place) const override {
// Fake RunImpl, for test only
Variable *var = scope.FindVar("X");
if (var != nullptr) {
phi::DenseTensor *tensor = var->GetMutable<phi::DenseTensor>();
tensor->mutable_data<float>(place);
}
int count = 0;
while (count <= 1000) {
++count;
}
}
};
} // namespace framework
} // namespace paddle
REGISTER_OPERATOR(fake_test_op,
paddle::framework::FakeTestOp,
paddle::framework::FakeTestOpMaker);
namespace paddle {
namespace framework {
ProgramDesc CreateTestProgram() {
// create a ProgramDesc:
// Z = fake_test_op(X, Y)
// Out = fake_test_op(Z, W)
ProgramDesc program;
auto *global_block = program.MutableBlock(0);
auto *x = global_block->Var("X");
x->SetType(proto::VarType::DENSE_TENSOR);
x->SetLoDLevel(0);
x->SetDataType(proto::VarType::FP32);
x->SetShape({1000, 784});
auto *y = global_block->Var("Y");
y->SetType(proto::VarType::DENSE_TENSOR);
y->SetLoDLevel(0);
y->SetDataType(proto::VarType::FP32);
y->SetShape({784, 100});
auto *op0 = global_block->AppendOp();
op0->SetType("fake_test_op");
op0->SetInput("X", {x->Name()});
op0->SetInput("Y", {y->Name()});
auto *z = global_block->Var("Z");
z->SetType(proto::VarType::DENSE_TENSOR);
op0->SetOutput("Out", {z->Name()});
auto *w = global_block->Var("W");
w->SetType(proto::VarType::DENSE_TENSOR);
w->SetLoDLevel(0);
w->SetDataType(proto::VarType::FP32);
w->SetShape({100, 10});
auto *op1 = global_block->AppendOp();
op1->SetType("fake_test_op");
op1->SetInput("X", {z->Name()});
op1->SetInput("Y", {w->Name()});
auto *out = global_block->Var("Out");
out->SetType(proto::VarType::DENSE_TENSOR);
op1->SetOutput("Out", {out->Name()});
return program;
}
TEST(CostModelTest, TestProfileMeasure_EmptyProgram) {
CostModel cost_model;
ProgramDesc empty_program;
CostData cost_data =
cost_model.ProfileMeasure(empty_program, empty_program, "cpu", {"time"});
EXPECT_EQ(cost_data.GetWholeTimeMs(), 0);
}
TEST(CostModelTest, TestProfileMeasure_Program) {
CostModel cost_model;
ProgramDesc program = CreateTestProgram();
ProgramDesc empty_program;
CostData cost_data =
cost_model.ProfileMeasure(program, empty_program, "cpu", {"time"});
double op0_time_ms = cost_data.GetOpTimeMs(0);
double op1_time_ms = cost_data.GetOpTimeMs(1);
EXPECT_GT(op0_time_ms, 0);
EXPECT_GT(op1_time_ms, 0);
EXPECT_GT(cost_data.GetWholeTimeMs(), op0_time_ms + op1_time_ms);
}
TEST(CostModelTest, TestProfileMeasure_UnsupportedDevice) {
CostModel cost_model;
ProgramDesc program = CreateTestProgram();
ProgramDesc empty_program;
EXPECT_THROW(cost_model.ProfileMeasure(
program, empty_program, "wrong_device", {"time"}),
paddle::platform::EnforceNotMet);
}
TEST(CostDataTest, TestGetGraphProgram) {
CostData cost_data;
EXPECT_EQ(cost_data.GetGraph(), nullptr);
EXPECT_EQ(cost_data.GetProgram(), nullptr);
}
TEST(CostDataTest, TestUninitialized) {
CostData cost_data;
EXPECT_EQ(cost_data.GetWholeMemoryBytes(), CostData::NOT_MEASURED);
EXPECT_EQ(cost_data.GetWholeTimeMs(), CostData::NOT_MEASURED);
}
TEST(CostDataTest, TestEmptyProgram) {
CostData cost_data;
ProgramDesc empty_program("");
EXPECT_EQ(cost_data.SetCostData(empty_program, {}), true);
EXPECT_EQ(cost_data.GetWholeMemoryBytes(), 0);
EXPECT_EQ(cost_data.GetWholeTimeMs(), 0);
}
TEST(CostDataTest, TestEmptyTimeEvent) {
CostData cost_data;
ProgramDesc program = CreateTestProgram();
EXPECT_EQ(cost_data.SetCostData(program, {}), false);
EXPECT_EQ(cost_data.GetWholeMemoryBytes(), CostData::NOT_MEASURED);
EXPECT_EQ(cost_data.GetWholeTimeMs(), CostData::NOT_MEASURED);
}
TEST(CostDataTest, TestNoOpEvent) {
CostData cost_data;
ProgramDesc program = CreateTestProgram();
std::vector<phi::Event> thread_events;
thread_events.push_back(
phi::Event(phi::EventType::kPushRange, "not exist name", 0));
std::vector<std::vector<phi::Event>> time_events{thread_events};
EXPECT_EQ(cost_data.SetCostData(program, time_events), false);
}
TEST(CostDataTest, TestNoOpPopEvent) {
CostData cost_data;
ProgramDesc program = CreateTestProgram();
std::vector<phi::Event> thread_events;
thread_events.push_back(
phi::Event(phi::EventType::kPushRange, "fake_test_op", 0));
std::vector<std::vector<phi::Event>> time_events{thread_events};
EXPECT_EQ(cost_data.SetCostData(program, time_events), false);
}
TEST(CostDataTest, TestNoWholeEvent) {
CostData cost_data;
ProgramDesc program = CreateTestProgram();
std::vector<phi::Event> thread_events;
thread_events.push_back(
phi::Event(phi::EventType::kPushRange, "fake_test_op", 0));
thread_events.push_back(
phi::Event(phi::EventType::kPopRange, "fake_test_op", 0));
thread_events.push_back(
phi::Event(phi::EventType::kPushRange, "fake_test_op", 0));
thread_events.push_back(
phi::Event(phi::EventType::kPopRange, "fake_test_op", 0));
std::vector<std::vector<phi::Event>> time_events{thread_events};
EXPECT_EQ(cost_data.SetCostData(program, time_events), false);
}
} // namespace framework
} // namespace paddle
@@ -0,0 +1,131 @@
// Copyright (c) 2019 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/fluid/framework/ir/cudnn_placement_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/operator.h"
namespace paddle::framework::ir {
class PlacementPassTest {
private:
void RegisterOpKernel() {
static bool is_registered = false;
if (!is_registered) {
auto& all_kernels = OperatorWithKernel::AllOpKernels();
phi::GPUPlace place = phi::GPUPlace(0);
OpKernelType plain_kernel_type = OpKernelType(proto::VarType::FP32,
place,
DataLayout::kAnyLayout,
LibraryType::kPlain);
OpKernelType cudnn_kernel_type = OpKernelType(proto::VarType::FP32,
place,
DataLayout::kAnyLayout,
LibraryType::kCUDNN);
auto fake_kernel_func = [](const ExecutionContext&) -> void {
static int num_calls = 0;
num_calls++;
};
all_kernels["conv2d"][cudnn_kernel_type] = fake_kernel_func;
all_kernels["pool2d"][cudnn_kernel_type] = fake_kernel_func;
all_kernels["depthwise_conv2d"][plain_kernel_type] = fake_kernel_func;
all_kernels["relu"][plain_kernel_type] = fake_kernel_func;
is_registered = true;
}
}
public:
void MainTest(std::initializer_list<std::string> cudnn_enabled_op_types,
unsigned expected_use_cudnn_true_count) {
// operator use_cudnn
// --------------------------------------------------
// (a,b)->concat->c -
// (c,weights,bias)->conv2d->f false
// f->relu->g -
// g->pool2d->h false
// (h,weights2,bias2)->depthwise_conv2d->k false
// k->relu->l -
Layers layers;
VarDesc* a = layers.data("a");
VarDesc* b = layers.data("b");
VarDesc* c = layers.concat(std::vector<VarDesc*>({a, b}));
VarDesc* weights_0 = layers.data("weights_0");
VarDesc* bias_0 = layers.data("bias_0");
VarDesc* f = layers.conv2d(c, weights_0, bias_0, false);
VarDesc* g = layers.relu(f);
VarDesc* h = layers.pool2d(g, false);
VarDesc* weights_1 = layers.data("weights_1");
VarDesc* bias_1 = layers.data("bias_1");
VarDesc* k = layers.depthwise_conv2d(h, weights_1, bias_1, false);
layers.relu(k);
RegisterOpKernel();
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("cudnn_placement_pass");
pass->Set("cudnn_enabled_op_types",
new std::unordered_set<std::string>(cudnn_enabled_op_types));
graph.reset(pass->Apply(graph.release()));
unsigned use_cudnn_true_count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp() && node->Op()) {
auto* op = node->Op();
if (op->HasAttr("use_cudnn") &&
PADDLE_GET_CONST(bool, op->GetAttr("use_cudnn"))) {
++use_cudnn_true_count;
}
}
}
EXPECT_EQ(use_cudnn_true_count, expected_use_cudnn_true_count);
}
void PlacementNameTest() {
auto pass = PassRegistry::Instance().Get("cudnn_placement_pass");
EXPECT_EQ(static_cast<PlacementPassBase*>(pass.get())->GetPlacementName(),
"cuDNN");
}
};
TEST(CUDNNPlacementPass, enable_conv2d) {
// 1 conv2d
PlacementPassTest().MainTest({"conv2d"}, 1);
}
TEST(CUDNNPlacementPass, enable_relu_pool) {
// 1 conv2d + 1 pool2d
PlacementPassTest().MainTest({"conv2d", "pool2d"}, 2);
}
TEST(CUDNNPlacementPass, enable_all) {
// 1 conv2d + 1 pool2d
// depthwise_conv2d does not have CUDNN kernel.
PlacementPassTest().MainTest({}, 2);
}
TEST(CUDNNPlacementPass, placement_name) {
PlacementPassTest().PlacementNameTest();
}
} // namespace paddle::framework::ir
USE_PASS(cudnn_placement_pass);
@@ -0,0 +1,46 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
TEST(delete_assign_op_pass, basic) {
ProgramDesc program;
auto* x_var = program.MutableBlock(0)->Var("assign_x");
auto* out_var = program.MutableBlock(0)->Var("assign_out");
out_var->SetName(x_var->Name());
OpDesc* assign_op = program.MutableBlock(0)->AppendOp();
assign_op->SetType("assign");
assign_op->SetInput("X", {x_var->Name()});
assign_op->SetOutput("Out", {out_var->Name()});
std::unique_ptr<Graph> graph(new Graph(program));
auto pass = PassRegistry::Instance().Get("delete_assign_op_pass");
graph.reset(pass->Apply(graph.release()));
int assign_num = GetNumOpNodes(graph, "assign");
PADDLE_ENFORCE_EQ(
assign_num,
0,
common::errors::PreconditionNotMet(
"graph should have 0 assign after delete_assign_op_pass, "
"but actually has %d.",
assign_num));
}
} // namespace paddle::framework::ir
USE_PASS(delete_assign_op_pass);
@@ -0,0 +1,318 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
auto* cpu_ctx = static_cast<phi::CPUContext*>(
phi::DeviceContextPool::Instance().Get(phi::CPUPlace()));
cpu_ctx->Alloc<float>(tensor);
}
VarDesc* Data(paddle::framework::BlockDesc* block,
std::string name,
std::vector<int64_t> shape = {},
bool is_persistable = false,
proto::VarType::Type data_type = proto::VarType::FP32) {
auto* var = block->Var(name);
var->SetType(proto::VarType::DENSE_TENSOR);
var->SetDataType(data_type);
var->SetShape(shape);
var->SetPersistable(is_persistable);
return var;
}
VarDesc* AddWriteToArray(BlockDesc* block,
std::vector<VarDesc*> x,
VarDesc* i,
VarDesc* out = nullptr) {
if (out == nullptr) {
out = Data(block, x[0]->Name() + "_out");
}
OpDesc* op = block->AppendOp();
op->SetType("write_to_array");
std::vector<std::string> x_names;
x_names.reserve(x.size());
for (auto k : x) {
x_names.push_back(k->Name());
}
op->SetInput("X", x_names);
op->SetInput("I", {i->Name()});
op->SetOutput("Out", {out->Name()});
return out;
}
VarDesc* AddReadFromArray(BlockDesc* block, VarDesc* x, VarDesc* i) {
auto* out = Data(block, x->Name() + "_out");
OpDesc* op = block->AppendOp();
op->SetType("read_from_array");
op->SetInput("X", {x->Name()});
op->SetInput("I", {i->Name()});
op->SetOutput("Out", {out->Name()});
return out;
}
VarDesc* AddCast(BlockDesc* block,
VarDesc* input,
int in_dtype = 5,
int out_dtype = 5) {
VarDesc* out = Data(block, input->Name() + "_out");
OpDesc* op = block->AppendOp();
op->SetType("cast");
op->SetInput("X", {input->Name()});
op->SetOutput("Out", {out->Name()});
op->SetAttr("in_dtype", in_dtype);
op->SetAttr("out_dtype", out_dtype);
return out;
}
VarDesc* AddLodReset(BlockDesc* block, VarDesc* input) {
VarDesc* out = Data(block, input->Name() + "_out");
OpDesc* op = block->AppendOp();
op->SetType("lod_reset");
op->SetInput("X", {input->Name()});
op->SetOutput("Out", {out->Name()});
return out;
}
std::vector<VarDesc*> AddBeamSearchDecode(BlockDesc* block,
VarDesc* ids,
VarDesc* scores) {
VarDesc* out_ids = Data(block, ids->Name() + "_out");
VarDesc* out_scores = Data(block, scores->Name() + "_out");
OpDesc* op = block->AppendOp();
op->SetType("beam_search_decode");
op->SetInput("Ids", {ids->Name()});
op->SetInput("Scores", {scores->Name()});
op->SetOutput("SentenceIds", {out_ids->Name()});
op->SetOutput("SentenceScores", {out_scores->Name()});
return {out_ids, out_scores};
}
int GetOpNum(Graph* graph, std::string op_type = "") {
int num_nodes = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp() && node->Op() &&
(node->Op()->Type() == op_type || op_type.empty())) {
num_nodes++;
}
}
return num_nodes;
}
TEST(ApplyCastWriteReadPass, basic) {
paddle::framework::ProgramDesc program;
auto* block0 = program.MutableBlock(0);
auto* block1 = program.AppendBlock(*block0);
auto* write_0_x = Data(block0, "write_0_x", {1});
auto* write_0_i = Data(block0, "write_0_i", {1});
auto* write_0_out = AddWriteToArray(block0, {write_0_x}, write_0_i);
OpDesc* while_loop = block0->AppendOp();
while_loop->SetType("while");
while_loop->SetInput("X", {write_0_out->Name()});
while_loop->SetOutput("Out", {write_0_out->Name()});
auto* cast_1_0_in = Data(block1, "cast_1_0", {1});
auto* cast_1_0_out = AddCast(block1, cast_1_0_in, 4, 5);
auto* write_1_i = Data(block1, "write_1_i", {1});
auto* write_1_out = Data(block1, write_0_out->Name(), {1});
AddWriteToArray(block1, {cast_1_0_out}, write_1_i, write_1_out);
auto* read_1_i = Data(block1, "read_1_i", {1});
auto* read_1_out = AddReadFromArray(block1, write_1_out, read_1_i);
AddCast(block1, read_1_out, 5, 4);
std::unique_ptr<ir::Graph> graph(new ir::Graph(program));
auto scope = new Scope();
graph->Set("__param_scope__", scope);
auto pass = PassRegistry::Instance().Get("delete_cast_op_pass");
pass->Apply(graph.get());
int cast_num_in_graph1 = GetOpNum(graph->GetSubGraph(1), "cast");
PADDLE_ENFORCE_EQ(cast_num_in_graph1,
0,
common::errors::PreconditionNotMet(
"graph1 should have 0 cast after delete_cast_op_pass, "
"but actually has %d.",
cast_num_in_graph1));
int cast_num_in_graph0 = GetOpNum(graph.get(), "cast");
PADDLE_ENFORCE_EQ(cast_num_in_graph0,
1,
common::errors::PreconditionNotMet(
"graph0 should have 1 cast after delete_cast_op_pass, "
"but actually has %d.",
cast_num_in_graph0));
}
TEST(ApplyCastLodResetWriteReadPass, basic) {
paddle::framework::ProgramDesc program;
auto* block0 = program.MutableBlock(0);
auto* block1 = program.AppendBlock(*block0);
auto* write_0_x = Data(block0, "write_0_x", {1});
auto* write_0_i = Data(block0, "write_0_i", {1});
auto* write_0_out = AddWriteToArray(block0, {write_0_x}, write_0_i);
OpDesc* while_loop = block0->AppendOp();
while_loop->SetType("while");
while_loop->SetInput("X", {write_0_out->Name()});
while_loop->SetOutput("Out", {write_0_out->Name()});
auto* ids = Data(block0, "ids", {1});
AddBeamSearchDecode(block0, ids, write_0_out);
auto* cast_1_0_in = Data(block1, "cast_1_0", {1});
auto* cast_1_0_out = AddCast(block1, cast_1_0_in, 4, 5);
auto* lod_reset_out = AddLodReset(block1, cast_1_0_out);
auto* write_1_i = Data(block1, "write_1_i", {1});
auto* write_1_out = Data(block1, write_0_out->Name(), {1});
AddWriteToArray(block1, {lod_reset_out}, write_1_i, write_1_out);
auto* read_1_i = Data(block1, "read_1_i", {1});
auto* read_1_out = AddReadFromArray(block1, write_1_out, read_1_i);
AddCast(block1, read_1_out, 5, 4);
std::unique_ptr<ir::Graph> graph(new ir::Graph(program));
auto scope = new Scope();
graph->Set("__param_scope__", scope);
auto pass = PassRegistry::Instance().Get("delete_cast_op_pass");
pass->Apply(graph.get());
int cast_num_in_graph1 = GetOpNum(graph->GetSubGraph(1), "cast");
PADDLE_ENFORCE_EQ(cast_num_in_graph1,
0,
common::errors::PreconditionNotMet(
"graph1 should have 0 cast after delete_cast_op_pass, "
"but actually has %d.",
cast_num_in_graph1));
int cast_num_in_graph0 = GetOpNum(graph.get(), "cast");
PADDLE_ENFORCE_EQ(cast_num_in_graph0,
2,
common::errors::PreconditionNotMet(
"graph0 should have 2 cast after delete_cast_op_pass, "
"but actually has %d.",
cast_num_in_graph0));
}
TEST(ApplyCastIndexSamplePass, basic) {
paddle::framework::ProgramDesc program;
auto* block = program.MutableBlock(0);
auto* cast0_in = Data(block, "cast0_in", {1});
auto* cast0_out = AddCast(block, cast0_in, 4, 5);
auto* index_sample_out = Data(block, "index_sample_out", {1});
OpDesc* index_sample = block->AppendOp();
index_sample->SetType("index_sample");
index_sample->SetInput("X", {cast0_out->Name()});
index_sample->SetOutput("Out", {index_sample_out->Name()});
AddCast(block, index_sample_out, 5, 4);
std::unique_ptr<ir::Graph> graph(new ir::Graph(program));
auto scope = new Scope();
graph->Set("__param_scope__", scope);
auto pass = PassRegistry::Instance().Get("delete_cast_op_pass");
pass->Apply(graph.get());
int cast_num_in_graph = GetOpNum(graph->GetSubGraph(0), "cast");
PADDLE_ENFORCE_EQ(GetOpNum(graph->GetSubGraph(0), "cast"),
0,
common::errors::PreconditionNotMet(
"graph should have 0 cast after delete_cast_op_pass, "
"but actually has %d.",
cast_num_in_graph));
}
TEST(ApplyCastScatterPass, basic) {
paddle::framework::ProgramDesc program;
auto* block = program.MutableBlock(0);
auto* cast0_in = Data(block, "cast0_in", {1});
auto* cast0_out = AddCast(block, cast0_in, 4, 5);
auto* cast1_in = Data(block, "cast1_in", {1});
auto* cast1_out = AddCast(block, cast1_in, 4, 5);
auto* scatter_out = Data(block, "scatter_out", {1});
OpDesc* scatter = block->AppendOp();
scatter->SetType("scatter");
scatter->SetInput("X", {cast0_out->Name()});
scatter->SetInput("Updates", {cast1_out->Name()});
scatter->SetOutput("Out", {scatter_out->Name()});
AddCast(block, scatter_out, 5, 4);
std::unique_ptr<ir::Graph> graph(new ir::Graph(program));
auto scope = new Scope();
graph->Set("__param_scope__", scope);
auto pass = PassRegistry::Instance().Get("delete_cast_op_pass");
pass->Apply(graph.get());
int cast_num_in_graph = GetOpNum(graph->GetSubGraph(0), "cast");
PADDLE_ENFORCE_EQ(GetOpNum(graph->GetSubGraph(0), "cast"),
0,
common::errors::PreconditionNotMet(
"graph should have 0 cast after delete_cast_op_pass, "
"but actually has %d.",
cast_num_in_graph));
}
TEST(ApplyCastLookupTablePass, basic) {
paddle::framework::ProgramDesc program;
auto* block = program.MutableBlock(0);
auto* lookup_table_w = Data(block, "lookup_table_w", {1}, true);
auto* lookup_table_out = Data(block, "scatter_out", {1});
OpDesc* lookup_table = block->AppendOp();
lookup_table->SetType("lookup_table_v2");
lookup_table->SetInput("W", {lookup_table_w->Name()});
lookup_table->SetOutput("Out", {lookup_table_out->Name()});
auto* cast_out = AddCast(block, lookup_table_out, 5, 4);
OpDesc* relu = block->AppendOp();
relu->SetType("relu");
relu->SetInput("X", {cast_out->Name()});
relu->SetOutput("Out", {"relu_out"});
std::unique_ptr<ir::Graph> graph(new ir::Graph(program));
auto scope = new Scope();
AddVarToScope(scope, lookup_table_w->Name(), {1});
graph->Set("__param_scope__", scope);
auto pass = PassRegistry::Instance().Get("delete_cast_op_pass");
pass->Apply(graph.get());
int cast_num_in_graph = GetOpNum(graph->GetSubGraph(0), "cast");
PADDLE_ENFORCE_EQ(GetOpNum(graph->GetSubGraph(0), "cast"),
0,
common::errors::PreconditionNotMet(
"graph should have 0 cast after delete_cast_op_pass, "
"but actually has %d.",
cast_num_in_graph));
}
TEST(ApplyCastPass, basic) {
paddle::framework::ProgramDesc program;
auto* block = program.MutableBlock(0);
auto* cast0_in = Data(block, "cast0_in", {1});
AddCast(block, cast0_in, 3, 3);
std::unique_ptr<ir::Graph> graph(new ir::Graph(program));
auto scope = new Scope();
graph->Set("__param_scope__", scope);
auto pass = PassRegistry::Instance().Get("delete_cast_op_pass");
pass->Apply(graph.get());
int cast_num_in_graph = GetOpNum(graph->GetSubGraph(0), "cast");
PADDLE_ENFORCE_EQ(GetOpNum(graph->GetSubGraph(0), "cast"),
0,
common::errors::PreconditionNotMet(
"graph should have 0 cast after delete_cast_op_pass, "
"but actually has %d.",
cast_num_in_graph));
}
} // namespace paddle::framework::ir
USE_PASS(delete_cast_op_pass);
@@ -0,0 +1,92 @@
/* Copyright (c) 2019 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/fluid/framework/ir/delete_dropout_op_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
TEST(DeleteDropoutOpsPass, dropout) {
for (std::string dropout_implementation :
{"downgrade_in_infer", "upscale_in_train"}) {
for (auto inplace : {false, true}) {
if (dropout_implementation == "downgrade_in_infer" && inplace == true) {
continue;
}
LOG(INFO) << "dropout_implementation: " << dropout_implementation
<< ", inplace: " << inplace;
Layers layers;
// (x, y) -> mul -> tmp_0
// (tmp_0) -> dropout -> (tmp_1)
// (tmp_1, z) -> elementwise_add -> (tmp_2)
// or
// (tmp_1, z) -> elementwise_add -> (tmp_0)
auto* x = layers.data("x");
auto* y = layers.data("y");
auto* z = layers.data("z");
auto* mul_out = layers.mul(x, y);
auto* dropout_out = layers.dropout(mul_out, 0.5f, dropout_implementation);
if (inplace) {
layers.elementwise_add(dropout_out, z, mul_out);
} else {
layers.elementwise_add(dropout_out, z);
}
std::unique_ptr<Graph> graph(new Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("delete_dropout_op_x_pass");
int num_dropout_nodes_before = GetNumOpNodes(graph, "dropout");
int num_scale_nodes_before = GetNumOpNodes(graph, "scale");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_dropout_nodes_after = GetNumOpNodes(graph, "dropout");
int num_scale_nodes_after = GetNumOpNodes(graph, "scale");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(
num_dropout_nodes_after,
0,
common::errors::InvalidArgument("num_dropout_nodes_after = %d.",
num_dropout_nodes_after));
if (dropout_implementation == "downgrade_in_infer") {
PADDLE_ENFORCE_EQ(
num_dropout_nodes_before,
num_scale_nodes_after - num_scale_nodes_before,
common::errors::InvalidArgument(
"num_dropout_nodes_before = %d, num_scale_nodes_after = %d, "
"num_scale_nodes_before = %d.",
num_dropout_nodes_before,
num_scale_nodes_after,
num_scale_nodes_before));
} else {
PADDLE_ENFORCE_EQ(
num_scale_nodes_after - num_scale_nodes_before,
0,
common::errors::InvalidArgument(
"num_scale_nodes_after = %d, num_scale_nodes_before = %d.",
num_scale_nodes_after,
num_scale_nodes_before));
}
}
}
}
} // namespace paddle::framework::ir
USE_PASS(delete_dropout_op_x_pass);
@@ -0,0 +1,47 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
TEST(delete_op_device_pass, relu) {
ProgramDesc program;
auto* x_var = program.MutableBlock(0)->Var("relu_x");
auto* out_var = program.MutableBlock(0)->Var("relu_out");
OpDesc* relu_op = program.MutableBlock(0)->AppendOp();
relu_op->SetType("relu");
relu_op->SetInput("X", {x_var->Name()});
relu_op->SetOutput("Out", {out_var->Name()});
relu_op->SetAttr("op_device", std::string{"gpu:0"});
std::unique_ptr<Graph> graph(new Graph(program));
auto pass = PassRegistry::Instance().Get("delete_op_device_pass");
graph.reset(pass->Apply(graph.release()));
for (auto* node : graph->Nodes()) {
if (!node->IsOp()) continue;
if (node->Op()->Type() == "relu") {
PADDLE_ENFORCE(!node->Op()->HasAttr("op_device"),
common::errors::InvalidArgument(
"Run delete_op_device_pass failed. Relu op still has "
"'op_device' attr."));
}
}
}
} // namespace paddle::framework::ir
USE_PASS(delete_op_device_pass);
@@ -0,0 +1,141 @@
/* Copyright (c) 2022 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/fluid/framework/ir/delete_weight_dequant_linear_op_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
namespace ir {
template <typename T>
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
auto* dev_ctx = static_cast<phi::CPUContext*>(
phi::DeviceContextPool::Instance().Get(phi::CPUPlace()));
dev_ctx->HostAlloc<T>(tensor, tensor->numel() * sizeof(T));
}
template <typename T>
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope<T>(param_scope, "scale", {1});
return param_scope;
}
TEST(DeleteWeightDequantLinearOpPass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (weight, scale) dequantize_linear -> dequantized_weight
// (x, dequantized_weight) matmul/fc/conv -> matmul_out
// (dequantized_weight) while -> [optional]
Layers layers;
auto* x = layers.data("x", {1, 128, 768});
auto* weight = layers.data("weight", {768, 768}, true);
auto* scale = layers.data("scale", {1}, true);
auto* zero_point = layers.data("zero_point", {1}, true);
auto* dequantized_weight =
layers.dequantize_linear(weight, scale, zero_point);
layers.matmul_v2(x, dequantized_weight);
layers.while_loop({dequantized_weight});
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope<float>());
auto pass =
PassRegistry::Instance().Get("delete_weight_dequant_linear_op_pass");
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_dequant_nodes_after = GetNumOpNodes(graph, "dequantize_linear");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(
num_nodes_before,
num_nodes_after + 3,
common::errors::InvalidArgument(
"After pass, the number of nodes should be reduced by 3, but the "
"number before pass is %d, after pass is %d.",
num_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_dequant_nodes_after,
0,
common::errors::InvalidArgument(
"After pass, the number of nodes of type "
"'dequantize_linear' should be 1, not %d.",
num_dequant_nodes_after));
}
TEST(DeleteWeightDequantLinearOpPass, basic_fp16) {
// inputs operator output
// --------------------------------------------------------------------
// (weight, scale) dequantize_linear -> dequantized_weight
// (x, dequantized_weight) matmul/fc/conv -> matmul_out
// (dequantized_weight) while -> [optional]
Layers layers;
auto* x = layers.data("x", {1, 128, 768});
auto* weight = layers.data("weight", {768, 768}, true);
auto* scale = layers.data("scale", {1}, true);
auto* zero_point = layers.data("zero_point", {1}, true);
auto* dequantized_weight =
layers.dequantize_linear(weight, scale, zero_point);
layers.matmul_v2(x, dequantized_weight);
layers.while_loop({dequantized_weight});
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope<phi::dtype::float16>());
auto pass =
PassRegistry::Instance().Get("delete_weight_dequant_linear_op_pass");
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_dequant_nodes_after = GetNumOpNodes(graph, "dequantize_linear");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(
num_nodes_before,
num_nodes_after + 3,
common::errors::InvalidArgument(
"After pass, the number of nodes should be reduced by 3, but the "
"number before pass is %d, after pass is %d.",
num_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_dequant_nodes_after,
0,
common::errors::InvalidArgument(
"After pass, the number of nodes of type "
"'dequantize_linear' should be 1, not %d.",
num_dequant_nodes_after));
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(delete_weight_dequant_linear_op_pass);
@@ -0,0 +1,109 @@
// Copyright (c) 2022 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/fluid/framework/ir/dense_fc_to_sparse_pass.h"
#include "paddle/fluid/framework/ir/fc_fuse_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
tensor->mutable_data<float>(phi::CPUPlace());
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope(param_scope, "conv2d_filters_0", {});
AddVarToScope(param_scope, "conv2d_bias_0", {});
AddVarToScope(param_scope, "weights_0_sparse_2_4", {});
AddVarToScope(param_scope, "weights_1", {});
AddVarToScope(param_scope, "bias_1", {});
AddVarToScope(param_scope, "bias_2", {});
return param_scope;
}
TEST(FCFusePass, basic) {
// inputs operator output
// --------------------------------------------------------
// (a, filters_0 bias_0) conv2d -> conv2d_out
// conv2d_out relu -> relu_out_0
// (relu_out_0, weights_0_sparse_2_4) mul -> mul_out_0
// (mul_out_0, bias_1) elementwise_add -> add_out_0
// add_out_0 relu -> relu_out_1
// (relu_out_1, weights_1) mul -> mul_out_1
// (mul_out_1, bias_2) elementwise_add -> add_out_1
Layers layers;
auto* a = layers.data("a");
auto* filters_0 = layers.data("conv2d_filters_0", {}, true);
auto* bias_0 = layers.data("conv2d_bias_0", {}, true);
auto* conv2d_out = layers.conv2d(a, filters_0, bias_0, false);
auto* relu_out_0 = layers.relu(conv2d_out);
auto* weights_0 = layers.data("weights_0_sparse_2_4", {5, 4}, true);
auto* mul_out_0 = layers.mul(relu_out_0, weights_0);
auto* bias_1 = layers.data("bias_1", {4}, true);
auto* add_out_0 = layers.elementwise_add(mul_out_0, bias_1, nullptr, 1);
auto* relu_out_1 = layers.relu(add_out_0);
auto* weights_1 = layers.data("weights_1", {8, 9}, true);
auto* mul_out_1 = layers.mul(relu_out_1, weights_1);
auto* bias_2 = layers.data("bias_2", {1, 9}, true);
auto* add_out_1 = layers.elementwise_add(mul_out_1, bias_2, nullptr, 1);
VLOG(4) << add_out_1;
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto fuse_pass = PassRegistry::Instance().Get("fc_fuse_pass");
auto sparse_pass = PassRegistry::Instance().Get("dense_fc_to_sparse_pass");
fuse_pass->Set("use_gpu", new bool(true));
sparse_pass->Set("use_gpu", new bool(true));
graph->Set("__param_scope__", CreateParamScope());
int num_nodes_before = static_cast<int>(graph->Nodes().size());
int num_mul_nodes_before = GetNumOpNodes(graph, "mul");
VLOG(3) << DebugString(graph);
graph.reset(fuse_pass->Apply(graph.release()));
graph.reset(sparse_pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fc_nodes_after = GetNumOpNodes(graph, "fc");
int num_sparse_fc_nodes_after = GetNumOpNodes(graph, "sparse_fc");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 6,
common::errors::InvalidArgument(
"num_nodes_before=%d, num_nodes_after=%d.",
num_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fc_nodes_after,
1,
common::errors::InvalidArgument("num_fc_nodes_after=%d.",
num_fc_nodes_after));
PADDLE_ENFORCE_EQ(num_mul_nodes_before,
num_fc_nodes_after + num_sparse_fc_nodes_after,
common::errors::InvalidArgument(
"num_mul_nodes_before=%d, num_fc_nodes_after=%d + "
"num_sparse_fc_nodes_after=%d.",
num_mul_nodes_before,
num_fc_nodes_after,
num_sparse_fc_nodes_after));
}
} // namespace paddle::framework::ir
USE_PASS(fc_fuse_pass);
USE_PASS(dense_fc_to_sparse_pass);
@@ -0,0 +1,152 @@
/* Copyright (c) 2022 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/fluid/framework/ir/dense_multihead_matmul_to_sparse_pass.h" // NOLINT
#include "paddle/fluid/framework/ir/multihead_matmul_fuse_pass.h" // NOLINT
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/op_version_registry.h"
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
tensor->mutable_data<float>(phi::CPUPlace());
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope(param_scope, "weights0_sparse_2_4", {768, 768});
AddVarToScope(param_scope, "weights1_sparse_2_4", {768, 768});
AddVarToScope(param_scope, "weights2_sparse_2_4", {768, 768});
AddVarToScope(param_scope, "bias_0", {768});
AddVarToScope(param_scope, "bias_1", {768});
AddVarToScope(param_scope, "bias_2", {768});
AddVarToScope(param_scope, "biasqk", {768});
AddVarToScope(param_scope, "weightsl", {768, 768});
return param_scope;
}
TEST(DenseMultiHeadMatmulToSparsePass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x) layer_norm -> layer_norm_out
// (layer_norm_out, weights_0_sparse_2_4) mul -> mul_out0
// (layer_norm_out, weights_1_sparse_2_4) mul -> mul_out1
// (layer_norm_out, weights_2_sparse_2_4) mul -> mul_out2
// (mul_out0, bias_0) elementweise_add -> eltadd_0
// (mul_out1, bias_1) elementweise_add -> eltadd_1
// (mul_out2, bias_2) elementweise_add -> eltadd_2
// (eltadd_0) reshape2 -> reshape_0
// (eltadd_1) reshape2 -> reshape_1
// (eltadd_2) reshape2 -> reshape_2
// (reshape_0) transpose2 -> transpose_0
// (reshape_1) transpose2 -> transpose_1
// (reshape_2) transpose2 -> transpose_2
// (transpose_0) scale -> scale_0
// (scale_0, transpose_1) matmul -> matmul_qk
// (matmul_qk, bias_qk) elementwise_add -> eltadd_qk
// (eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) mul -> mul_qkv
Layers layers;
auto* x = layers.data("x", {1, 128, 768});
auto out = layers.layer_norm(x);
auto* layer_out = out[0];
auto* weights_0 = layers.data("weights0_sparse_2_4", {768, 768}, true);
auto* weights_1 = layers.data("weights1_sparse_2_4", {768, 768}, true);
auto* weights_2 = layers.data("weights2_sparse_2_4", {768, 768}, true);
auto* mul_out_0 = layers.mul(layer_out, weights_0, nullptr, 2);
auto* mul_out_1 = layers.mul(layer_out, weights_1, nullptr, 2);
auto* mul_out_2 = layers.mul(layer_out, weights_2, nullptr, 2);
auto* b0 = layers.data("bias_0", {768}, true);
auto* b1 = layers.data("bias_1", {768}, true);
auto* b2 = layers.data("bias_2", {768}, true);
auto* elementwise_out_0 = layers.elementwise_add(mul_out_0, b0, nullptr, 2);
auto* elementwise_out_1 = layers.elementwise_add(mul_out_1, b1, nullptr, 2);
auto* elementwise_out_2 = layers.elementwise_add(mul_out_2, b2, nullptr, 2);
std::vector<int> shape = {1, 128, 12, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
auto* reshape_1 = layers.reshape2(elementwise_out_1, shape, true);
auto* reshape_2 = layers.reshape2(elementwise_out_2, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto* transpose_1 = layers.transpose2(reshape_1, axis, true);
auto* transpose_2 = layers.transpose2(reshape_2, axis, true);
auto* scale_0 = layers.scale(transpose_0, 0.125, 0, false);
auto* matmul_qk = layers.matmul(scale_0, transpose_1, nullptr, false, true);
auto* bqk = layers.data("biasqk", {1, 12, 128, 128}, true);
auto* elementwise_qk = layers.elementwise_add(matmul_qk, bqk);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
auto* matmul_qkv = layers.matmul(softmax_qk, transpose_2);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 768}, true);
auto* weights_l = layers.data("weightsl", {768, 768}, true);
layers.mul(reshape_qkv_out, weights_l, nullptr, 2);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
auto fuse_pass =
PassRegistry::Instance().Get("multihead_matmul_fuse_pass_v2");
auto sparse_pass =
PassRegistry::Instance().Get("dense_multihead_matmul_to_sparse_pass");
if (fuse_pass.get() == nullptr || sparse_pass.get() == nullptr)
LOG(INFO) << "asdfasdf";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(fuse_pass->Apply(graph.release()));
graph.reset(sparse_pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fused_nodes_after = GetNumOpNodes(graph, "sparse_multihead_matmul");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 39,
common::errors::InvalidArgument(
"After the multihead_matmul pass and sparse pass, The "
"node num in graph "
"should be %d, but the result is %d",
num_nodes_before - 39,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the multihead_matmul pass and sparse pass, "
"there should be one "
"sparse_multihead_matmul op, but the result is %d",
num_fused_nodes_after));
}
} // namespace paddle::framework::ir
USE_PASS(multihead_matmul_fuse_pass);
USE_PASS(multihead_matmul_fuse_pass_v2);
USE_PASS(dense_multihead_matmul_to_sparse_pass);
@@ -0,0 +1,103 @@
/* Copyright (c) 2019 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/fluid/framework/ir/embedding_eltwise_layernorm_fuse_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/op_version_registry.h"
namespace paddle::framework::ir {
TEST(EmbeddingElewiseLayernormFusePass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, y) elementwise_add -> elementwise_out
// (elementwise_out, scale, bias) layer_norm -> layer_norm_out...
Layers layers;
auto* x0 = layers.data("x0", {1, 256, 1});
auto* x1 = layers.data("x1", {1, 256, 1});
auto* x2 = layers.data("x2", {1, 256, 1});
auto* x3 = layers.data("x3", {1, 256, 1});
auto* emb0 = layers.data("emb0", {18000, 768}, true);
auto* emb1 = layers.data("emb1", {4, 768}, true);
auto* emb2 = layers.data("emb2", {513, 768}, true);
auto* emb3 = layers.data("emb3", {3, 768}, true);
auto* lkt0 = layers.embedding(x0, emb0);
auto* lkt1 = layers.embedding(x1, emb1);
auto* lkt2 = layers.embedding(x2, emb2);
auto* lkt3 = layers.embedding(x3, emb3);
auto* elementwise_out1 = layers.elementwise_add(lkt0, lkt2);
auto* elementwise_out2 = layers.elementwise_add(elementwise_out1, lkt1);
auto* elementwise_out3 = layers.elementwise_add(elementwise_out2, lkt3);
auto* scale = layers.data("scale", {768}, true);
auto* bias = layers.data("bias", {768}, true);
layers.layer_norm(elementwise_out3, scale, bias);
auto* y0 = layers.data("y0", {1, 256, 1});
auto* y1 = layers.data("y1", {1, 256, 1});
auto* y2 = layers.data("y2", {1, 256, 1});
auto* emb0y = layers.data("emb0y", {18000, 768}, true);
auto* emb1y = layers.data("emb1y", {4, 768}, true);
auto* emb2y = layers.data("emb2y", {513, 768}, true);
auto* lkt0y = layers.embedding(y0, emb0y);
auto* lkt1y = layers.embedding(y1, emb1y);
auto* lkt2y = layers.embedding(y2, emb2y);
auto* elementwise_out1y = layers.elementwise_add(lkt0y, lkt2y);
auto* elementwise_out2y = layers.elementwise_add(elementwise_out1y, lkt1y);
auto* scaley = layers.data("scaley", {768}, true);
auto* biasy = layers.data("biasy", {768}, true);
layers.layer_norm(elementwise_out2y, scaley, biasy);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass =
PassRegistry::Instance().Get("embedding_eltwise_layernorm_fuse_pass");
int num_nodes_before = graph->Nodes().size();
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = graph->Nodes().size();
int num_fused_nodes_after =
GetNumOpNodes(graph, "fused_embedding_eltwise_layernorm");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 28,
common::errors::PreconditionNotMet(
"The number of nodes before and after the fuse does "
"not meet expectations"));
PADDLE_ENFORCE_EQ(
num_fused_nodes_after,
2,
common::errors::PreconditionNotMet(
"The number of fusion nodes does not meet expectations after fuse"));
}
TEST(EmbeddingElewiseLayernormFusePass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("embedding_eltwise_layernorm_fuse_pass"));
}
} // namespace paddle::framework::ir
USE_PASS(embedding_eltwise_layernorm_fuse_pass);
@@ -0,0 +1,75 @@
/* Copyright (c) 2019 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/fluid/framework/ir/fc_elementwise_layernorm_fuse_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
TEST(FCElementwiseLayerNormFusePass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, weights_0, bias_0) fc -> fc_out_0
// (fc_out_0, weights_1, bias_1) fc -> fc_out_1
// (fc_out_1, y) elementwise_add -> elementwise_out
// (elementwise_out, scale, bias_2) layer_norm ->
Layers layers;
auto* x = layers.data("x", {128, 768});
auto* weights_0 = layers.data("weights_0", {768, 3072}, true);
auto* bias_0 = layers.data("bias_0", {3072}, true);
auto* fc_out_0 = layers.fc(x, weights_0, bias_0); // {128, 3072}
auto* weights_1 = layers.data("weights_1", {3072, 768}, true);
auto* bias_1 = layers.data("bias_1", {768}, true);
auto* fc_out_1 =
layers.fc(fc_out_0, weights_1, bias_1, 1, "relu"); // {128, 768}
fc_out_1->SetShape({128, 768});
auto* y = layers.data("y", {128, 768});
auto* elementwise_out = layers.elementwise_add(fc_out_1, y);
auto* scale = layers.data("scale", {768}, true);
auto* bias_2 = layers.data("bias_2", {768}, true);
layers.layer_norm(elementwise_out, scale, bias_2);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass =
PassRegistry::Instance().Get("fc_elementwise_layernorm_fuse_pass");
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fused_nodes_after =
GetNumOpNodes(graph, "fused_fc_elementwise_layernorm");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(
num_nodes_before,
num_nodes_after + 6,
common::errors::InvalidArgument(
"After pass, the number of nodes should be reduced by 6, but the "
"number before pass is %d, after pass is %d.",
num_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After pass, the number of nodes of type "
"'fused_fc_elementwise_layernorm' should be 1, not %d.",
num_fused_nodes_after));
}
} // namespace paddle::framework::ir
USE_PASS(fc_elementwise_layernorm_fuse_pass);
@@ -0,0 +1,101 @@
// Copyright (c) 2018 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/fluid/framework/ir/fc_fuse_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
tensor->mutable_data<float>(phi::CPUPlace());
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope(param_scope, "conv2d_filters_0", {});
AddVarToScope(param_scope, "conv2d_bias_0", {});
AddVarToScope(param_scope, "weights_0", {});
AddVarToScope(param_scope, "weights_1", {});
AddVarToScope(param_scope, "bias_1", {});
AddVarToScope(param_scope, "bias_2", {});
return param_scope;
}
TEST(FCFusePass, basic) {
// inputs operator output
// --------------------------------------------------------
// (a, filters_0 bias_0) conv2d -> conv2d_out
// conv2d_out relu -> relu_out_0
// (relu_out_0, weights_0) mul -> mul_out_0
// (mul_out_0, bias_1) elementwise_add -> add_out_0
// add_out_0 relu -> relu_out_1
// (relu_out_1, weights_1) mul -> mul_out_1
// (mul_out_1, bias_2) elementwise_add -> add_out_1
Layers layers;
auto* a = layers.data("a");
auto* filters_0 = layers.data("conv2d_filters_0", {}, true);
auto* bias_0 = layers.data("conv2d_bias_0", {}, true);
auto* conv2d_out = layers.conv2d(a, filters_0, bias_0, false);
auto* relu_out_0 = layers.relu(conv2d_out);
auto* weights_0 = layers.data("weights_0", {5, 4}, true);
auto* mul_out_0 = layers.mul(relu_out_0, weights_0);
auto* bias_1 = layers.data("bias_1", {4}, true);
auto* add_out_0 = layers.elementwise_add(mul_out_0, bias_1, nullptr, 1);
auto* relu_out_1 = layers.relu(add_out_0);
auto* weights_1 = layers.data("weights_1", {8, 9}, true);
auto* mul_out_1 = layers.mul(relu_out_1, weights_1);
auto* bias_2 = layers.data("bias_2", {1, 9}, true);
auto* add_out_1 = layers.elementwise_add(mul_out_1, bias_2, nullptr, 1);
VLOG(4) << add_out_1;
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("fc_fuse_pass");
pass->Set("use_gpu", new bool(true));
graph->Set("__param_scope__", CreateParamScope());
int num_nodes_before = static_cast<int>(graph->Nodes().size());
int num_mul_nodes_before = GetNumOpNodes(graph, "mul");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fc_nodes_after = GetNumOpNodes(graph, "fc");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 6,
common::errors::InvalidArgument(
"num_nodes_before=%d, num_nodes_after=%d.",
num_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fc_nodes_after,
2,
common::errors::InvalidArgument("num_fc_nodes_after=%d.",
num_fc_nodes_after));
PADDLE_ENFORCE_EQ(num_mul_nodes_before,
num_fc_nodes_after,
common::errors::InvalidArgument(
"num_mul_nodes_before=%d, num_fc_nodes_after=%d.",
num_mul_nodes_before,
num_fc_nodes_after));
}
} // namespace paddle::framework::ir
USE_PASS(fc_fuse_pass);
@@ -0,0 +1,51 @@
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/ir/fc_gru_fuse_pass_tester.h"
namespace paddle::framework::ir::fc_gru_test {
TEST(FcGruFusePass, basic) {
std::unique_ptr<ir::Graph> graph = PrepareGraph();
auto pass = PassRegistry::Instance().Get("fc_gru_fuse_pass");
pass->Set("use_gpu", new bool(true));
graph->Set("__param_scope__", CreateParamScope());
int num_nodes_before = static_cast<int>(graph->Nodes().size());
int num_gru_nodes_before = GetNumOpNodes(graph, "gru");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fuse_gru_nodes_after = GetNumOpNodes(graph, "fusion_gru");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 6,
common::errors::PreconditionNotMet(
"The number of nodes before and after "
"the fuse does not meet expectations"));
PADDLE_ENFORCE_EQ(
num_fuse_gru_nodes_after,
2,
common::errors::PreconditionNotMet("The number of gru nodes before the "
"fuse does not meet expectations"));
PADDLE_ENFORCE_EQ(num_gru_nodes_before,
num_fuse_gru_nodes_after,
common::errors::PreconditionNotMet(
"The number of fusion_gru nodes does not meet "
"expectations after fuse"));
}
} // namespace paddle::framework::ir::fc_gru_test
USE_PASS(fc_gru_fuse_pass);
@@ -0,0 +1,51 @@
// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/ir/fc_lstm_fuse_pass_tester.h"
namespace paddle::framework::ir::fc_lstm_test {
TEST(FcLstmFusePass, basic) {
std::unique_ptr<ir::Graph> graph = PrepareGraph();
auto pass = PassRegistry::Instance().Get("fc_lstm_fuse_pass");
pass->Set("use_gpu", new bool(false));
graph->Set("__param_scope__", CreateParamScope());
int num_nodes_before = static_cast<int>(graph->Nodes().size());
int num_lstm_nodes_before = GetNumOpNodes(graph, "lstm");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fusion_lstm_nodes_after = GetNumOpNodes(graph, "fusion_lstm");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after - 6,
common::errors::PreconditionNotMet(
"The number of nodes before and after "
"the fuse does not meet expectations"));
PADDLE_ENFORCE_EQ(
num_fusion_lstm_nodes_after,
2,
common::errors::PreconditionNotMet("The number of lstm nodes before the "
"fuse does not meet expectations"));
PADDLE_ENFORCE_EQ(
num_lstm_nodes_before,
num_fusion_lstm_nodes_after,
common::errors::PreconditionNotMet("The number of fusion_gru nodes does "
"not meet expectations after fuse"));
}
} // namespace paddle::framework::ir::fc_lstm_test
USE_PASS(fc_lstm_fuse_pass);
@@ -0,0 +1,172 @@
/* Copyright (c) 2022 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/fluid/framework/ir/fuse_multi_transformer_layer_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/op_version_registry.h"
#define DEF_INPUT_DATA \
Layers layers; \
int num_layers = 3; \
auto* x = layers.data("x", {1, 128, 1024}); \
auto* src_mask = layers.data("src_mask", {1, 16, 128, 128}); \
auto* ln_scale = layers.data("ln_scale", {1024}, true); \
auto* ln_bias = layers.data("ln_bias", {1024}, true); \
auto* ffn_ln_scale = layers.data("ffn_ln_scale", {1024}, true); \
auto* ffn_ln_bias = layers.data("ffn_ln_bias", {1024}, true); \
auto* qkv_w = layers.data("qkv_w", {3, 16, 64, 1024}, true); \
auto* out_linear_w = layers.data("out_linear_w", {1024, 1024}, true); \
auto* ffn1_w = layers.data("ffn1_w", {1024, 4096}, true); \
auto* ffn2_w = layers.data("ffn2_w", {4096, 1024}, true); \
auto* qkv_bias = layers.data("qkv_bias", {3072}, true); \
auto* out_linear_bias = layers.data("out_linear_bias", {1024}, true); \
auto* ffn1_bias = layers.data("ffn1_bias", {4096}, true); \
auto* ffn2_bias = layers.data("ffn2_bias", {1024}, true);
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
tensor->mutable_data<float>(phi::CPUPlace());
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope(param_scope, "ln_scale", {1024});
AddVarToScope(param_scope, "ln_bias", {1024});
AddVarToScope(param_scope, "ffn_ln_scale", {1024});
AddVarToScope(param_scope, "ffn_ln_bias", {1024});
AddVarToScope(param_scope, "qkv_w", {3, 16, 64, 1024});
AddVarToScope(param_scope, "out_linear_w", {1024, 1024});
AddVarToScope(param_scope, "ffn1_w", {1024, 4096});
AddVarToScope(param_scope, "ffn2_w", {4096, 1024});
AddVarToScope(param_scope, "qkv_bias", {3072});
AddVarToScope(param_scope, "out_linear_bias", {1024});
AddVarToScope(param_scope, "ffn1_bias", {4096});
AddVarToScope(param_scope, "ffn2_bias", {1024});
return param_scope;
}
TEST(FuseMultiTransformerLayerPass, encoder_fp) {
DEF_INPUT_DATA
// Layers
for (int i = 0; i < num_layers; ++i) {
auto* cache_kv = layers.fill_constant_batch_size_like(
x,
static_cast<int>(proto::VarType::FP32),
0,
1,
{2, -1, 16, 1024, 64},
0);
auto outs = layers.fused_multi_transformer(x,
cache_kv,
src_mask,
qkv_w,
qkv_bias,
out_linear_w,
out_linear_bias,
ffn1_w,
ffn1_bias,
ffn2_w,
ffn2_bias,
ln_scale,
ln_bias,
ffn_ln_scale,
ffn_ln_bias,
0.1,
1e-12);
x = outs[0];
}
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
graph->Set(kFusedMultiTransformerEncoderFusionCount, new int(num_layers));
graph->Set("enable_int8", new bool(false));
auto pass = PassRegistry::Instance().Get("fuse_multi_transformer_layer_pass");
if (pass.get() == nullptr)
LOG(INFO) << "get fuse_multi_transformer_layer_pass failed";
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(
num_nodes_after,
1,
common::errors::InvalidArgument(
"After the fuse_multi_transformer_layer_pass, "
"The node num in graph should be 1, but the result is %d",
num_nodes_after));
}
TEST(FuseMultiTransformerLayerPass, decoder_fp) {
DEF_INPUT_DATA
x = layers.data("x", {1, 1, 1024});
auto* cache_kv = layers.data("cache_kv", {2, 1, 16, 1024, 64}, true);
src_mask = layers.data("src_mask", {1, 16, 1, 129});
// Layers
for (int i = 0; i < num_layers; ++i) {
auto* shape_out = layers.shape(src_mask);
auto* time_stamp = layers.slice(shape_out, {0}, {3}, {4});
auto outs = layers.fused_multi_transformer(x,
cache_kv,
src_mask,
qkv_w,
qkv_bias,
out_linear_w,
out_linear_bias,
ffn1_w,
ffn1_bias,
ffn2_w,
ffn2_bias,
ln_scale,
ln_bias,
ffn_ln_scale,
ffn_ln_bias,
0.1,
1e-12,
time_stamp);
x = outs[0];
}
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto param_scope = CreateParamScope();
AddVarToScope(param_scope, "cache_kv", {2, 1, 16, 1024, 64});
graph->Set("__param_scope__", param_scope);
graph->Set(kFusedMultiTransformerDecoderFusionCount, new int(num_layers));
auto pass = PassRegistry::Instance().Get("fuse_multi_transformer_layer_pass");
if (pass.get() == nullptr)
LOG(INFO) << "get fuse_multi_transformer_layer_pass failed";
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(
num_nodes_after,
1,
common::errors::InvalidArgument(
"After the fuse_multi_transformer_layer_pass, "
"The node num in graph should be 1, but the result is %d",
num_nodes_after));
}
} // namespace paddle::framework::ir
USE_PASS(fuse_multi_transformer_layer_pass);
@@ -0,0 +1,555 @@
/* Copyright (c) 2022 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/fluid/framework/ir/fused_multi_transformer_encoder_pass.h" // NOLINT
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/op_version_registry.h"
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
tensor->mutable_data<float>(phi::CPUPlace());
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
// MHA: pre Layer Norm
AddVarToScope(param_scope, "ln_scale", {1024});
AddVarToScope(param_scope, "ln_bias", {1024});
// MHA: QKV fc
AddVarToScope(param_scope, "weights0", {1024, 1024});
AddVarToScope(param_scope, "weights1", {1024, 1024});
AddVarToScope(param_scope, "weights2", {1024, 1024});
AddVarToScope(param_scope, "bias_0", {1024});
AddVarToScope(param_scope, "bias_1", {1024});
AddVarToScope(param_scope, "bias_2", {1024});
// MHA: QK bias
AddVarToScope(param_scope, "biasqk", {1024});
// MHA: out Linear
AddVarToScope(param_scope, "weights_l", {1024, 1024});
AddVarToScope(param_scope, "bias_l", {1024});
// MHA: pre Layer Norm
AddVarToScope(param_scope, "ffn_ln_scale", {1024});
AddVarToScope(param_scope, "ffn_ln_bias", {1024});
// FFN: fc1 -> (gelu) -> fc2
AddVarToScope(param_scope, "ffn_weights0", {1024, 4096});
AddVarToScope(param_scope, "ffn_weights1", {4096, 1024});
AddVarToScope(param_scope, "ffn_bias_0", {4096});
AddVarToScope(param_scope, "ffn_bias_1", {1024});
return param_scope;
}
TEST(FusedMultiTransformerDecoderPass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, ln_scale, ln_bias) layer_norm -> layer_norm_out
// (layer_norm_out, weights_0) matmul_v2 -> matmul_out0
// (layer_norm_out, weights_1) matmul_v2 -> matmul_out1
// (layer_norm_out, weights_2) matmul_v2 -> matmul_out2
// (matmul_out0, bias_0) elementwise_add -> eltadd_0
// (matmul_out1, bias_1) elementwise_add -> eltadd_1
// (matmul_out2, bias_2) elementwise_add -> eltadd_2
// (eltadd_0) reshape2 -> reshape_0
// (eltadd_1) reshape2 -> reshape_1
// (eltadd_2) reshape2 -> reshape_2
// (reshape_0) transpose2 -> transpose_0
// (reshape_1) transpose2 -> transpose_1
// (reshape_2) transpose2 -> transpose_2
// (transpose_1) concat -> concat_0
// (transpose_2) concat -> concat_2
// (concat_0) assign -> assign_0
// (concat_1) assign -> assign_2
// (transpose_0, transpose_1) matmul -> matmul_qk
// (matmul_qk, bias_qk) elementwise_add -> eltadd_qk
// (eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul_v2 -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) matmul_v2 -> matmul_linear
// (matmul_linear) elementwise_add -> eltadd_linear
// (eltadd_out) elementwise_add -> attention_out
//
// (attention_out, scale, bias) layer_norm -> ffn_layer_norm_out
// (layer_norm_out, ffn_matmul0_w) matmul_v2 -> ffn_matmul0
// (ffn_matmul0, ffn_bias0) elementwise_add -> ffn_eltadd0
// (ffn_eltadd0) gelu -> ffn_gelu
// (ffn_gelu) matmul_v2 -> ffn_matmul1
// (ffn_matmul1, ffn_bias1) elementwise_add -> ffn_eltadd1
// (attention_out, ffn_eltadd1) elementwise_add -> ffn_output
Layers layers;
// MHA: pre LayerNorm
auto* x = layers.data("x", {1, 128, 1024});
auto* ln_scale = layers.data("ln_scale", {1024}, true);
auto* ln_bias = layers.data("ln_bias", {1024}, true);
auto* ln_out = layers.layer_norm(x, ln_scale, ln_bias)[0];
// MHA: QKV fc
auto* weights_0 = layers.data("weights0", {1024, 1024}, true);
auto* weights_1 = layers.data("weights1", {1024, 1024}, true);
auto* weights_2 = layers.data("weights2", {1024, 1024}, true);
auto* matmul_out_0 =
layers.matmul_v2(ln_out, weights_0, nullptr, false, true);
auto* matmul_out_1 =
layers.matmul_v2(ln_out, weights_1, nullptr, false, true);
auto* matmul_out_2 =
layers.matmul_v2(ln_out, weights_2, nullptr, false, true);
auto* b0 = layers.data("bias_0", {1024}, true);
auto* b1 = layers.data("bias_1", {1024}, true);
auto* b2 = layers.data("bias_2", {1024}, true);
auto* elementwise_out_0 =
layers.elementwise_add(matmul_out_0, b0, nullptr, 2);
auto* elementwise_out_1 =
layers.elementwise_add(matmul_out_1, b1, nullptr, 2);
auto* elementwise_out_2 =
layers.elementwise_add(matmul_out_2, b2, nullptr, 2);
std::vector<int> shape = {1, 128, 16, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
auto* reshape_1 = layers.reshape2(elementwise_out_1, shape, true);
auto* reshape_2 = layers.reshape2(elementwise_out_2, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto* transpose_1 = layers.transpose2(reshape_1, axis, true);
auto* transpose_2 = layers.transpose2(reshape_2, axis, true);
auto* cache_k = layers.data("cache_k", {1, 16, 128, 64});
auto* cache_v = layers.data("cache_v", {1, 16, 128, 64});
auto* concat_k = layers.concat({cache_k, transpose_1}, 2);
auto* concat_v = layers.concat({cache_v, transpose_2}, 2);
layers.assign(concat_k);
layers.assign(concat_v);
// MHA: QK matmul
auto* matmul_qk = layers.matmul(transpose_0, concat_k, nullptr, false, true);
auto* bqk = layers.data("biasqk", {1, 12, 128, 128}, true);
auto* elementwise_qk = layers.elementwise_add(matmul_qk, bqk);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
// MHA: QKV matmul
auto* matmul_qkv = layers.matmul_v2(softmax_qk, concat_v);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 1024}, true);
// MHA: out Linear
auto* weights_l = layers.data("weights_l", {1024, 1024}, true);
auto* bias_l = layers.data("weightsl", {1024, 1024}, true);
auto* linear_matmut_out =
layers.matmul_v2(reshape_qkv_out, weights_l, nullptr, false, true);
auto* linear_eltadd_out =
layers.elementwise_add(linear_matmut_out, bias_l, nullptr, 2);
auto* attention_out = layers.elementwise_add(x, linear_eltadd_out);
// FFN: pre LayerNorm
auto* ffn_ln_scale = layers.data("ffn_ln_scale", {1024}, true);
auto* ffn_ln_bias = layers.data("ffn_ln_bias", {1024}, true);
auto* ffn_ln_out =
layers.layer_norm(attention_out, ffn_ln_scale, ffn_ln_bias)[0];
// FFN: fc1 -> gelu -> fc2
auto* ffn_weights0 = layers.data("ffn_weights0", {1024, 4096}, true);
auto* ffn_weights1 = layers.data("ffn_weights1", {4096, 1024}, true);
auto* ffn_bias0 = layers.data("ffn_bias0", {4096}, true);
auto* ffn_bias1 = layers.data("ffn_bias1", {1024}, true);
auto* ffn_matmul0_out =
layers.matmul_v2(ffn_ln_out, ffn_weights0, nullptr, false, true);
auto* ffn_eltadd0_out =
layers.elementwise_add(ffn_matmul0_out, ffn_bias0, nullptr, 2);
auto* ffn_gelu_out = layers.gelu(ffn_eltadd0_out);
auto* ffn_matmul1_out =
layers.matmul_v2(ffn_gelu_out, ffn_weights1, nullptr, false, true);
auto* ffn_eltadd1_out =
layers.elementwise_add(ffn_matmul1_out, ffn_bias1, nullptr, 2);
layers.elementwise_add(attention_out, ffn_eltadd1_out);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
graph->Set("enable_int8", new bool(false));
auto pass =
PassRegistry::Instance().Get("fused_multi_transformer_decoder_pass");
if (pass.get() == nullptr)
LOG(INFO) << "get fused_multi_transformer_decoder_pass failed";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
int num_fused_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 60,
common::errors::InvalidArgument(
"After the fused_multi_transformer_decoder_pass, The "
"node num in graph "
"should be %d, but the result is %d",
num_nodes_before - 60,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the fused_multi_transformer_decoder pass, "
"there should be one fused_multi_transformer op, "
"but the result is %d",
num_fused_nodes_after));
}
TEST(FusedMultiTransformerDecoderPass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("fused_multi_transformer_decoder_pass"));
}
TEST(FusedMultiTransformerDecoderFuseQKVPass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, ln_scale, ln_bias) layer_norm -> layer_norm_out
// (layer_norm_out, weights_0) matmul_v2 -> matmul_out0
// (matmul_out0, bias_0) elementwise_add -> eltadd_0
// (eltadd_0) reshape2 -> reshape_0
// (reshape_0) transpose2 -> transpose_0
// (transpose_0) split -> split_q, split_k,
// split_v (split_k) concat -> concat_k
// (split_v) concat -> concat_v
// (concat_k) assign -> assign_k
// (concat_v) assign -> assign_v
// (split_q, split_k) matmul_v2 -> matmul_qk
// (matmul_qk) scale -> scale_qk
// (scale_qk, bias_qk) elementwise_add -> eltadd_qk
// (eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul_v2 -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) matmul_v2 -> matmul_linear
// (matmul_linear) elementwise_add -> eltadd_linear
// (eltadd_out) elementwise_add -> attention_out
//
// (attention_out, scale, bias) layer_norm -> ffn_layer_norm_out
// (layer_norm_out, ffn_matmul0_w) matmul_v2 -> ffn_matmul0
// (ffn_matmul0, ffn_bias0) elementwise_add -> ffn_eltadd0
// (ffn_eltadd0) gelu -> ffn_gelu
// (ffn_gelu) matmul_v2 -> ffn_matmul1
// (ffn_matmul1, ffn_bias1) elementwise_add -> ffn_eltadd1
// (attention_out, ffn_eltadd1) elementwise_add -> ffn_output
//
// (transpose_1, transpose_2) while -> decoder block
Layers layers;
// MHA: pre LayerNorm
auto* x = layers.data("x", {1, 128, 1024});
auto* ln_scale = layers.data("ln_scale", {1024}, true);
auto* ln_bias = layers.data("ln_bias", {1024}, true);
auto* ln_out = layers.layer_norm(x, ln_scale, ln_bias)[0];
// MHA: QKV fc
auto* weights_0 = layers.data("weights0", {1024, 3072}, true);
auto* matmul_out_0 =
layers.matmul_v2(ln_out, weights_0, nullptr, false, true);
auto* b0 = layers.data("bias_0", {3072}, true);
auto* elementwise_out_0 =
layers.elementwise_add(matmul_out_0, b0, nullptr, 2);
std::vector<int> shape = {1, 128, 16, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto split_outs = layers.split(transpose_0, 3, 3);
auto* split_q = split_outs[0];
auto* split_k = split_outs[1];
auto* split_v = split_outs[2];
auto* cache_k = layers.data("cache_k", {1, 16, 128, 64});
auto* cache_v = layers.data("cache_v", {1, 16, 128, 64});
auto* concat_k = layers.concat({cache_k, split_k}, 2);
auto* concat_v = layers.concat({cache_v, split_v}, 2);
layers.assign(concat_k);
layers.assign(concat_v);
// MHA: QK matmul
auto* matmul_qk = layers.matmul_v2(split_q, concat_k, nullptr, false, true);
auto* scale_qk = layers.scale(matmul_qk, 0.125, 0, false);
auto* bqk = layers.data("biasqk", {1, 12, 128, 128}, true);
auto* elementwise_qk = layers.elementwise_add(scale_qk, bqk);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
// MHA: QKV matmul
auto* matmul_qkv = layers.matmul_v2(softmax_qk, concat_v);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 1024}, true);
// MHA: out Linear
auto* weights_l = layers.data("weights_l", {1024, 1024}, true);
auto* bias_l = layers.data("weightsl", {1024, 1024}, true);
auto* linear_matmut_out =
layers.matmul_v2(reshape_qkv_out, weights_l, nullptr, false, true);
auto* linear_eltadd_out =
layers.elementwise_add(linear_matmut_out, bias_l, nullptr, 2);
auto* attention_out = layers.elementwise_add(x, linear_eltadd_out);
// FFN: pre LayerNorm
auto* ffn_ln_scale = layers.data("ffn_ln_scale", {1024}, true);
auto* ffn_ln_bias = layers.data("ffn_ln_bias", {1024}, true);
auto* ffn_ln_out =
layers.layer_norm(attention_out, ffn_ln_scale, ffn_ln_bias)[0];
// FFN: fc1 -> gelu -> fc2
auto* ffn_weights0 = layers.data("ffn_weights0", {1024, 4096}, true);
auto* ffn_weights1 = layers.data("ffn_weights1", {4096, 1024}, true);
auto* ffn_bias0 = layers.data("ffn_bias0", {4096}, true);
auto* ffn_bias1 = layers.data("ffn_bias1", {1024}, true);
auto* ffn_matmul0_out =
layers.matmul_v2(ffn_ln_out, ffn_weights0, nullptr, false, true);
auto* ffn_eltadd0_out =
layers.elementwise_add(ffn_matmul0_out, ffn_bias0, nullptr, 2);
auto* ffn_gelu_out = layers.gelu(ffn_eltadd0_out);
auto* ffn_matmul1_out =
layers.matmul_v2(ffn_gelu_out, ffn_weights1, nullptr, false, true);
auto* ffn_eltadd1_out =
layers.elementwise_add(ffn_matmul1_out, ffn_bias1, nullptr, 2);
layers.elementwise_add(attention_out, ffn_eltadd1_out);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
graph->Set("enable_int8", new bool(false));
auto pass = PassRegistry::Instance().Get(
"fused_multi_transformer_decoder_fuse_qkv_pass");
if (pass.get() == nullptr)
LOG(INFO) << "get fused_multi_transformer_decoder_fuse_qkv_pass failed";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
int num_fused_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(
num_nodes_before,
num_nodes_after + 52,
common::errors::InvalidArgument(
"After the fused_multi_transformer_decoder_fuse_qkv_pass, "
"The node num in graph should be %d, but the result is %d",
num_nodes_before - 52,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the fused_multi_transformer_decoder_fuse_qkv "
"pass, there should be one fused_multi_transformer "
"op, but the result is %d",
num_fused_nodes_after));
}
TEST(FusedMultiTransformerDecoderFuseQKVPass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("fused_multi_transformer_decoder_fuse_qkv_pass"));
}
TEST(MultiDevicesFusedMultiTransformerDecoderFuseQKVPass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, ln_scale, ln_bias) layer_norm -> layer_norm_out
// (layer_norm_out) c_identity -> c_identity_out
// (c_identity_out, weights_0) matmul_v2 -> matmul_out0
// (matmul_out0, bias_0) elementwise_add -> eltadd_0
// (eltadd_0) reshape2 -> reshape_0
// (reshape_0) transpose2 -> transpose_0
// (transpose_0) split -> split_q, split_k,
// split_v (split_k) concat -> concat_k
// (split_v) concat -> concat_v
// (concat_k) assign -> assign_k
// (concat_v) assign -> assign_v
// (split_q, split_k) matmul_v2 -> matmul_qk
// (matmul_qk) scale -> scale_qk
// (scale_qk, bias_qk) elementwise_add -> eltadd_qk
// (eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul_v2 -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) matmul_v2 -> matmul_linear
// (matmul_linear) all_reduce_sum -> c_all_reduce_out
// (matmul_linear) elementwise_add -> eltadd_linear
// (eltadd_out) elementwise_add -> attention_out
//
// (attention_out, scale, bias) layer_norm -> ffn_layer_norm_out
// (ffn_layer_norm_out) c_identity -> ffn_c_identity_out
// (layer_norm_out, ffn_matmul0_w) matmul_v2 -> ffn_matmul0
// (ffn_matmul0, ffn_bias0) elementwise_add -> ffn_eltadd0
// (ffn_eltadd0) gelu -> ffn_gelu
// (ffn_gelu) matmul_v2 -> ffn_matmul1
// (ffn_matmul1) all_reduce_sum -> c_allreduce_out
// (ffn_matmul1, ffn_bias1) elementwise_add -> ffn_eltadd1
// (attention_out, ffn_eltadd1) elementwise_add -> ffn_output
//
// (transpose_1, transpose_2) while -> decoder block
Layers layers;
// MHA: pre LayerNorm
auto* x = layers.data("x", {1, 128, 1024});
auto* ln_scale = layers.data("ln_scale", {1024}, true);
auto* ln_bias = layers.data("ln_bias", {1024}, true);
auto* ln_out = layers.layer_norm(x, ln_scale, ln_bias)[0];
auto* c_identity_out = layers.c_identity(ln_out);
// MHA: QKV fc
auto* weights_0 = layers.data("weights0", {1024, 3072}, true);
auto* matmul_out_0 =
layers.matmul_v2(c_identity_out, weights_0, nullptr, false, true);
auto* b0 = layers.data("bias_0", {3072}, true);
auto* elementwise_out_0 =
layers.elementwise_add(matmul_out_0, b0, nullptr, 2);
std::vector<int> shape = {1, 128, 16, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto split_outs = layers.split(transpose_0, 3, 3);
auto* split_q = split_outs[0];
auto* split_k = split_outs[1];
auto* split_v = split_outs[2];
auto* cache_k = layers.data("cache_k", {1, 16, 128, 64});
auto* cache_v = layers.data("cache_v", {1, 16, 128, 64});
auto* concat_k = layers.concat({cache_k, split_k}, 2);
auto* concat_v = layers.concat({cache_v, split_v}, 2);
layers.assign(concat_k);
layers.assign(concat_v);
// MHA: QK matmul
auto* matmul_qk = layers.matmul_v2(split_q, concat_k, nullptr, false, true);
auto* scale_qk = layers.scale(matmul_qk, 0.125, 0, false);
auto* bqk = layers.data("biasqk", {1, 12, 128, 128}, true);
auto* elementwise_qk = layers.elementwise_add(scale_qk, bqk);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
// MHA: QKV matmul
auto* matmul_qkv = layers.matmul_v2(softmax_qk, concat_v);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 1024}, true);
// MHA: out Linear
auto* weights_l = layers.data("weights_l", {1024, 1024}, true);
auto* bias_l = layers.data("weightsl", {1024, 1024}, true);
auto* linear_matmut_out =
layers.matmul_v2(reshape_qkv_out, weights_l, nullptr, false, true);
auto* c_allreduce_out = layers.c_allreduce_sum(linear_matmut_out);
auto* linear_eltadd_out =
layers.elementwise_add(c_allreduce_out, bias_l, nullptr, 2);
auto* attention_out = layers.elementwise_add(x, linear_eltadd_out);
// FFN: pre LayerNorm
auto* ffn_ln_scale = layers.data("ffn_ln_scale", {1024}, true);
auto* ffn_ln_bias = layers.data("ffn_ln_bias", {1024}, true);
auto* ffn_ln_out =
layers.layer_norm(attention_out, ffn_ln_scale, ffn_ln_bias)[0];
auto* ffn_c_identity_out = layers.c_identity(ffn_ln_out);
// FFN: fc1 -> gelu -> fc2
auto* ffn_weights0 = layers.data("ffn_weights0", {1024, 4096}, true);
auto* ffn_weights1 = layers.data("ffn_weights1", {4096, 1024}, true);
auto* ffn_bias0 = layers.data("ffn_bias0", {4096}, true);
auto* ffn_bias1 = layers.data("ffn_bias1", {1024}, true);
auto* ffn_matmul0_out =
layers.matmul_v2(ffn_c_identity_out, ffn_weights0, nullptr, false, true);
auto* ffn_eltadd0_out =
layers.elementwise_add(ffn_matmul0_out, ffn_bias0, nullptr, 2);
auto* ffn_gelu_out = layers.gelu(ffn_eltadd0_out);
auto* ffn_matmul1_out =
layers.matmul_v2(ffn_gelu_out, ffn_weights1, nullptr, false, true);
auto* ffn_c_allreduce_out = layers.c_allreduce_sum(ffn_matmul1_out);
auto* ffn_eltadd1_out =
layers.elementwise_add(ffn_c_allreduce_out, ffn_bias1, nullptr, 2);
layers.elementwise_add(attention_out, ffn_eltadd1_out);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
graph->Set("enable_int8", new bool(false));
auto pass = PassRegistry::Instance().Get(
"multi_devices_fused_multi_transformer_decoder_fuse_qkv_pass");
if (pass.get() == nullptr)
LOG(INFO)
<< "get multi_devices_fused_multi_transformer_decoder_fuse_qkv_pass "
"failed";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
int num_fused_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(
num_nodes_before,
num_nodes_after + 60,
common::errors::InvalidArgument(
"After the fused_multi_transformer_decoder_fuse_qkv_pass, "
"The node num in graph should be %d, but the result is %d",
num_nodes_before - 60,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the fused_multi_transformer_decoder_fuse_qkv "
"multi-devices pass, there should be one "
"fused_multi_transformer op, but the result is %d",
num_fused_nodes_after));
}
TEST(MultiDevicesFusedMultiTransformerDecoderFuseQKVPass,
pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible(
"multi_devices_fused_multi_transformer_decoder_fuse_qkv_pass"));
}
} // namespace paddle::framework::ir
USE_PASS(fused_multi_transformer_decoder_pass);
USE_PASS(fused_multi_transformer_decoder_fuse_qkv_pass);
USE_PASS(multi_devices_fused_multi_transformer_decoder_fuse_qkv_pass);
@@ -0,0 +1,717 @@
/* Copyright (c) 2022 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/fluid/framework/ir/fused_multi_transformer_encoder_pass.h" // NOLINT
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/op_version_registry.h"
#ifndef UNUSED
#define UNUSED __attribute__((unused))
#endif
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
tensor->mutable_data<float>(phi::CPUPlace());
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
// MHA: pre Layer Norm
AddVarToScope(param_scope, "ln_scale", {1024});
AddVarToScope(param_scope, "ln_bias", {1024});
// MHA: QKV fc
AddVarToScope(param_scope, "weights0", {1024, 1024});
AddVarToScope(param_scope, "weights1", {1024, 1024});
AddVarToScope(param_scope, "weights2", {1024, 1024});
AddVarToScope(param_scope, "bias_0", {1024});
AddVarToScope(param_scope, "bias_1", {1024});
AddVarToScope(param_scope, "bias_2", {1024});
// MHA: QK bias
AddVarToScope(param_scope, "biasqk", {1024});
// MHA: out Linear
AddVarToScope(param_scope, "weights_l", {1024, 1024});
AddVarToScope(param_scope, "bias_l", {1024});
// MHA: pre Layer Norm
AddVarToScope(param_scope, "ffn_ln_scale", {1024});
AddVarToScope(param_scope, "ffn_ln_bias", {1024});
// FFN: fc1 -> (gelu) -> fc2
AddVarToScope(param_scope, "ffn_weights0", {1024, 4096});
AddVarToScope(param_scope, "ffn_weights1", {4096, 1024});
AddVarToScope(param_scope, "ffn_bias0", {4096});
AddVarToScope(param_scope, "ffn_bias1", {1024});
return param_scope;
}
TEST(FusedMultiTransformerEncoderPass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, weights_0) matmul_v2 -> matmul_out0
// (x, weights_1) matmul_v2 -> matmul_out1
// (x, weights_2) matmul_v2 -> matmul_out2
// (matmul_out0, bias_0) elementwise_add -> eltadd_0
// (matmul_out1, bias_1) elementwise_add -> eltadd_1
// (matmul_out2, bias_2) elementwise_add -> eltadd_2
// (eltadd_0) reshape2 -> reshape_0
// (eltadd_1) reshape2 -> reshape_1
// (eltadd_2) reshape2 -> reshape_2
// (reshape_0) transpose2 -> transpose_0
// (reshape_1) transpose2 -> transpose_1
// (reshape_2) transpose2 -> transpose_2
// (transpose_0) scale -> scale_0
// (scale_0, transpose_1) matmul -> matmul_qk
// (matmul_qk, bias_qk) elementwise_add -> eltadd_qk
// (eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul_v2 -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) matmul_v2 -> matmul_linear
// (matmul_linear) elementwise_add -> eltadd_linear
// (eltadd_linear) elementwise_add -> attention_out
//
// (attention_out, scale, bias) layer_norm -> layer_norm_out
// (layer_norm_out, ffn_matmul0_w) matmul_v2 -> ffn_matmul0
// (ffn_matmul0, ffn_bias0) elementwise_add -> ffn_eltadd0
// (ffn_eltadd0) gelu -> ffn_gelu
// (ffn_gelu) matmul_v2 -> ffn_matmul1
// (ffn_matmul1, ffn_bias1) elementwise_add -> ffn_eltadd1
// (layer_norm_out, ffn_eltadd1) elementwise_add -> ffn_output
// (ffn_output, scale, bias) layer_norm -> ffn_layer_norm_out
Layers layers;
// MHA: pre LayerNorm
auto* x = layers.data("x", {1, 128, 1024});
// MHA: QKV fc
auto* weights_0 = layers.data("weights0", {1024, 1024}, true);
auto* weights_1 = layers.data("weights1", {1024, 1024}, true);
auto* weights_2 = layers.data("weights2", {1024, 1024}, true);
auto* matmul_out_0 = layers.matmul_v2(x, weights_0, nullptr, false, false);
auto* matmul_out_1 = layers.matmul_v2(x, weights_1, nullptr, false, false);
auto* matmul_out_2 = layers.matmul_v2(x, weights_2, nullptr, false, false);
auto* b0 = layers.data("bias_0", {1024}, true);
auto* b1 = layers.data("bias_1", {1024}, true);
auto* b2 = layers.data("bias_2", {1024}, true);
auto* elementwise_out_0 =
layers.elementwise_add(matmul_out_0, b0, nullptr, 2);
auto* elementwise_out_1 =
layers.elementwise_add(matmul_out_1, b1, nullptr, 2);
auto* elementwise_out_2 =
layers.elementwise_add(matmul_out_2, b2, nullptr, 2);
std::vector<int> shape = {1, 128, 16, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
auto* reshape_1 = layers.reshape2(elementwise_out_1, shape, true);
auto* reshape_2 = layers.reshape2(elementwise_out_2, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto* transpose_1 = layers.transpose2(reshape_1, axis, true);
auto* transpose_2 = layers.transpose2(reshape_2, axis, true);
// q scale
auto* scale_q = layers.scale(transpose_0, 0.125, 0, false);
// MHA: QK matmul
auto* matmul_qk =
layers.matmul_v2(scale_q, transpose_1, nullptr, false, true);
auto* bqk = layers.data("biasqk", {1, 1, 1, 128}, true);
auto* elementwise_qk = layers.elementwise_add(matmul_qk, bqk, nullptr, -1);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
// MHA: QKV matmul
auto* matmul_qkv = layers.matmul_v2(softmax_qk, transpose_2);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 1024}, true);
// MHA: out Linear
auto* weights_l = layers.data("weights_l", {1024, 1024}, true);
auto* bias_l = layers.data("bias_l", {1024}, true);
auto* linear_matmut_out =
layers.matmul_v2(reshape_qkv_out, weights_l, nullptr, false, false);
auto* linear_eltadd_out =
layers.elementwise_add(linear_matmut_out, bias_l, nullptr, 2);
auto* attention_out = layers.elementwise_add(x, linear_eltadd_out);
// post LayerNorm
auto* ln_scale = layers.data("ln_scale", {1024}, true);
auto* ln_bias = layers.data("ln_bias", {1024}, true);
auto* ln_out = layers.layer_norm(attention_out, ln_scale, ln_bias)[0];
// FFN: fc1 -> gelu -> fc2
auto* ffn_weights0 = layers.data("ffn_weights0", {1024, 4096}, true);
auto* ffn_weights1 = layers.data("ffn_weights1", {4096, 1024}, true);
auto* ffn_bias0 = layers.data("ffn_bias0", {4096}, true);
auto* ffn_bias1 = layers.data("ffn_bias1", {1024}, true);
auto* ffn_matmul0_out =
layers.matmul_v2(ln_out, ffn_weights0, nullptr, false, true);
auto* ffn_eltadd0_out =
layers.elementwise_add(ffn_matmul0_out, ffn_bias0, nullptr, 2);
auto* ffn_gelu_out = layers.gelu(ffn_eltadd0_out);
auto* ffn_matmul1_out =
layers.matmul_v2(ffn_gelu_out, ffn_weights1, nullptr, false, true);
auto* ffn_eltadd1_out =
layers.elementwise_add(ffn_matmul1_out, ffn_bias1, nullptr, 2);
auto* ffn_out = layers.elementwise_add(ln_out, ffn_eltadd1_out);
// FFN: post LayerNorm
auto* ffn_ln_scale = layers.data("ffn_ln_scale", {1024}, true);
auto* ffn_ln_bias = layers.data("ffn_ln_bias", {1024}, true);
UNUSED auto res = layers.layer_norm(ffn_out, ffn_ln_scale, ffn_ln_bias)[0];
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
graph->Set("enable_int8", new bool(false));
auto pass =
PassRegistry::Instance().Get("fused_multi_transformer_encoder_pass");
if (pass.get() == nullptr)
LOG(INFO) << "get fused_multi_transformer_encoder_pass failed";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
int num_fused_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 58,
common::errors::InvalidArgument(
"After the fused_multi_transformer_encoder_pass, The "
"node num in graph "
"should be %d, but the result is %d",
num_nodes_before - 58,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the fused_multi_transformer_encoder pass, "
"there should be one fused_multi_transformer op, "
"but the result is %d",
num_fused_nodes_after));
}
TEST(FusedMultiTransformerEncoderPass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("fused_multi_transformer_encoder_pass"));
}
TEST(MultiDevicesFusedMultiTransformerEncoderPass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x) c_identity -> c_identity0_out
// (x) c_identity -> c_identity1_out
// (x) c_identity -> c_identity2_out
// (c_identity0_out, weights_0) matmul_v2 -> matmul_out0
// (c_identity1_out, weights_1) matmul_v2 -> matmul_out1
// (c_identity2_out, weights_2) matmul_v2 -> matmul_out2
// (matmul_out0, bias_0) elementwise_add -> eltadd_0
// (matmul_out1, bias_1) elementwise_add -> eltadd_1
// (matmul_out2, bias_2) elementwise_add -> eltadd_2
// (eltadd_0) reshape2 -> reshape_0
// (eltadd_1) reshape2 -> reshape_1
// (eltadd_2) reshape2 -> reshape_2
// (reshape_0) transpose2 -> transpose_0
// (reshape_1) transpose2 -> transpose_1
// (reshape_2) transpose2 -> transpose_2
// (transpose_0) scale -> scale_0
// (scale_0, transpose_1) matmul -> matmul_qk
// (matmul_qk, bias_qk) elementwise_add -> eltadd_qk
// (eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul_v2 -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) matmul_v2 -> matmul_linear
// (matmul_linear) c_all_reduce -> c_all_reduce_out
// (c_all_reduce_out) elementwise_add -> eltadd_linear
// (eltadd_linear) elementwise_add -> attention_out
//
// (attention_out, scale, bias) layer_norm -> layer_norm_out
// (layer_norm_out) c_identity -> ffn_c_identity_out
// (ffn_c_identity_out, ffn_matmul0_w)matmul_v2 -> ffn_matmul0
// (ffn_matmul0, ffn_bias0) elementwise_add -> ffn_eltadd0
// (ffn_eltadd0) gelu -> ffn_gelu
// (ffn_gelu) matmul_v2 -> ffn_matmul1
// (ffn_matmul1) c_all_reduce -> ffn_c_all_reduce_out
// (ffn_c_all_reduce_out, ffn_bias1)elementwise_add -> ffn_eltadd1
// (layer_norm_out, ffn_eltadd1) elementwise_add -> ffn_output
// (ffn_output, scale, bias) layer_norm -> ffn_layer_norm_out
Layers layers;
// MHA: pre LayerNorm
auto* x = layers.data("x", {1, 128, 1024});
auto* c_identity0_out = layers.c_identity(x);
auto* c_identity1_out = layers.c_identity(x);
auto* c_identity2_out = layers.c_identity(x);
// MHA: QKV fc
auto* weights_0 = layers.data("weights0", {1024, 1024}, true);
auto* weights_1 = layers.data("weights1", {1024, 1024}, true);
auto* weights_2 = layers.data("weights2", {1024, 1024}, true);
auto* matmul_out_0 =
layers.matmul_v2(c_identity0_out, weights_0, nullptr, false, false);
auto* matmul_out_1 =
layers.matmul_v2(c_identity1_out, weights_1, nullptr, false, false);
auto* matmul_out_2 =
layers.matmul_v2(c_identity2_out, weights_2, nullptr, false, false);
auto* b0 = layers.data("bias_0", {1024}, true);
auto* b1 = layers.data("bias_1", {1024}, true);
auto* b2 = layers.data("bias_2", {1024}, true);
auto* elementwise_out_0 =
layers.elementwise_add(matmul_out_0, b0, nullptr, 2);
auto* elementwise_out_1 =
layers.elementwise_add(matmul_out_1, b1, nullptr, 2);
auto* elementwise_out_2 =
layers.elementwise_add(matmul_out_2, b2, nullptr, 2);
std::vector<int> shape = {1, 128, 16, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
auto* reshape_1 = layers.reshape2(elementwise_out_1, shape, true);
auto* reshape_2 = layers.reshape2(elementwise_out_2, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto* transpose_1 = layers.transpose2(reshape_1, axis, true);
auto* transpose_2 = layers.transpose2(reshape_2, axis, true);
// q scale
auto* scale_q = layers.scale(transpose_0, 0.125, 0, false);
// MHA: QK matmul
auto* matmul_qk =
layers.matmul_v2(scale_q, transpose_1, nullptr, false, true);
auto* bqk = layers.data("biasqk", {1, 1, 1, 128}, true);
auto* elementwise_qk = layers.elementwise_add(matmul_qk, bqk, nullptr, -1);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
// MHA: QKV matmul
auto* matmul_qkv = layers.matmul_v2(softmax_qk, transpose_2);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 1024}, true);
// MHA: out Linear
auto* weights_l = layers.data("weights_l", {1024, 1024}, true);
auto* bias_l = layers.data("bias_l", {1024}, true);
auto* linear_matmut_out =
layers.matmul_v2(reshape_qkv_out, weights_l, nullptr, false, false);
auto* c_allreduce_out = layers.c_allreduce_sum(linear_matmut_out);
auto* linear_eltadd_out =
layers.elementwise_add(c_allreduce_out, bias_l, nullptr, 2);
auto* attention_out = layers.elementwise_add(x, linear_eltadd_out);
// post LayerNorm
auto* ln_scale = layers.data("ln_scale", {1024}, true);
auto* ln_bias = layers.data("ln_bias", {1024}, true);
auto* ln_out = layers.layer_norm(attention_out, ln_scale, ln_bias)[0];
auto* ffn_c_identity_out = layers.c_identity(ln_out);
// FFN: fc1 -> gelu -> fc2
auto* ffn_weights0 = layers.data("ffn_weights0", {1024, 4096}, true);
auto* ffn_weights1 = layers.data("ffn_weights1", {4096, 1024}, true);
auto* ffn_bias0 = layers.data("ffn_bias0", {4096}, true);
auto* ffn_bias1 = layers.data("ffn_bias1", {1024}, true);
auto* ffn_matmul0_out =
layers.matmul_v2(ffn_c_identity_out, ffn_weights0, nullptr, false, false);
auto* ffn_eltadd0_out =
layers.elementwise_add(ffn_matmul0_out, ffn_bias0, nullptr, 2);
auto* ffn_gelu_out = layers.gelu(ffn_eltadd0_out);
auto* ffn_matmul1_out =
layers.matmul_v2(ffn_gelu_out, ffn_weights1, nullptr, false, false);
auto* ffn_allreduce_out = layers.c_allreduce_sum(ffn_matmul1_out);
auto* ffn_eltadd1_out =
layers.elementwise_add(ffn_allreduce_out, ffn_bias1, nullptr, 2);
auto* ffn_out = layers.elementwise_add(ln_out, ffn_eltadd1_out);
// FFN: post LayerNorm
auto* ffn_ln_scale = layers.data("ffn_ln_scale", {1024}, true);
auto* ffn_ln_bias = layers.data("ffn_ln_bias", {1024}, true);
UNUSED auto res = layers.layer_norm(ffn_out, ffn_ln_scale, ffn_ln_bias)[0];
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
graph->Set("enable_int8", new bool(false));
auto pass = PassRegistry::Instance().Get(
"multi_devices_fused_multi_transformer_encoder_pass");
if (pass.get() == nullptr)
LOG(INFO)
<< "get multi_devices_fused_multi_transformer_encoder_pass failed";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
int num_fused_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 70,
common::errors::InvalidArgument(
"After the fused_multi_transformer_encoder_pass, The "
"node num in graph "
"should be %d, but the result is %d",
num_nodes_before - 70,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the fused_multi_transformer_encoder pass, "
"there should be one fused_multi_transformer op, "
"but the result is %d",
num_fused_nodes_after));
}
TEST(MultiDevicesFusedMultiTransformerEncoderPass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible(
"multi_devices_fused_multi_transformer_encoder_pass"));
}
TEST(FusedMultiTransformerEncoderFuseQKVPass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, ln_scale, ln_bias) layer_norm -> layer_norm_out
// (layer_norm_out, weights_0) matmul_v2 -> matmul_out0
// (matmul_out0, bias_0) elementwise_add -> eltadd_0
// (eltadd_0) reshape2 -> reshape_0
// (reshape_0) transpose2 -> transpose_0
// (transpose_0) split -> split_q, split_k,
// split_v (split_k) assign -> assign_k
// (split_v) assign -> assign_v
// (split_q, split_k) matmul_v2 -> matmul_qk
// (matmul_qk) scale -> scale_qk
// (scale_qk, eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul_v2 -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) matmul_v2 -> matmul_linear
// (matmul_linear) elementwise_add -> eltadd_linear
// (eltadd_out) elementwise_add -> attention_out
//
// (attention_out, scale, bias) layer_norm -> ffn_layer_norm_out
// (layer_norm_out, ffn_matmul0_w) matmul_v2 -> ffn_matmul0
// (ffn_matmul0, ffn_bias0) elementwise_add -> ffn_eltadd0
// (ffn_eltadd0) gelu -> ffn_gelu
// (ffn_gelu) matmul_v2 -> ffn_matmul1
// (ffn_matmul1, ffn_bias1) elementwise_add -> ffn_eltadd1
// (attention_out, ffn_eltadd1) elementwise_add -> ffn_output
//
// (transpose_1, transpose_2) while -> decoder block
Layers layers;
// MHA: pre LayerNorm
auto* x = layers.data("x", {1, 128, 1024});
auto* ln_scale = layers.data("ln_scale", {1024}, true);
auto* ln_bias = layers.data("ln_bias", {1024}, true);
auto* ln_out = layers.layer_norm(x, ln_scale, ln_bias)[0];
// MHA: QKV fc
auto* weights_0 = layers.data("weights0", {1024, 3072}, true);
auto* matmul_out_0 =
layers.matmul_v2(ln_out, weights_0, nullptr, false, true);
auto* b0 = layers.data("bias_0", {3072}, true);
auto* elementwise_out_0 =
layers.elementwise_add(matmul_out_0, b0, nullptr, 2);
std::vector<int> shape = {1, 128, 16, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto split_outs = layers.split(transpose_0, 3, 3);
auto* split_q = split_outs[0];
auto* split_k = split_outs[1];
auto* split_v = split_outs[2];
layers.assign(split_k);
layers.assign(split_v);
// Link to decoder while block
layers.while_loop({split_k, split_v});
// MHA: QK matmul
auto* matmul_qk = layers.matmul_v2(split_q, split_k, nullptr, false, true);
auto* scale_qk = layers.scale(matmul_qk, 0.125, 0, false);
auto* bqk = layers.data("biasqk", {1, 1, 1, 128}, true);
auto* elementwise_qk = layers.elementwise_add(scale_qk, bqk);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
// MHA: QKV matmul
auto* matmul_qkv = layers.matmul_v2(softmax_qk, split_v);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 1024}, true);
// MHA: out Linear
auto* weights_l = layers.data("weights_l", {1024, 1024}, true);
auto* bias_l = layers.data("weightsl", {1024, 1024}, true);
auto* linear_matmut_out =
layers.matmul_v2(reshape_qkv_out, weights_l, nullptr, false, true);
auto* linear_eltadd_out =
layers.elementwise_add(linear_matmut_out, bias_l, nullptr, 2);
auto* attention_out = layers.elementwise_add(x, linear_eltadd_out);
// FFN: pre LayerNorm
auto* ffn_ln_scale = layers.data("ffn_ln_scale", {1024}, true);
auto* ffn_ln_bias = layers.data("ffn_ln_bias", {1024}, true);
auto* ffn_ln_out =
layers.layer_norm(attention_out, ffn_ln_scale, ffn_ln_bias)[0];
// FFN: fc1 -> gelu -> fc2
auto* ffn_weights0 = layers.data("ffn_weights0", {1024, 4096}, true);
auto* ffn_weights1 = layers.data("ffn_weights1", {4096, 1024}, true);
auto* ffn_bias0 = layers.data("ffn_bias0", {4096}, true);
auto* ffn_bias1 = layers.data("ffn_bias1", {1024}, true);
auto* ffn_matmul0_out =
layers.matmul_v2(ffn_ln_out, ffn_weights0, nullptr, false, true);
auto* ffn_eltadd0_out =
layers.elementwise_add(ffn_matmul0_out, ffn_bias0, nullptr, 2);
auto* ffn_gelu_out = layers.gelu(ffn_eltadd0_out);
auto* ffn_matmul1_out =
layers.matmul_v2(ffn_gelu_out, ffn_weights1, nullptr, false, true);
auto* ffn_eltadd1_out =
layers.elementwise_add(ffn_matmul1_out, ffn_bias1, nullptr, 2);
layers.elementwise_add(attention_out, ffn_eltadd1_out);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("enable_int8", new bool(false));
graph->Set("__param_scope__", CreateParamScope());
auto pass = PassRegistry::Instance().Get(
"fused_multi_transformer_encoder_fuse_qkv_pass");
if (pass.get() == nullptr)
LOG(INFO) << "get fused_multi_transformer_encoder_fuse_qkv_pass failed";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
int num_fused_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(
num_nodes_before,
num_nodes_after + 46,
common::errors::InvalidArgument(
"After the fused_multi_transformer_encoder_fuse_qkv_pass, "
"The node num in graph should be %d, but the result is %d",
num_nodes_before - 46,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the fused_multi_transformer_encoder_fuse_qkv "
"pass, there should be one fused_multi_transformer "
"op, but the result is %d",
num_fused_nodes_after));
}
TEST(FusedMultiTransformerEncoderFuseQKVPass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("fused_multi_transformer_encoder_fuse_qkv_pass"));
}
TEST(MultiDevicesFusedMultiTransformerEncoderFuseQKVPass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, ln_scale, ln_bias) layer_norm -> layer_norm_out
// (layer_norm_out) c_identity -> c_identity_out
// (c_identity_out, weights_0) matmul_v2 -> matmul_out0
// (matmul_out0) elementwise_add -> eltadd_0
// (eltadd_0) reshape2 -> reshape_0
// (reshape_0) transpose2 -> transpose_0
// (transpose_0) split -> split_q, split_k,
// split_v (split_k) assign -> assign_k
// (split_v) assign -> assign_v
// (split_q, split_k) matmul_v2 -> matmul_qk
// (matmul_qk) scale -> scale_qk
// (scale_qk, bias_qk) elementwise_add -> eltadd_qk
// (eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul_v2 -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) matmul_v2 -> matmul_linear
// (matmul_linear) c_all_reduce -> c_all_reduce_out
// (c_all_reduce_out) elementwise_add -> eltadd_linear
// (eltadd_out) elementwise_add -> attention_out
//
// (attention_out, scale, bias) layer_norm -> ffn_layer_norm_out
// (ffn_layer_norm_out) c_identity -> ffn_c_identity_out
// (ffn_c_identity_out, ffn_matmul0_w)matmul_v2 -> ffn_matmul0
// (ffn_matmul0, ffn_bias0) elementwise_add -> ffn_eltadd0
// (ffn_eltadd0) gelu -> ffn_gelu
// (ffn_gelu) matmul_v2 -> ffn_matmul1
// (ffn_matmul1) c_all_reduce -> ffn_c_all_reduce_out
// (ffn_c_all_reduce_out, ffn_bias1)elementwise_add -> ffn_eltadd1
// (attention_out, ffn_eltadd1) elementwise_add -> ffn_output
//
// (transpose_1, transpose_2) while -> decoder block
Layers layers;
// MHA: pre LayerNorm
auto* x = layers.data("x", {1, 128, 1024});
auto* ln_scale = layers.data("ln_scale", {1024}, true);
auto* ln_bias = layers.data("ln_bias", {1024}, true);
auto* ln_out = layers.layer_norm(x, ln_scale, ln_bias)[0];
auto* c_identity_out = layers.c_identity(ln_out);
// MHA: QKV fc
auto* weights_0 = layers.data("weights0", {1024, 3072}, true);
auto* matmul_out_0 =
layers.matmul_v2(c_identity_out, weights_0, nullptr, false, true);
auto* b0 = layers.data("bias_0", {3072}, true);
auto* elementwise_out_0 =
layers.elementwise_add(matmul_out_0, b0, nullptr, 2);
std::vector<int> shape = {1, 128, 16, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto split_outs = layers.split(transpose_0, 3, 3);
auto* split_q = split_outs[0];
auto* split_k = split_outs[1];
auto* split_v = split_outs[2];
layers.assign(split_k);
layers.assign(split_v);
// Link to decoder while block
layers.while_loop({split_k, split_v});
// MHA: QK matmul
auto* matmul_qk = layers.matmul_v2(split_q, split_k, nullptr, false, true);
auto* scale_qk = layers.scale(matmul_qk, 0.125, 0, false);
auto* bqk = layers.data("biasqk", {1, 1, 1, 128}, true);
auto* elementwise_qk = layers.elementwise_add(scale_qk, bqk);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
// MHA: QKV matmul
auto* matmul_qkv = layers.matmul_v2(softmax_qk, split_v);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 1024}, true);
// MHA: out Linear
auto* weights_l = layers.data("weights_l", {1024, 1024}, true);
auto* bias_l = layers.data("weightsl", {1024, 1024}, true);
auto* linear_matmut_out =
layers.matmul_v2(reshape_qkv_out, weights_l, nullptr, false, true);
auto* c_allreduce_out = layers.c_allreduce_sum(linear_matmut_out);
auto* linear_eltadd_out =
layers.elementwise_add(c_allreduce_out, bias_l, nullptr, 2);
auto* attention_out = layers.elementwise_add(x, linear_eltadd_out);
// FFN: pre LayerNorm
auto* ffn_ln_scale = layers.data("ffn_ln_scale", {1024}, true);
auto* ffn_ln_bias = layers.data("ffn_ln_bias", {1024}, true);
auto* ffn_ln_out =
layers.layer_norm(attention_out, ffn_ln_scale, ffn_ln_bias)[0];
auto* ffn_c_identity_out = layers.c_identity(ffn_ln_out);
// FFN: fc1 -> gelu -> fc2
auto* ffn_weights0 = layers.data("ffn_weights0", {1024, 4096}, true);
auto* ffn_weights1 = layers.data("ffn_weights1", {4096, 1024}, true);
auto* ffn_bias0 = layers.data("ffn_bias0", {4096}, true);
auto* ffn_bias1 = layers.data("ffn_bias1", {1024}, true);
auto* ffn_matmul0_out =
layers.matmul_v2(ffn_c_identity_out, ffn_weights0, nullptr, false, true);
auto* ffn_eltadd0_out =
layers.elementwise_add(ffn_matmul0_out, ffn_bias0, nullptr, 2);
auto* ffn_gelu_out = layers.gelu(ffn_eltadd0_out);
auto* ffn_matmul1_out =
layers.matmul_v2(ffn_gelu_out, ffn_weights1, nullptr, false, true);
auto* ffn_allreduce_out = layers.c_allreduce_sum(ffn_matmul1_out);
auto* ffn_eltadd1_out =
layers.elementwise_add(ffn_allreduce_out, ffn_bias1, nullptr, 2);
layers.elementwise_add(attention_out, ffn_eltadd1_out);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("enable_int8", new bool(false));
graph->Set("__param_scope__", CreateParamScope());
auto pass = PassRegistry::Instance().Get(
"multi_devices_fused_multi_transformer_encoder_fuse_qkv_pass");
if (pass.get() == nullptr)
LOG(INFO)
<< "get multi_devices_fused_multi_transformer_encoder_fuse_qkv_pass "
"failed";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
int num_fused_nodes_after = GetNumOpNodes(graph, "fused_multi_transformer");
PADDLE_ENFORCE_EQ(
num_nodes_before,
num_nodes_after + 54,
common::errors::InvalidArgument(
"After the fused_multi_transformer_encoder_fuse_qkv_pass, "
"The node num in graph should be %d, but the result is %d",
num_nodes_before - 54,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the fused_multi_transformer_encoder_fuse_qkv "
"multi-devices pass, there should be one "
"fused_multi_transformer op, but the result is %d",
num_fused_nodes_after));
}
TEST(MultiDevicesFusedMultiTransformerEncoderFuseQKVPass,
pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible(
"multi_devices_fused_multi_transformer_encoder_fuse_qkv_pass"));
}
} // namespace paddle::framework::ir
USE_PASS(fused_multi_transformer_encoder_pass);
USE_PASS(fused_multi_transformer_encoder_fuse_qkv_pass);
USE_PASS(multi_devices_fused_multi_transformer_encoder_pass);
USE_PASS(multi_devices_fused_multi_transformer_encoder_fuse_qkv_pass);
@@ -0,0 +1,18 @@
# Fusion Group IR Pass Tests
cc_test(
test_fusion_group_pass
SRCS fusion_group_pass_test.cc
DEPS fusion_group_pass graph_viz_pass)
if(WITH_GPU OR WITH_ROCM)
cc_test(
test_code_generator
SRCS code_generator_test.cc
DEPS code_generator phi common lod_tensor graph_viz_pass)
# Set timeout for test_code_generator
if(WITH_TESTING AND TEST test_code_generator)
set_tests_properties(test_code_generator PROPERTIES TIMEOUT 120)
endif()
endif()
@@ -0,0 +1,510 @@
/* Copyright (c) 2019 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 <cmath>
#include <string>
#include "paddle/fluid/framework/ir/fusion_group/code_generator.h"
#include "paddle/fluid/framework/ir/fusion_group/operation.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/phi/backends/device_code.h"
#include "paddle/phi/common/float16.h"
namespace phi {
class DenseTensor;
} // namespace phi
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
namespace paddle::framework::ir::fusion_group {
// relu
inline float relu(float x) { return x > 0 ? x : 0.; } // NOLINT
inline float relu_grad_dx(float x, float out, float dout) {
return out > 0 ? dout : 0;
}
// sigmoid
inline float sigmoid(float x) { return (1.0f) / (1.0 + std::exp(-x)); }
inline float sigmoid_grad_dx(float x, float out, float dout) {
return dout * out * (1 - out);
}
// tanh
inline float tanh(float x) { return (2.0f) / (1.0 + std::exp(-2 * x)) - 1.0; }
inline float tanh_grad_dx(float x, float out, float dout) {
return dout * (1.0 - out * out);
}
// elementwise_add
inline float elementwise_add(float x, float y) { return x + y; }
inline float elementwise_add_grad_dx(float x, float y, float out, float dout) {
return dout;
}
inline float elementwise_add_grad_dy(float x, float y, float out, float dout) {
return dout;
}
// elementwise_sub
inline float elementwise_sub(float x, float y) { return x - y; }
inline float elementwise_sub_grad_dx(float x, float y, float out, float dout) {
return dout;
}
inline float elementwise_sub_grad_dy(float x, float y, float out, float dout) {
return -dout;
}
// elementwise_mul
inline float elementwise_mul(float x, float y) { return x * y; }
inline float elementwise_mul_grad_dx(float x, float y, float out, float dout) {
return dout * y;
}
inline float elementwise_mul_grad_dy(float x, float y, float out, float dout) {
return dout * x;
}
void CheckOutput(const std::vector<OperationExpression>& expressions,
const std::vector<phi::DenseTensor> cpu_tensors,
const std::vector<int> input_ids_of_subgraph,
const std::vector<int> output_ids_of_subgraph,
int i,
float eps) {
std::vector<float> var(cpu_tensors.size());
for (auto id : input_ids_of_subgraph) {
if (id >= 0) {
var[id] = cpu_tensors[id].data<float>()[i];
}
}
for (auto expression : expressions) {
std::string op_type = expression.GetOpType();
auto input_ids = expression.GetInputIds();
auto output_ids = expression.GetOutputIds();
if (op_type == "relu") {
var[output_ids[0]] = relu(var[input_ids[0]]);
} else if (op_type == "sigmoid") {
var[output_ids[0]] = sigmoid(var[input_ids[0]]);
} else if (op_type == "tanh") {
var[output_ids[0]] = tanh(var[input_ids[0]]);
} else if (op_type == "elementwise_add") {
var[output_ids[0]] =
elementwise_add(var[input_ids[0]], var[input_ids[1]]);
} else if (op_type == "elementwise_sub") {
var[output_ids[0]] =
elementwise_sub(var[input_ids[0]], var[input_ids[1]]);
} else if (op_type == "elementwise_mul") {
var[output_ids[0]] =
elementwise_mul(var[input_ids[0]], var[input_ids[1]]);
} else if (op_type == "relu_grad") {
var[output_ids[0]] =
relu_grad_dx(0, var[input_ids[1]], var[input_ids[2]]);
} else if (op_type == "sigmoid_grad") {
var[output_ids[0]] =
sigmoid_grad_dx(0, var[input_ids[1]], var[input_ids[2]]);
} else if (op_type == "tanh_grad") {
var[output_ids[0]] =
tanh_grad_dx(0, var[input_ids[1]], var[input_ids[2]]);
} else if (op_type == "elementwise_add_grad") {
var[output_ids[0]] = elementwise_add_grad_dx(0, 0, 0, var[input_ids[3]]);
var[output_ids[1]] = elementwise_add_grad_dy(0, 0, 0, var[input_ids[3]]);
} else if (op_type == "elementwise_mul_grad") {
var[output_ids[0]] =
elementwise_mul_grad_dx(0, var[input_ids[1]], 0, var[input_ids[3]]);
var[output_ids[1]] =
elementwise_mul_grad_dy(var[input_ids[0]], 0, 0, var[input_ids[3]]);
}
}
for (auto id : output_ids_of_subgraph) {
float actual = cpu_tensors[id].data<float>()[i];
float expect = var[id];
if (fabs(actual - expect) > eps) {
LOG(INFO) << "Precision check failed from i = " << id
<< ", expect: " << expect << ", actual: " << actual;
EXPECT_LT(fabs(actual - expect), eps);
}
}
}
template <typename T>
void SetupRandomCPUTensor(phi::DenseTensor* tensor) {
static unsigned int seed = 100;
std::mt19937 rng(seed++);
std::uniform_real_distribution<double> uniform_dist(0, 1);
T* ptr = tensor->data<T>();
EXPECT_NE(ptr, nullptr);
for (int64_t i = 0; i < tensor->numel(); ++i) {
ptr[i] = static_cast<T>(uniform_dist(rng)) - static_cast<T>(0.5);
}
}
} // namespace paddle::framework::ir::fusion_group
namespace fusion_group = paddle::framework::ir::fusion_group;
template <typename T>
void TestMainImpl(std::string func_name,
std::string code_str,
std::vector<phi::DenseTensor> cpu_tensors,
int n,
std::vector<int> input_ids,
std::vector<int> output_ids) {
bool is_float16 = std::type_index(typeid(T)) ==
std::type_index(typeid(phi::dtype::float16));
phi::GPUPlace place = phi::GPUPlace(0);
phi::GPUDeviceCode device_code(place, func_name, code_str);
#ifdef PADDLE_WITH_HIP
device_code.Compile(true);
#else
device_code.Compile(is_float16);
#endif
std::vector<phi::DenseTensor> gpu_tensors(cpu_tensors.size());
std::vector<phi::DenseTensor> tmp_cpu_tensors(cpu_tensors.size());
std::vector<T*> gpu_ptrs(gpu_tensors.size());
std::vector<void*> args;
args.push_back(&n);
for (auto id : input_ids) {
if (id >= 0) {
gpu_ptrs[id] =
gpu_tensors[id].mutable_data<T>(cpu_tensors[id].dims(), place);
fusion_group::SetupRandomCPUTensor<float>(&cpu_tensors[id]);
if (is_float16) {
phi::dtype::float16* tmp_cpu_ptr =
tmp_cpu_tensors[id].mutable_data<phi::dtype::float16>(
cpu_tensors[id].dims(), phi::CPUPlace());
const float* cpu_ptr = cpu_tensors[id].data<float>();
for (int64_t i = 0; i < cpu_tensors[id].numel(); ++i) {
tmp_cpu_ptr[i] = phi::dtype::float16(cpu_ptr[i]);
}
paddle::framework::TensorCopySync(
tmp_cpu_tensors[id], place, &gpu_tensors[id]);
} else {
paddle::framework::TensorCopySync(
cpu_tensors[id], place, &gpu_tensors[id]);
}
args.push_back(&gpu_ptrs[id]);
}
}
for (auto id : output_ids) {
gpu_ptrs[id] =
gpu_tensors[id].mutable_data<T>(cpu_tensors[id].dims(), place);
args.push_back(&gpu_ptrs[id]);
}
device_code.SetNumThreads(1024);
device_code.SetWorkloadPerThread(1);
device_code.Launch(n, &args);
auto* dev_ctx = reinterpret_cast<phi::GPUContext*>(
phi::DeviceContextPool::Instance().Get(place));
dev_ctx->Wait();
// Copy the results back to CPU.
for (auto id : output_ids) {
if (is_float16) {
phi::dtype::float16* tmp_cpu_ptr =
tmp_cpu_tensors[id].mutable_data<phi::dtype::float16>(
cpu_tensors[id].dims(), phi::CPUPlace());
paddle::framework::TensorCopySync(
gpu_tensors[id], phi::CPUPlace(), &tmp_cpu_tensors[id]);
float* cpu_ptr = cpu_tensors[id].mutable_data<float>(
cpu_tensors[id].dims(), phi::CPUPlace());
for (int64_t i = 0; i < cpu_tensors[id].numel(); ++i) {
cpu_ptr[i] = static_cast<float>(tmp_cpu_ptr[i]);
}
} else {
paddle::framework::TensorCopySync(
gpu_tensors[id], phi::CPUPlace(), &cpu_tensors[id]);
}
}
}
void TestElementwiseMain(
std::string func_name,
std::string code_str,
std::vector<fusion_group::OperationExpression> expressions,
std::vector<int> input_ids,
std::vector<int> output_ids,
std::string dtype) {
std::unordered_set<int> ids;
for (auto id : input_ids) {
ids.insert(id);
}
for (auto id : output_ids) {
ids.insert(id);
}
// Prepare CPU tensors which always hold float.
std::vector<phi::DenseTensor> cpu_tensors(ids.size());
auto dims = common::make_ddim(
{static_cast<int64_t>(256), static_cast<int64_t>(1024)});
for (auto& cpu_tensor : cpu_tensors) {
cpu_tensor.mutable_data<float>(dims, phi::CPUPlace());
}
int n = cpu_tensors[0].numel();
if (dtype == "__half") {
TestMainImpl<phi::dtype::float16>(
func_name, code_str, cpu_tensors, n, input_ids, output_ids);
} else {
TestMainImpl<float>(
func_name, code_str, cpu_tensors, n, input_ids, output_ids);
}
// Check the results
float eps = (dtype == "__half") ? 1E-2 : 1E-5;
for (int i = 0; i < n; i++) {
fusion_group::CheckOutput(
expressions, cpu_tensors, input_ids, output_ids, i, eps);
}
}
void TestMain(std::string func_name,
std::vector<fusion_group::OperationExpression> expressions,
std::vector<int> input_ids,
std::vector<int> output_ids,
std::string dtype) {
fusion_group::OperationMap::Init();
fusion_group::CodeGenerator code_generator;
std::string code_str = code_generator.Generate(func_name, expressions);
VLOG(3) << code_str;
LOG(INFO) << "dtype: " << dtype;
TestElementwiseMain(
func_name, code_str, expressions, input_ids, output_ids, dtype);
}
void TestMain(fusion_group::SubGraph* subgraph,
std::vector<int> input_ids,
std::vector<int> output_ids,
std::string dtype) {
fusion_group::OperationMap::Init();
fusion_group::CodeGenerator code_generator;
std::string code_str = code_generator.Generate(subgraph);
VLOG(3) << code_str;
// Need to check the accuracy according to expressions.
std::vector<fusion_group::OperationExpression> expressions =
code_generator.ConvertToExpressions(subgraph);
TestElementwiseMain(subgraph->GetFuncName(),
code_str,
expressions,
input_ids,
output_ids,
dtype);
}
TEST(code_generator, elementwise) {
for (std::string dtype : {"float", "__half"}) {
// t2 = t0 * t1
// t4 = t2 + t3
// t6 = t4 - t5
// t7 = relu(t6)
// t8 = sigmoid(t7)
fusion_group::OperationExpression exp1(
"elementwise_mul", {0, 1}, {2}, dtype, dtype);
fusion_group::OperationExpression exp2(
"elementwise_add", {2, 3}, {4}, dtype, dtype);
fusion_group::OperationExpression exp3(
"elementwise_sub", {4, 5}, {6}, dtype, dtype);
fusion_group::OperationExpression exp4("relu", {6}, {7}, dtype, dtype);
fusion_group::OperationExpression exp5("sigmoid", {7}, {8}, dtype, dtype);
std::vector<fusion_group::OperationExpression> expressions = {
exp1, exp2, exp3, exp4, exp5};
// Expressions:
// Op(elementwise_mul), inputs:{0,1}, outputs:{2}
// Op(elementwise_add), inputs:{2,3}, outputs:{4}
// Op(elementwise_sub), inputs:{4,5}, outputs:{6}
// Op(relu), inputs:{6}, outputs:{7}
// Op(sigmoid), inputs:{7}, outputs:{8}
std::vector<int> input_ids = {0, 1, 3, 5};
std::vector<int> output_ids = {2, 4, 6, 7, 8};
TestMain("elementwise_kernel_0", expressions, input_ids, output_ids, dtype);
}
}
TEST(code_generator, elementwise_grad) {
for (std::string dtype : {"float", "__half"}) {
// The var order: t0, t1, t2, t3, t0', t1', t2', t3'
// t2 = t0 * t1
// t3 = relu(t2)
// t2' = relu_grad(t2, t3, t3')
// t0', t1' = elementwise_mul_grad(t0, t1, t2, t2')
fusion_group::OperationExpression exp1(
"relu_grad", {-1, 3, 7}, {6}, dtype, dtype);
fusion_group::OperationExpression exp2(
"elementwise_mul_grad", {0, 1, 2, 6}, {4, 5}, dtype, dtype);
std::vector<fusion_group::OperationExpression> expressions = {exp1, exp2};
// Expressions:
// Op(relu_grad), inputs:{2,3,7}, outputs:{6}
// Op(elementwise_mul_grad), inputs:{0,1,2,6}, outputs:{4,5}
std::vector<int> input_ids = {0, 1, 2, 3, 7};
std::vector<int> output_ids = {4, 5, 6};
TestMain(
"elementwise_grad_kernel_0", expressions, input_ids, output_ids, dtype);
}
}
std::unique_ptr<paddle::framework::ir::Graph> BuildGraph(bool backward,
std::string dtype) {
// inputs operator output
// --------------------------------------------------------
// x0 sigmoid -> tmp_0
// (tmp_0, x1) elementwise_mul -> tmp_1
// x2 tanh -> tmp_2
// (x3, tmp_2) elementwise_mul -> tmp_3
// (tmp_1, tmp_3) elementwise_add -> tmp_4
//
// Expression: tmp_4 = sigmoid(x0) * x1 + tanh(x2) * x3
// The var order (their ids may be different):
// backward is false - x0(0), x1(1), x2(2), x3(3);
// - tmp_0(4), tmp_2(5), tmp_3(6), tmp_1(7), tmp_4(8)
// backward is true - tmp_1(0), tmp_4@GRAD(1), tmp_3(2), tmp_4(3),
// tmp_2(4), x3(5), x1(6), tmp_0(7), x0(8), x2(9)
// - tmp_3@GRAD(10), tmp_1@GRAD(11), tmp_0@GRAD(12),
// tmp_2@GRAD(13), x2@GRAD(14), x0@GRAD(15),
// x3@GRAD(16), x1@GRAD(17)
paddle::framework::ir::Layers layers;
std::vector<int64_t> shape = {16, 32};
auto* x0 = layers.data("x0", shape);
auto* tmp_0 = layers.sigmoid(x0);
auto* x1 = layers.data("x1", shape);
auto* tmp_1 = layers.elementwise_mul(tmp_0, x1);
auto* x2 = layers.data("x2", shape);
auto* tmp_2 = layers.tanh(x2);
auto* x3 = layers.data("x3", shape);
auto* tmp_3 = layers.elementwise_mul(x3, tmp_2);
auto* tmp_4 = layers.elementwise_add(tmp_1, tmp_3);
std::vector<paddle::framework::VarDesc*> elementwise_vars = {
tmp_0, tmp_1, tmp_2, tmp_3, tmp_4};
for (auto* var : elementwise_vars) {
var->SetShape(shape);
}
if (backward) {
layers.backward({tmp_4});
}
std::unique_ptr<paddle::framework::ir::Graph> graph(
new paddle::framework::ir::Graph(layers.main_program()));
auto var_type = (dtype == "__half") ? paddle::framework::proto::VarType::FP16
: paddle::framework::proto::VarType::FP32;
for (auto* n : graph->Nodes()) {
if (n && n->IsVar() && n->Var()) {
n->Var()->SetDataType(var_type);
}
}
return graph;
}
std::unordered_set<paddle::framework::ir::Node*> DistilGradNodes(
const std::unique_ptr<paddle::framework::ir::Graph>& graph) {
auto is_grad_op = [&](paddle::framework::ir::Node* n) -> bool {
if (n && n->IsOp() && n->Op()) {
std::string suffix = "_grad";
std::string op_type = n->Op()->Type();
size_t pos = op_type.rfind(suffix);
return pos != std::string::npos &&
pos == (op_type.length() - suffix.length());
}
return false;
};
std::unordered_set<paddle::framework::ir::Node*> grad_nodes;
for (auto* n : graph->Nodes()) {
if (is_grad_op(n)) {
grad_nodes.insert(n);
} else if (n && n->IsVar() && n->Var()) {
// Remove forward op nodes from inputs
std::vector<paddle::framework::ir::Node*> inputs;
for (auto* in : n->inputs) {
if (in && in->IsOp() && in->Op() && is_grad_op(in)) {
inputs.push_back(in);
}
}
n->inputs = inputs;
// Remove forward op nodes from outputs
std::vector<paddle::framework::ir::Node*> outputs;
for (auto* out : n->outputs) {
if (out && out->IsOp() && out->Op() && is_grad_op(out)) {
outputs.push_back(out);
}
}
n->outputs = outputs;
grad_nodes.insert(n);
}
}
return grad_nodes;
}
TEST(code_generator, subgraph) {
for (std::string dtype : {"float", "__half"}) {
std::unique_ptr<paddle::framework::ir::Graph> graph =
BuildGraph(false, dtype);
fusion_group::SubGraph subgraph(
0, "elementwise_kernel_1", true, graph->Nodes());
// Expressions generated by code_generator (they may be different):
// Op(sigmoid), inputs:{0}, outputs:{4}
// Op(elementwise_mul), inputs:{4,1}, outputs:{7}
// Op(tanh), inputs:{2}, outputs:{5}
// Op(elementwise_mul), inputs:{3,5}, outputs:{6}
// Op(elementwise_add), inputs:{7,6}, outputs:{8}
std::vector<int> input_ids = {0, 1, 2, 3};
std::vector<int> output_ids = {4, 5, 6, 7, 8};
TestMain(&subgraph, input_ids, output_ids, dtype);
}
}
TEST(code_generator, subgraph_grad) {
for (std::string dtype : {"float", "__half"}) {
std::unique_ptr<paddle::framework::ir::Graph> graph =
BuildGraph(true, dtype);
fusion_group::SubGraph subgraph(
0, "elementwise_grad_kernel_1", true, DistilGradNodes(graph));
// Expressions generated by code_generator (they may be different):
// Op(elementwise_add_grad), inputs:{1,2,3,0}, outputs:{11,10}
// Op(elementwise_mul_grad), inputs:{5,4,2,10}, outputs:{17,13}
// Op(elementwise_mul_grad), inputs:{7,6,1,11}, outputs:{12,15}
// Op(sigmoid_grad), inputs:{8,7,12}, outputs:{16}
// Op(tanh_grad), inputs:{9,4,13}, outputs:{14}
std::vector<int> input_ids = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int> output_ids = {10, 11, 12, 13, 14, 15, 16, 17};
TestMain(&subgraph, input_ids, output_ids, dtype);
}
}
#endif
@@ -0,0 +1,158 @@
/* Copyright (c) 2019 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/fluid/framework/ir/fusion_group/fusion_group_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
namespace ir {
void VisualizeGraph(std::unique_ptr<Graph>* graph, std::string graph_viz_path) {
// Insert a graph_viz_pass to transform the graph to a .dot file.
// It can be used for debug.
auto graph_viz_pass = PassRegistry::Instance().Get("graph_viz_pass");
graph_viz_pass->Set("graph_viz_path", new std::string(graph_viz_path));
graph->reset(graph_viz_pass->Apply(graph->release()));
}
std::unique_ptr<Graph> BuildElementwiseListGraph(bool backward = false) {
// inputs operator output
// --------------------------------------------------------
// (x, y) mul -> tmp_0
// (tmp_0, z) elementwise_add -> tmp_1
// tmp_1 relu -> tmp_2
// (tmp_2, w) elementwise_add -> tmp_3
//
// Expression: tmp_3 = relu(mul(x, y) + z) + w
Layers layers;
std::vector<int64_t> shape = {16, 32};
auto* x = layers.data("x", {16, 16});
auto* y = layers.data("y", {16, 32});
auto* tmp_0 = layers.mul(x, y);
auto* z = layers.data("z", shape);
auto* tmp_1 = layers.elementwise_add(tmp_0, z);
auto* tmp_2 = layers.relu(tmp_1);
auto* w = layers.data("w", shape);
auto* tmp_3 = layers.elementwise_add(tmp_2, w);
std::vector<VarDesc*> elementwise_vars = {tmp_0, z, tmp_1, tmp_2, w, tmp_3};
for (auto* var : elementwise_vars) {
var->SetShape(shape);
}
if (backward) {
layers.backward({tmp_3});
}
std::unique_ptr<Graph> graph(new Graph(layers.main_program()));
for (auto* n : graph->Nodes()) {
if (n && n->IsVar() && n->Var()) {
n->Var()->SetDataType(proto::VarType::FP32);
}
}
return graph;
}
std::unique_ptr<Graph> BuildElementwiseTreeGraph(bool backward = false) {
// inputs operator output
// --------------------------------------------------------
// (x0, y0) mul -> tmp_0
// x1 sigmoid -> tmp_1
// (tmp_0, tmp_1) elementwise_mul -> tmp_2
// x2 sigmoid -> tmp_3
// x3 tanh -> tmp_4
// (tmp_3, tmp_4) elementwise_mul -> tmp_5
// (tmp_2, tmp_5) elementwise_add -> tmp_6
// x4 tanh -> tmp_7
// x5 sigmoid -> tmp_8
// (tmp_7, tmp_8) elementwise_mul -> tmp_9
// (tmp_6, tmp_9) mul -> tmp_10
//
// Expression: tmp_6 = mul(x0, y0) * sigmoid(x1) + sigmoid(x2) * tanh(x3)
// tmp_9 = tanh(x4) * sigmoid(x5)
// tmp_10 = mul(tmp_6, tmp_9)
Layers layers;
std::vector<int64_t> shape = {16, 32};
auto* x0 = layers.data("x0", {16, 16});
auto* y0 = layers.data("y0", {16, 32});
auto* tmp_0 = layers.mul(x0, y0);
auto* x1 = layers.data("x1", shape);
auto* tmp_1 = layers.sigmoid(x1);
auto* tmp_2 = layers.elementwise_mul(tmp_0, tmp_1);
auto* x2 = layers.data("x2", shape);
auto* tmp_3 = layers.sigmoid(x2);
auto* x3 = layers.data("x3", shape);
auto* tmp_4 = layers.tanh(x3);
auto* tmp_5 = layers.elementwise_mul(tmp_3, tmp_4);
auto* tmp_6 = layers.elementwise_add(tmp_2, tmp_5);
auto* x4 = layers.data("x4", shape);
auto* tmp_7 = layers.tanh(x4);
auto* x5 = layers.data("x5", shape);
auto* tmp_8 = layers.sigmoid(x5);
auto* tmp_9 = layers.elementwise_mul(tmp_7, tmp_8);
auto* tmp_10 = layers.mul(tmp_6, tmp_9);
std::vector<VarDesc*> elementwise_vars = {
tmp_0, tmp_1, tmp_2, tmp_3, tmp_4, tmp_5, tmp_6, tmp_7, tmp_8, tmp_9};
for (auto* var : elementwise_vars) {
var->SetShape(shape);
}
if (backward) {
layers.backward({tmp_10});
}
std::unique_ptr<Graph> graph(new Graph(layers.main_program()));
for (auto* n : graph->Nodes()) {
if (n && n->IsVar() && n->Var()) {
n->Var()->SetDataType(proto::VarType::FP32);
}
}
return graph;
}
int TestMain(std::unique_ptr<Graph> graph, std::string prefix) {
// VisualizeGraph(&graph, prefix + ".dot");
auto pass = PassRegistry::Instance().Get("fusion_group_pass");
pass->Set("use_gpu", new bool(true));
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
// VisualizeGraph(&graph, prefix + ".fusion_group.dot");
int num_fusion_group_ops = GetNumOpNodes(graph, "fusion_group");
VLOG(3) << DebugString(graph);
return num_fusion_group_ops;
}
TEST(FusionGroupPass, elementwise_list) {
std::unique_ptr<Graph> graph = BuildElementwiseListGraph(true);
int num_fusion_group_ops = TestMain(std::move(graph), "elementwise_list");
EXPECT_EQ(num_fusion_group_ops, 2);
}
TEST(FusionGroupPass, elementwise_tree) {
std::unique_ptr<Graph> graph = BuildElementwiseTreeGraph(true);
int num_fusion_group_ops = TestMain(std::move(graph), "elementwise_tree");
EXPECT_EQ(num_fusion_group_ops, 4);
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(fusion_group_pass);
USE_PASS(graph_viz_pass);
@@ -0,0 +1,227 @@
// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/ir/generate_pass.h"
#include "gtest/gtest.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
REGISTER_GENERATE_PASS(generate_fc_fuse) {
paddle::framework::ir::PassPairs pass_pairs;
for (bool with_relu : {true, false}) {
// pattern
SUBGRAPH_(pattern) = [subgraph = &pattern, with_relu](
VAR_(x), VAR_(y), VAR_(z)) {
VLOG(3) << "exec lambda func.";
auto mul = OP_(mul)({{"X", x}, {"Y", y}}).Out("Out");
auto ewadd = OP_(elementwise_add)({{"X", mul}, {"Y", z}}).Out("Out");
if (with_relu) { // NOLINT
return OP_(relu)({"X", ewadd}).Out("Out");
} else {
return ewadd;
}
};
// replace
SUBGRAPH_(replace) = [subgraph = &replace](VAR_(x), VAR_(y), VAR_(z)) {
auto& fc = OP_(fc)({{"Input", x}, {"W", y}, {"Bias", z}});
return fc.Out("Out");
};
pass_pairs.AddPassDesc(pattern, replace);
}
return pass_pairs;
}
REGISTER_GENERATE_PASS(generate_multi_add_to_addn) {
// pattern
SUBGRAPH_(pattern) = [subgraph = &pattern](VAR_(x), VAR_(y), VAR_(z)) {
auto ewadd1 = OP_(elementwise_add)({{"X", x}, {"Y", y}}).Out("Out");
auto ewadd2 = OP_(elementwise_add)({{"X", ewadd1}, {"Y", z}}).Out("Out");
return ewadd2;
};
// replace
SUBGRAPH_(replace) = [subgraph = &replace](VAR_(x), VAR_(y), VAR_(z)) {
return OP_(sum)({"X", {x, y, z}}).Out("Out");
};
return {pattern, replace};
}
REGISTER_GENERATE_PASS(generate_combine_matmul) {
// pattern
SUBGRAPH_(pattern) = [subgraph = &pattern](VAR_(x), VAR_(y), VAR_(z)) {
auto matmul1 = OP_(matmul)({{"X", x}, {"Y", y}}).Out("Out");
auto matmul2 = OP_(matmul)({{"X", x}, {"Y", z}}).Out("Out");
return std::make_tuple(matmul1, matmul2);
};
// replace
SUBGRAPH_(replace) = [subgraph = &replace](VAR_(x), VAR_(y), VAR_(z)) {
auto concat = OP_(concat)({"X", {y, z}}).Out("Out");
auto matmul = OP_(matmul)({{"X", x}, {"Y", concat}}).Out("Out");
auto slice1 = OP_(slice)({"X", matmul}).Out("Out");
auto slice2 = OP_(slice)({"X", matmul}).Out("Out");
return std::make_tuple(slice1, slice2);
};
return {pattern, replace};
}
namespace paddle {
namespace framework {
namespace ir {
TEST(GeneratePass, construct_with_string) {
std::string binary_str;
register_generate_fc_fuse().MultiPassDesc().SerializeToString(&binary_str);
GeneratePass generate_pass(binary_str);
}
TEST(GeneratePass, generate_fc_fuse) {
// inputs operator output
// --------------------------------------------------------
// (a, filters_0 bias_0) conv2d -> conv2d_out
// conv2d_out relu -> relu_out_0
// (relu_out_0, weights_0) mul -> mul_out_0
// (mul_out_0, bias_1) elementwise_add -> add_out_0
// add_out_0 relu -> relu_out_1
// (relu_out_1, weights_1) mul -> mul_out_1
// (mul_out_1, bias_2) elementwise_add -> add_out_1
Layers layers;
auto* a = layers.data("a");
auto* filters_0 = layers.data("conv2d_filters_0", {}, true);
auto* bias_0 = layers.data("conv2d_bias_0", {}, true);
auto* conv2d_out = layers.conv2d(a, filters_0, bias_0, false);
auto* relu_out_0 = layers.relu(conv2d_out);
auto* weights_0 = layers.data("weights_0", {}, true);
auto* mul_out_0 = layers.mul(relu_out_0, weights_0);
auto* bias_1 = layers.data("bias_1", {}, true);
auto* add_out_0 = layers.elementwise_add(mul_out_0, bias_1, nullptr, 1);
auto* relu_out_1 = layers.relu(add_out_0);
auto* weights_1 = layers.data("weights_1", {}, true);
auto* mul_out_1 = layers.mul(relu_out_1, weights_1);
auto* bias_2 = layers.data("bias_2", {}, true);
auto* add_out_1 = layers.elementwise_add(mul_out_1, bias_2, nullptr, 1);
VLOG(4) << add_out_1;
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("generate_fc_fuse");
int num_nodes_before = static_cast<int>(graph->Nodes().size());
int num_mul_nodes_before = GetNumOpNodes(graph, "mul");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fc_nodes_after = GetNumOpNodes(graph, "fc");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 6,
common::errors::InvalidArgument(
"num_nodes_before=%d, num_nodes_after=%d.",
num_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fc_nodes_after,
2,
common::errors::InvalidArgument("num_fc_nodes_after=%d.",
num_fc_nodes_after));
PADDLE_ENFORCE_EQ(num_mul_nodes_before,
num_fc_nodes_after,
common::errors::InvalidArgument(
"num_mul_nodes_before=%d, num_fc_nodes_after=%d.",
num_mul_nodes_before,
num_fc_nodes_after));
}
TEST(GeneratePass, generate_multi_add_to_addn) {
// inputs operator output
// --------------------------------------------------------
// (a, b) elementwise_add -> add_out_0
// (add_out_0, c) elementwise_add -> add_out_1
Layers layers;
auto* a = layers.data("a");
auto* b = layers.data("b");
auto* c = layers.data("c");
auto* add_out_0 = layers.elementwise_add(a, b);
layers.elementwise_add(add_out_0, c);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("generate_multi_add_to_addn");
int num_nodes_before = static_cast<int>(graph->Nodes().size());
int num_add_nodes_before = GetNumOpNodes(graph, "elementwise_add");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_addn_nodes_after = GetNumOpNodes(graph, "sum");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 2,
common::errors::InvalidArgument(
"num_nodes_before=%d, num_nodes_after=%d.",
num_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_addn_nodes_after,
1,
common::errors::InvalidArgument("num_addn_nodes_after=%d.",
num_addn_nodes_after));
PADDLE_ENFORCE_EQ(num_add_nodes_before,
num_addn_nodes_after + 1,
common::errors::InvalidArgument(
"num_add_nodes_before=%d, num_addn_nodes_after=%d.",
num_add_nodes_before,
num_addn_nodes_after));
}
TEST(GeneratePass, generate_combine_matmul) {
// inputs operator output
// --------------------------------------------------------
// (a, b) matmul -> matmul_out_0
// (a, c) matmul -> matmul_out_1
Layers layers;
auto* a = layers.data("a");
auto* b = layers.data("b");
auto* c = layers.data("c");
layers.matmul(a, b);
layers.matmul(a, c);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("generate_combine_matmul");
int num_nodes_before = static_cast<int>(graph->Nodes().size());
int num_matmul_nodes_before = GetNumOpNodes(graph, "matmul");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_matmul_nodes_after = GetNumOpNodes(graph, "matmul");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after - 4,
common::errors::InvalidArgument(
"num_nodes_before=%d, num_nodes_after=%d.",
num_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_matmul_nodes_after,
1,
common::errors::InvalidArgument(
"num_matmul_nodes_after=%d.", num_matmul_nodes_after));
PADDLE_ENFORCE_EQ(
num_matmul_nodes_before,
num_matmul_nodes_after + 1,
common::errors::InvalidArgument(
"num_matmul_nodes_before=%d, num_matmul_nodes_after=%d.",
num_matmul_nodes_before,
num_matmul_nodes_after));
}
} // namespace ir
} // namespace framework
} // namespace paddle
@@ -0,0 +1,223 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/ir/graph_helper.h"
#include "gtest/gtest.h"
#include "paddle/fluid/framework/ir/graph.h"
#include "paddle/fluid/framework/program_desc.h"
namespace paddle::framework::ir {
void BuildCircleGraph(Graph* g) {
ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation);
ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable);
o1->outputs.push_back(v1);
o1->inputs.push_back(v1);
v1->inputs.push_back(o1);
v1->outputs.push_back(o1);
}
void BuildCircleGraph2(Graph* g) {
ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation);
ir::Node* o2 = g->CreateEmptyNode("op2", Node::Type::kOperation);
ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable);
ir::Node* v2 = g->CreateEmptyNode("var2", Node::Type::kVariable);
o1->outputs.push_back(v1);
o2->inputs.push_back(v1);
v1->inputs.push_back(o1);
v1->outputs.push_back(o2);
o2->outputs.push_back(v2);
o1->inputs.push_back(v2);
v2->inputs.push_back(o2);
v2->outputs.push_back(o1);
}
void BuildNoCircleGraph(Graph* g) {
ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation);
ir::Node* o2 = g->CreateEmptyNode("op2", Node::Type::kOperation);
ir::Node* o3 = g->CreateEmptyNode("op3", Node::Type::kOperation);
ir::Node* o4 = g->CreateEmptyNode("op4", Node::Type::kOperation);
ir::Node* o5 = g->CreateEmptyNode("op5", Node::Type::kOperation);
ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable);
ir::Node* v2 = g->CreateEmptyNode("var2", Node::Type::kVariable);
ir::Node* v3 = g->CreateEmptyNode("var3", Node::Type::kVariable);
ir::Node* v4 = g->CreateEmptyNode("var4", Node::Type::kVariable);
// o1->v1->o2
o1->outputs.push_back(v1);
o2->inputs.push_back(v1);
v1->inputs.push_back(o1);
v1->outputs.push_back(o2);
// o2->v2->o3
// o2->v2->o4
o2->outputs.push_back(v2);
o3->inputs.push_back(v2);
o4->inputs.push_back(v2);
v2->inputs.push_back(o2);
v2->outputs.push_back(o3);
v2->outputs.push_back(o4);
// o2->v3->o5
o2->outputs.push_back(v3);
o5->inputs.push_back(v3);
v3->inputs.push_back(o2);
v3->outputs.push_back(o5);
// o3-v4->o5
o3->outputs.push_back(v4);
o5->inputs.push_back(v4);
v4->inputs.push_back(o3);
v4->outputs.push_back(o5);
}
TEST(GraphHelperTest, Basic) {
ProgramDesc prog;
Graph g(prog);
BuildCircleGraph(&g);
ASSERT_TRUE(HasCircle(g));
Graph g2(prog);
BuildCircleGraph2(&g2);
ASSERT_TRUE(HasCircle(g2));
auto adj_list = BuildOperationAdjList(g2);
for (auto& adj : adj_list) {
auto& adj_set = adj.second;
if (adj.first->Name() == "op1") {
ASSERT_EQ((*adj_set.begin())->Name(), "op2");
} else if (adj.first->Name() == "op2") {
ASSERT_EQ((*adj_set.begin())->Name(), "op1");
} else {
ASSERT_TRUE(false);
}
}
Graph g3(prog);
BuildNoCircleGraph(&g3);
ASSERT_FALSE(HasCircle(g3));
auto sorted = TopologySortOperations(g3);
std::map<std::string, size_t> node_map;
for (size_t i = 0; i < sorted.size(); ++i) {
node_map[sorted[i]->Name()] = i;
}
ASSERT_EQ(node_map.at("op1"), 0UL);
ASSERT_EQ(node_map.at("op2"), 1UL);
ASSERT_TRUE(node_map.at("op3") < node_map.at("op5"));
}
void BuildZeroGraph(Graph* g) {}
void BuildOneGraph(Graph* g) {
ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation);
ir::Node* o2 = g->CreateEmptyNode("op2", Node::Type::kOperation);
ir::Node* o3 = g->CreateEmptyNode("op3", Node::Type::kOperation);
ir::Node* o4 = g->CreateEmptyNode("op4", Node::Type::kOperation);
ir::Node* o5 = g->CreateEmptyNode("op5", Node::Type::kOperation);
ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable);
ir::Node* v2 = g->CreateEmptyNode("var2", Node::Type::kVariable);
ir::Node* v3 = g->CreateEmptyNode("var3", Node::Type::kVariable);
ir::Node* v4 = g->CreateEmptyNode("var4", Node::Type::kVariable);
// o1->v1->o2
o1->outputs.push_back(v1);
o2->inputs.push_back(v1);
v1->inputs.push_back(o1);
v1->outputs.push_back(o2);
// o2->v2->o3
// o2->v2->o4
o2->outputs.push_back(v2);
o3->inputs.push_back(v2);
o4->inputs.push_back(v2);
v2->inputs.push_back(o2);
v2->outputs.push_back(o3);
v2->outputs.push_back(o4);
// o2->v3->o5
o2->outputs.push_back(v3);
o5->inputs.push_back(v3);
v3->inputs.push_back(o2);
v3->outputs.push_back(o5);
// o3-v4->o5
o3->outputs.push_back(v4);
o5->inputs.push_back(v4);
v4->inputs.push_back(o3);
v4->outputs.push_back(o5);
}
void BuildTwoGraphs(Graph* g) {
ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation);
ir::Node* o2 = g->CreateEmptyNode("op2", Node::Type::kOperation);
ir::Node* o3 = g->CreateEmptyNode("op3", Node::Type::kOperation);
ir::Node* o4 = g->CreateEmptyNode("op4", Node::Type::kOperation);
ir::Node* o5 = g->CreateEmptyNode("op5", Node::Type::kOperation);
ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable);
ir::Node* v2 = g->CreateEmptyNode("var2", Node::Type::kVariable);
ir::Node* v3 = g->CreateEmptyNode("var3", Node::Type::kVariable);
ir::Node* v4 = g->CreateEmptyNode("var4", Node::Type::kVariable);
// o1->v1->o2
o1->outputs.push_back(v1);
o2->inputs.push_back(v1);
v1->inputs.push_back(o1);
v1->outputs.push_back(o2);
// o2->v2->o3
// o2->v2->o4
o2->outputs.push_back(v2);
o3->inputs.push_back(v2);
o4->inputs.push_back(v2);
v2->inputs.push_back(o2);
v2->outputs.push_back(o3);
v2->outputs.push_back(o4);
// o2->v3->o5
// o2->outputs.push_back(v3);
o5->inputs.push_back(v3);
// v3->inputs.push_back(o2);
v3->outputs.push_back(o5);
// o3-v4->o5
o3->outputs.push_back(v4);
// o5->inputs.push_back(v4);
v4->inputs.push_back(o3);
// v4->outputs.push_back(o5);
}
TEST(GraphHelperTest, Circles) {
ProgramDesc prog;
Graph g(prog);
BuildCircleGraph(&g);
std::vector<std::vector<ir::Node*>> circles;
ASSERT_TRUE(FindCircleSubGraph(g, &circles));
ASSERT_EQ(circles.size(), 1UL);
}
TEST(GraphHelperTest, GraphNum) {
ProgramDesc prog;
Graph g(prog);
BuildZeroGraph(&g);
ASSERT_EQ(GraphNum(g), 0UL);
Graph g2(prog);
BuildOneGraph(&g2);
ASSERT_EQ(GraphNum(g2), 1UL);
Graph g3(prog);
BuildTwoGraphs(&g3);
ASSERT_EQ(GraphNum(g3), 2UL);
}
} // namespace paddle::framework::ir
@@ -0,0 +1,208 @@
// Copyright (c) 2018 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/fluid/framework/ir/graph_pattern_detector.h"
namespace paddle::framework::ir {
class Node;
void BuildGraph(Graph* g) {
ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation);
ir::Node* o2 = g->CreateEmptyNode("op2", Node::Type::kOperation);
ir::Node* o3 = g->CreateEmptyNode("op3", Node::Type::kOperation);
ir::Node* o4 = g->CreateEmptyNode("op4", Node::Type::kOperation);
ir::Node* o5 = g->CreateEmptyNode("op5", Node::Type::kOperation);
ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable);
ir::Node* v2 = g->CreateEmptyNode("var2", Node::Type::kVariable);
ir::Node* v3 = g->CreateEmptyNode("var3", Node::Type::kVariable);
ir::Node* v4 = g->CreateEmptyNode("var4", Node::Type::kVariable);
// o1->v1->o2
o1->outputs.push_back(v1);
o2->inputs.push_back(v1);
v1->inputs.push_back(o1);
v1->outputs.push_back(o2);
// o2->v2->o3
// o2->v2->o4
o2->outputs.push_back(v2);
o3->inputs.push_back(v2);
o4->inputs.push_back(v2);
v2->inputs.push_back(o2);
v2->outputs.push_back(o3);
v2->outputs.push_back(o4);
// o2->v3->o5
o2->outputs.push_back(v3);
o5->inputs.push_back(v3);
v3->inputs.push_back(o2);
v3->outputs.push_back(o5);
// o3-v4->o5
o3->outputs.push_back(v4);
o5->inputs.push_back(v4);
v4->inputs.push_back(o3);
v4->outputs.push_back(o5);
}
TEST(PDPattern, NewNode) {
PDPattern x;
auto* n = x.NewNode([](Node* x) { return true; });
ASSERT_TRUE(n);
ASSERT_EQ(x.nodes_.size(), 1UL);
}
TEST(PDPattern, AddEdge) {
PDPattern x;
auto* a = x.NewNode([](Node* x) { return true; });
auto* b = x.NewNode([](Node* x) { return true; });
ASSERT_TRUE(a);
ASSERT_TRUE(b);
x.AddEdge(a, b);
ASSERT_EQ(x.nodes_.size(), 2UL);
ASSERT_EQ(x.edges_.size(), 1UL);
ASSERT_EQ(x.edges_.front().first, a);
ASSERT_EQ(x.edges_.front().second, b);
ASSERT_EQ(x.nodes().size(), 2UL);
ASSERT_EQ(x.edges().size(), 1UL);
ASSERT_EQ(x.edges().front().first, a);
ASSERT_EQ(x.edges().front().second, b);
}
TEST(GraphPatternDetector, MarkPDNodesInGraph) {
GraphPatternDetector x;
// mark o2, o3, v2
// The pattern is a graph:
// o2(a node named o2) -> v2(a node named v2)
// v2 -> o3(a node named o3)
auto* o2 = x.pattern_.NewNode([](Node* node) {
// The teller can be any condition, such as op type, or variable's shape.
return node && node->Name() == "op2" && node->IsOp();
});
auto* o3 = x.pattern_.NewNode([](Node* node) {
// The teller can be any condition, such as op type, or variable's shape.
return node && node->Name() == "op3" && node->IsOp();
});
auto* v2 = x.pattern_.NewNode([](Node* node) {
// The teller can be any condition, such as op type, or variable's shape.
return node && node->Name() == "var2" && node->IsVar();
});
ASSERT_FALSE(o2->Tell(nullptr));
ASSERT_FALSE(o3->Tell(nullptr));
ASSERT_FALSE(v2->Tell(nullptr));
x.pattern_.AddEdge(o2, v2);
x.pattern_.AddEdge(v2, o3);
ASSERT_EQ(x.pattern_.edges().size(), 2UL);
ASSERT_EQ(x.pattern_.edges()[0].first, o2);
ASSERT_EQ(x.pattern_.edges()[0].second, v2);
ASSERT_EQ(x.pattern_.edges()[1].first, v2);
ASSERT_EQ(x.pattern_.edges()[1].second, o3);
ProgramDesc program;
Graph graph(program);
BuildGraph(&graph);
x.MarkPDNodesInGraph(graph);
ASSERT_EQ(x.pdnodes2nodes_.size(), 3UL);
auto subgraphs = x.DetectPatterns();
ASSERT_EQ(subgraphs.size(), 1UL);
}
TEST(GraphPatternDetector, MultiSubgraph) {
ProgramDesc program;
Graph graph(program);
BuildGraph(&graph);
GraphPatternDetector x;
// The pattern is a graph:
// op -> var
auto* any_op = x.mutable_pattern()->NewNode(
[](Node* node) {
return node->IsOp() && (node->Name() == "op2" || node->Name() == "op3");
},
"OP0");
auto* any_var = x.mutable_pattern()
->NewNode([](Node* node) { return node->IsVar(); }, "VAR")
->AsIntermediate();
auto* any_op1 = x.mutable_pattern()->NewNode(
[](Node* node) { return node->IsOp(); }, "OP1");
x.mutable_pattern()->AddEdge(any_op, any_var);
x.mutable_pattern()->AddEdge(any_var, any_op1);
int count = 0;
GraphPatternDetector::handle_t handle =
[&](const GraphPatternDetector::subgraph_t& s, Graph* g) {
LOG(INFO) << "Detect " << s.at(any_op)->Name() << " -> "
<< s.at(any_var)->Name() << " -> " << s.at(any_op1)->Name();
count++;
};
x(&graph, handle);
// 1. Detect op3 -> var4 -> op5
// 2. Detect op2 -> var2 -> op3
// 3. Detect op2 -> var2 -> op4
// 4. Detect op2 -> var3 -> op5
// But 2 and 3 and 4 overlapped, so keep 2, so the final choices are 1 and 2
ASSERT_GE(count, 1);
ASSERT_LE(count, 2);
}
TEST(GraphPatternDetector, IntermediateCheck) {
ProgramDesc program;
Graph graph(program);
BuildGraph(&graph);
// o2->v2->o3
// o2->v2->o4
// check o2+o3 fuse, should fail because v2 also link to o4.
GraphPatternDetector detector;
auto* op2 = detector.mutable_pattern()->NewNode(
[](Node* x) { return x && x->IsOp() && x->Name() == "op2"; }, "op2");
auto* op3 = detector.mutable_pattern()->NewNode(
[](Node* x) { return x && x->IsOp() && x->Name() == "op3"; }, "op3");
auto* v2 =
detector.mutable_pattern()
->NewNode(
[](Node* x) { return x && x->IsVar() && x->Name() == "var2"; },
"var2")
->AsIntermediate();
v2->LinksFrom({op2}).LinksTo({op3});
int count = 0;
detector(&graph,
[&](const GraphPatternDetector::subgraph_t& g, Graph* graph) {
++count;
});
EXPECT_EQ(count, 0);
count = 0;
v2->AsInput();
detector(&graph,
[&](const GraphPatternDetector::subgraph_t& g, Graph* graph) {
++count;
});
ASSERT_EQ(count, 1);
}
} // namespace paddle::framework::ir
+337
View File
@@ -0,0 +1,337 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/ir/graph.h"
#include "gtest/gtest.h"
#include "paddle/fluid/framework/details/multi_devices_helper.h"
#include "paddle/fluid/framework/op_registry.h"
#include "paddle/fluid/framework/operator.h"
#include "paddle/fluid/framework/program_desc.h"
namespace paddle::framework {
class NOP : public OperatorBase {
public:
NOP(const std::string &type,
const VariableNameMap &inputs,
const VariableNameMap &outputs,
const AttributeMap &attrs)
: OperatorBase(type, inputs, outputs, attrs) {}
private:
void RunImpl(const Scope &scope, const phi::Place &place) const override {}
};
class SumOpMaker : public OpProtoAndCheckerMaker {
public:
void Make() override {
AddInput("X", "").AsDuplicable();
AddOutput("Out", "").AsDuplicable();
AddComment("");
}
};
class SumOpVarTypeInference : public VarTypeInference {
public:
void operator()(InferVarTypeContext *ctx) const override {
auto default_var_type = proto::VarType::SELECTED_ROWS;
if (ctx->InputTypeAnyOf("X", proto::VarType::DENSE_TENSOR)) {
default_var_type = proto::VarType::DENSE_TENSOR;
}
ctx->SetOutputType("Out", default_var_type);
}
};
class DummyOpMaker : public OpProtoAndCheckerMaker {
public:
void Make() override {
AddInput("X", "").AsDuplicable();
AddOutput("Out", "").AsDuplicable();
AddComment("");
}
};
class DummyOpVarTypeInference : public VarTypeInference {
public:
void operator()(framework::InferVarTypeContext *ctx) const override {}
};
} // namespace paddle::framework
REGISTER_OPERATOR(fake_sum,
paddle::framework::NOP,
paddle::framework::SumOpMaker,
paddle::framework::SumOpVarTypeInference);
REGISTER_OPERATOR(dummy,
paddle::framework::NOP,
paddle::framework::SumOpMaker,
paddle::framework::SumOpVarTypeInference);
REGISTER_OPERATOR(sum_without_infer_var_type,
paddle::framework::NOP,
paddle::framework::SumOpMaker);
namespace paddle::framework {
TEST(GraphTest, Basic) {
ProgramDesc prog;
auto *op = prog.MutableBlock(0)->AppendOp();
op->SetType("fake_sum");
op->SetInput("X", {"test_a", "test_b", "test_c"});
op->SetOutput("Out", {"test_out"});
op->SetAttr("op_role", 1);
prog.MutableBlock(0)->Var("test_a")->SetType(proto::VarType::SELECTED_ROWS);
prog.MutableBlock(0)->Var("test_b")->SetType(proto::VarType::SELECTED_ROWS);
prog.MutableBlock(0)->Var("test_c")->SetType(proto::VarType::SELECTED_ROWS);
prog.MutableBlock(0)->Var("test_out");
op->InferVarType(prog.MutableBlock(0));
ASSERT_EQ(proto::VarType::SELECTED_ROWS,
prog.MutableBlock(0)->Var("test_out")->GetType());
prog.MutableBlock(0)->Var("test_b")->SetType(proto::VarType::DENSE_TENSOR);
op->InferVarType(prog.MutableBlock(0));
ASSERT_EQ(proto::VarType::DENSE_TENSOR,
prog.MutableBlock(0)->Var("test_out")->GetType());
std::unique_ptr<ir::Graph> g(new ir::Graph(prog));
std::vector<ir::Node *> nodes(g->Nodes().begin(), g->Nodes().end());
for (ir::Node *n : nodes) {
if (n->Name() == "fake_sum") {
ASSERT_EQ(n->inputs.size(), 3UL);
ASSERT_EQ(n->outputs.size(), 1UL);
} else if (n->Name() == "test_a" || n->Name() == "test_b" ||
n->Name() == "test_c") {
ASSERT_EQ(n->inputs.size(), 0UL);
ASSERT_EQ(n->outputs.size(), 1UL);
} else if (n->Name() == "test_out") {
ASSERT_EQ(n->inputs.size(), 1UL);
ASSERT_EQ(n->outputs.size(), 0UL);
}
}
ASSERT_EQ(nodes.size(), 5UL);
}
TEST(GraphTest, TestException) {
ProgramDesc prog;
std::unique_ptr<ir::Graph> g(new ir::Graph(prog));
bool not_met_exception = false;
try {
g->Erase("no_attr");
} catch (const platform::EnforceNotMet &e) {
not_met_exception = true;
}
ASSERT_TRUE(not_met_exception);
not_met_exception = false;
try {
g->CreateVarNode(nullptr);
} catch (const platform::EnforceNotMet &e) {
not_met_exception = true;
}
ASSERT_TRUE(not_met_exception);
not_met_exception = false;
try {
g->CreateOpNode(nullptr);
} catch (const platform::EnforceNotMet &e) {
not_met_exception = true;
}
ASSERT_TRUE(not_met_exception);
not_met_exception = false;
try {
g->RemoveNode(nullptr);
} catch (const platform::EnforceNotMet &e) {
not_met_exception = true;
}
ASSERT_TRUE(not_met_exception);
not_met_exception = false;
try {
g->AddNode(nullptr);
g->AddNode(nullptr);
} catch (const platform::EnforceNotMet &e) {
not_met_exception = true;
}
ASSERT_TRUE(not_met_exception);
}
TEST(GraphTest, TestInterfaceConvertAllBlocks) {
// Set FLAGS_convert_all_blocks to true to make sure this test works.
bool flag_temp = FLAGS_convert_all_blocks;
FLAGS_convert_all_blocks = true;
ProgramDesc prog;
prog.MutableBlock(0)->Var("init_var")->SetType(proto::VarType::SELECTED_ROWS);
ir::Graph g(prog);
ASSERT_TRUE(g.IsMainGraph());
const std::string kIntValue = "int_value";
const int INT_VALUE = 3;
g.Set<int>(kIntValue, new int(INT_VALUE));
ASSERT_TRUE(g.Has(kIntValue));
ASSERT_EQ(g.GetOrInit<int>(kIntValue), INT_VALUE);
ASSERT_EQ(g.Get<int>(kIntValue), INT_VALUE);
g.Erase(kIntValue);
ASSERT_TRUE(!g.Has(kIntValue));
g.SetNotOwned<int>(kIntValue, new int(INT_VALUE));
ASSERT_TRUE(g.Has(kIntValue));
g.Erase(kIntValue);
g.ReleaseNodes();
ASSERT_EQ(g.Nodes().size(), 0UL);
g.CreateVarNode(new VarDesc("temp_var_desc_name"));
g.CreateOpNode(prog.MutableBlock(0)->AppendOp());
g.CreateControlDepVar();
g.CreateEmptyNode("temp_empty_node_name", ir::Node::Type::kVariable);
ASSERT_EQ(g.Nodes().size(), 4UL);
g.RemoveNode(g.RetrieveNode(1));
ASSERT_EQ(g.Nodes().size(), 3UL);
// Recover FLAGS_convert_all_blocks.
FLAGS_convert_all_blocks = flag_temp;
}
TEST(GraphTest, TestMultiBlock) {
// Set FLAGS_convert_all_blocks to true to make sure this test works.
bool flag_temp = FLAGS_convert_all_blocks;
FLAGS_convert_all_blocks = true;
// Step1: Build a program with 3 blocks.
ProgramDesc prog;
ASSERT_EQ(prog.Size(), 1UL);
prog.AppendBlock(prog.Block(0));
prog.AppendBlock(prog.Block(0));
ASSERT_EQ(prog.Size(), 3UL);
// Set contents in block_0.
auto *op = prog.MutableBlock(0)->AppendOp();
op->SetType("fake_sum");
op->SetInput("X", {"test_a", "test_b", "test_c"});
op->SetOutput("Out", {"test_out"});
op->SetAttr("op_role", 1);
prog.MutableBlock(0)->Var("test_a")->SetType(proto::VarType::SELECTED_ROWS);
prog.MutableBlock(0)->Var("test_b")->SetType(proto::VarType::SELECTED_ROWS);
prog.MutableBlock(0)->Var("test_c")->SetType(proto::VarType::SELECTED_ROWS);
prog.MutableBlock(0)->Var("test_out");
op->InferVarType(prog.MutableBlock(0));
ASSERT_EQ(proto::VarType::SELECTED_ROWS,
prog.MutableBlock(0)->Var("test_out")->GetType());
prog.MutableBlock(0)->Var("test_b")->SetType(proto::VarType::DENSE_TENSOR);
op->InferVarType(prog.MutableBlock(0));
ASSERT_EQ(proto::VarType::DENSE_TENSOR,
prog.MutableBlock(0)->Var("test_out")->GetType());
// Set contents in block_1.
op = prog.MutableBlock(1)->AppendOp();
op->SetType("fake_sum");
op->SetInput("X", {"a"});
op->SetOutput("Out", {"b"});
op->SetAttr("op_role", 1);
op = prog.MutableBlock(1)->AppendOp();
op->SetType("dummy");
op->SetInput("X", {"c"});
op->SetOutput("Out", {"d"});
op->SetAttr("op_role", 1);
prog.MutableBlock(1)->Var("a")->SetType(proto::VarType::DENSE_TENSOR);
prog.MutableBlock(1)->Var("b")->SetType(proto::VarType::DENSE_TENSOR);
prog.MutableBlock(1)->Var("c")->SetType(proto::VarType::DENSE_TENSOR);
prog.MutableBlock(1)->Var("d")->SetType(proto::VarType::DENSE_TENSOR);
// Set contents in block_2.
op = prog.MutableBlock(2)->AppendOp();
op->SetType("fake_sum");
op->SetInput("X", {"a"});
op->SetOutput("Out", {"b"});
op->SetAttr("op_role", 1);
op = prog.MutableBlock(2)->AppendOp();
op->SetType("dummy");
op->SetInput("X", {"c"});
op->SetOutput("Out", {"d"});
op->SetAttr("op_role", 1);
prog.MutableBlock(2)->Var("a")->SetType(proto::VarType::DENSE_TENSOR);
prog.MutableBlock(2)->Var("b")->SetType(proto::VarType::DENSE_TENSOR);
prog.MutableBlock(2)->Var("c")->SetType(proto::VarType::DENSE_TENSOR);
prog.MutableBlock(1)->Var("d")->SetType(proto::VarType::DENSE_TENSOR);
// Step2: Convert program into graph, 3 blocks corresponding 3 sub_graphs.
std::unique_ptr<ir::Graph> g(new ir::Graph(prog));
ASSERT_EQ(g->IsMainGraph(), true);
ASSERT_EQ(g->SubGraphsSize(), 3UL);
// Check contents in sub_graph_0.
const ir::Graph *g0 = g->GetSubGraph(0);
std::vector<ir::Node *> nodes(g0->Nodes().begin(), g0->Nodes().end());
for (ir::Node *n : nodes) {
if (n->Name() == "fake_sum") {
ASSERT_EQ(n->inputs.size(), 3UL);
ASSERT_EQ(n->outputs.size(), 1UL);
} else if (n->Name() == "test_a" || n->Name() == "test_b" ||
n->Name() == "test_c") {
ASSERT_EQ(n->inputs.size(), 0UL);
ASSERT_EQ(n->outputs.size(), 1UL);
} else if (n->Name() == "test_out") {
ASSERT_EQ(n->inputs.size(), 1UL);
ASSERT_EQ(n->outputs.size(), 0UL);
}
}
ASSERT_EQ(nodes.size(), 5UL);
// Check contents in sub_graph_1.
const ir::Graph *g1 = g->GetSubGraph(1);
for (ir::Node *n : g1->Nodes()) {
if (n->Name() == "fake_sum") {
ASSERT_EQ(n->outputs[0]->Name(), "b");
ASSERT_EQ(n->outputs.size(), 1UL);
}
if (n->Name() == "dummy") {
ASSERT_EQ(n->inputs[0]->Name(), "c");
ASSERT_EQ(n->inputs.size(), 1UL);
}
}
// Check contents in sub_graph_2.
const ir::Graph *g2 = g->GetSubGraph(2);
for (ir::Node *n : g2->Nodes()) {
if (n->Name() == "fake_sum") {
ASSERT_EQ(n->outputs[0]->Name(), "b");
ASSERT_EQ(n->outputs.size(), 1UL);
}
if (n->Name() == "dummy") {
ASSERT_EQ(n->inputs[0]->Name(), "c");
ASSERT_EQ(n->inputs.size(), 1UL);
}
}
// Step3: Clone graph.
std::shared_ptr<ir::Graph> clone_g = g->Clone();
ASSERT_EQ(clone_g->IsMainGraph(), true);
ASSERT_EQ(clone_g->SubGraphsSize(), 3UL);
// Recover FLAGS_convert_all_blocks.
FLAGS_convert_all_blocks = flag_temp;
}
} // namespace paddle::framework
@@ -0,0 +1,417 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/ir/graph_to_program_pass.h"
#include <algorithm>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/details/build_strategy.h"
#include "paddle/fluid/framework/ir/graph.h"
#include "paddle/fluid/framework/op_desc.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/framework/var_desc.h"
namespace paddle {
namespace framework {
namespace ir {
class Node;
void BuildNoCircleGraph(Graph* g) {
OpDesc op1;
op1.SetType("op1");
OpDesc op2;
op2.SetType("op2");
OpDesc op3;
op3.SetType("op3");
OpDesc op4;
op4.SetType("op4");
OpDesc op5;
op5.SetType("op5");
VarDesc var1("var1");
VarDesc var2("var2");
VarDesc var3("var3");
VarDesc var4("var4");
ir::Node* o1 = g->CreateOpNode(&op1);
ir::Node* o2 = g->CreateOpNode(&op2);
ir::Node* o3 = g->CreateOpNode(&op3);
ir::Node* o4 = g->CreateOpNode(&op4);
ir::Node* o5 = g->CreateOpNode(&op5);
ir::Node* v1 = g->CreateVarNode(&var1);
ir::Node* v2 = g->CreateVarNode(&var2);
ir::Node* v3 = g->CreateVarNode(&var3);
ir::Node* v4 = g->CreateVarNode(&var4);
// o1->v1->o2
o1->outputs.push_back(v1);
o2->inputs.push_back(v1);
v1->inputs.push_back(o1);
v1->outputs.push_back(o2);
// o2->v2->o3
// o2->v2->o4
o2->outputs.push_back(v2);
o3->inputs.push_back(v2);
o4->inputs.push_back(v2);
v2->outputs.push_back(o3);
v2->outputs.push_back(o4);
v2->inputs.push_back(o2);
// o4->v3->o5
o4->outputs.push_back(v3);
o5->inputs.push_back(v3);
v3->inputs.push_back(o4);
v3->outputs.push_back(o5);
// o3-v4->o5
o3->outputs.push_back(v4);
o5->inputs.push_back(v4);
v4->inputs.push_back(o3);
v4->outputs.push_back(o5);
}
TEST(GraphToProgramPass, Basic) {
ProgramDesc prog;
std::unique_ptr<Graph> g(new Graph(prog));
BuildNoCircleGraph(g.get());
auto pass = paddle::framework::ir::PassRegistry::Instance().Get(
"graph_to_program_pass");
ProgramDesc compiled_prog;
pass->SetNotOwned<paddle::framework::ProgramDesc>("program", &compiled_prog);
pass->Apply(g.get());
std::vector<OpDesc*> ops = compiled_prog.Block(0).AllOps();
EXPECT_EQ(ops[0]->Type(), "op1");
EXPECT_EQ(ops[1]->Type(), "op2");
if (ops[2]->Type() == "op3") {
EXPECT_EQ(ops[3]->Type(), "op4");
} else if (ops[2]->Type() == "op4") {
EXPECT_EQ(ops[3]->Type(), "op3");
}
EXPECT_EQ(ops[4]->Type(), "op5");
std::unordered_set<std::string> vars;
for (VarDesc* v : compiled_prog.Block(0).AllVars()) {
vars.insert(v->Name());
}
EXPECT_TRUE(vars.find("var1") != vars.end());
EXPECT_TRUE(vars.find("var2") != vars.end());
EXPECT_TRUE(vars.find("var3") != vars.end());
}
void BuildProgramWithMultiBlock(ProgramDesc* program) {
auto* global_block = program->MutableBlock(0);
auto* mul_1_x = global_block->Var("Mul_1_X");
mul_1_x->SetType(proto::VarType::DENSE_TENSOR);
mul_1_x->SetLoDLevel(0);
mul_1_x->SetDataType(proto::VarType::FP32);
mul_1_x->SetShape({1000, 784});
auto* mul_1_y = global_block->Var("Mul_1_Y");
mul_1_y->SetType(proto::VarType::DENSE_TENSOR);
mul_1_y->SetLoDLevel(0);
mul_1_y->SetDataType(proto::VarType::FP32);
mul_1_y->SetShape({784, 100});
auto* mul_1_out = global_block->Var("Mul_1_Out");
mul_1_out->SetType(proto::VarType::DENSE_TENSOR);
auto* mul_op_1 = global_block->AppendOp();
mul_op_1->SetType("mul");
mul_op_1->SetInput("X", {mul_1_x->Name()});
mul_op_1->SetInput("Y", {mul_1_y->Name()});
mul_op_1->SetOutput("Y", {mul_1_out->Name()});
// building cond op such as less_than
auto* less_than_op_1 = global_block->AppendOp();
less_than_op_1->SetType("less_than");
auto* less_than_1_x = global_block->Var("Less_than_1_X");
less_than_1_x->SetType(proto::VarType::DENSE_TENSOR);
less_than_1_x->SetLoDLevel(0);
less_than_1_x->SetDataType(proto::VarType::FP32);
less_than_1_x->SetShape({1});
auto* less_than_1_y = global_block->Var("Less_than_1_Y");
less_than_1_y->SetType(proto::VarType::DENSE_TENSOR);
less_than_1_y->SetLoDLevel(0);
less_than_1_y->SetDataType(proto::VarType::FP32);
less_than_1_y->SetShape({1});
auto* less_than_1_out = global_block->Var("Less_than_1_Out");
less_than_1_out->SetType(proto::VarType::BOOL);
less_than_op_1->SetInput("X", {less_than_1_x->Name()});
less_than_op_1->SetInput("Y", {less_than_1_y->Name()});
less_than_op_1->SetOutput("Out", {less_than_1_out->Name()});
BlockDesc* sub_block = program->AppendBlock(*global_block);
std::vector<BlockDesc*> sub_blocks;
sub_blocks.push_back(sub_block);
BlockDesc* sub_block2 =
program->AppendBlock(*sub_block); // for testing nested case.
sub_blocks.push_back(sub_block2);
// building while op in sub_block
auto* while_op = global_block->AppendOp();
while_op->SetType("while");
while_op->SetAttr("sub_block", sub_blocks[0]);
auto* while_x = global_block->Var("While_X");
while_x->SetType(proto::VarType::DENSE_TENSOR);
while_x->SetLoDLevel(0);
while_x->SetDataType(proto::VarType::FP32);
while_x->SetShape({1});
while_op->SetInput("kX", {while_x->Name()});
while_op->SetInput("kCondition", {less_than_1_out->Name()});
auto* while_out = global_block->Var("While_Out");
while_out->SetType(proto::VarType::DENSE_TENSOR);
while_out->SetLoDLevel(0);
while_out->SetDataType(proto::VarType::FP32);
while_out->SetShape({1});
auto* steps = global_block->Var("StepScopes");
while_op->SetOutput("kOutputs", {while_out->Name()});
while_op->SetOutput("kStepScopes", {steps->Name()});
auto* mul_2_x = global_block->Var("Mul_2_X");
mul_2_x->SetType(proto::VarType::DENSE_TENSOR);
mul_2_x->SetLoDLevel(0);
mul_2_x->SetDataType(proto::VarType::FP32);
mul_2_x->SetShape({1000, 784});
auto* mul_2_y = global_block->Var("Mul_2_Y");
mul_2_y->SetType(proto::VarType::DENSE_TENSOR);
mul_2_y->SetLoDLevel(0);
mul_2_y->SetDataType(proto::VarType::FP32);
mul_2_y->SetShape({784, 100});
auto* mul_op_2 = sub_blocks[0]->AppendOp();
mul_op_2->SetType("mul");
mul_op_2->SetInput("X", {mul_2_x->Name()});
mul_op_2->SetInput("Y", {mul_2_y->Name()});
auto* mul_2_out = global_block->Var("Mul_2_Out");
mul_2_out->SetType(proto::VarType::DENSE_TENSOR);
mul_op_2->SetOutput("Y", {mul_2_out->Name()});
auto* less_than_op_2 = sub_blocks[0]->AppendOp();
less_than_op_2->SetType("less_than");
auto* less_than_2_x = global_block->Var("Less_than_2_X");
less_than_2_x->SetType(proto::VarType::DENSE_TENSOR);
less_than_2_x->SetLoDLevel(0);
less_than_2_x->SetDataType(proto::VarType::FP32);
less_than_2_x->SetShape({1});
auto* less_than_2_y = global_block->Var("Less_than_2_Y");
less_than_2_y->SetType(proto::VarType::DENSE_TENSOR);
less_than_2_y->SetLoDLevel(0);
less_than_2_y->SetDataType(proto::VarType::FP32);
less_than_2_y->SetShape({1});
less_than_op_2->SetInput("X", {less_than_2_x->Name()});
less_than_op_2->SetInput("Y", {less_than_2_y->Name()});
auto* less_than_2_out = global_block->Var("Less_than_2_Out");
less_than_2_out->SetType(proto::VarType::BOOL);
less_than_op_2->SetOutput("Out", {less_than_2_out->Name()});
auto* cond_op = sub_blocks[0]->AppendOp();
cond_op->SetType("conditional_block");
cond_op->SetAttr("sub_block", sub_blocks[1]);
auto* cond_x = sub_blocks[0]->Var("Cond_X");
cond_x->SetType(proto::VarType::DENSE_TENSOR);
cond_x->SetLoDLevel(0);
cond_x->SetDataType(proto::VarType::FP32);
cond_x->SetShape({1});
cond_op->SetInput("kInputs", {cond_x->Name()});
cond_op->SetInput("kCondition", {less_than_2_out->Name()});
auto* cond_out = sub_blocks[0]->Var("Cond_Out");
cond_out->SetType(proto::VarType::DENSE_TENSOR);
cond_out->SetLoDLevel(0);
cond_out->SetDataType(proto::VarType::FP32);
cond_out->SetShape({1});
auto* scope = sub_blocks[0]->Var("Scope");
scope->SetType(proto::VarType::STEP_SCOPES);
cond_op->SetOutput("kOutputs", {cond_out->Name()});
cond_op->SetOutput("kScope", {scope->Name()});
auto* mul_3_x = global_block->Var("Mul_3_X");
mul_3_x->SetType(proto::VarType::DENSE_TENSOR);
mul_3_x->SetLoDLevel(0);
mul_3_x->SetDataType(proto::VarType::FP32);
mul_3_x->SetShape({1000, 784});
auto* mul_3_y = global_block->Var("Mul_3_Y");
mul_3_y->SetType(proto::VarType::DENSE_TENSOR);
mul_3_y->SetLoDLevel(0);
mul_3_y->SetDataType(proto::VarType::FP32);
mul_3_y->SetShape({784, 100});
auto* mul_3_out = global_block->Var("Mul_3_Out");
mul_3_out->SetType(proto::VarType::DENSE_TENSOR);
auto* mul_op_3 = sub_blocks[1]->AppendOp();
mul_op_3->SetType("mul");
mul_op_3->SetInput("X", {mul_3_x->Name()});
mul_op_3->SetInput("Y", {mul_3_y->Name()});
mul_op_3->SetOutput("Y", {mul_3_out->Name()});
}
bool VarComparator(const VarDesc* a, const VarDesc* b) {
return a->Name() < b->Name();
}
void CheckBlockVarsEqual(const BlockDesc& before_block,
const BlockDesc& after_block) {
auto before_vars = before_block.AllVars();
auto after_vars = after_block.AllVars();
EXPECT_EQ(before_vars.size(), after_vars.size());
// var's order is unimportant
std::sort(before_vars.begin(), before_vars.end(), VarComparator);
std::sort(after_vars.begin(), after_vars.end(), VarComparator);
for (size_t var_idx = 0; var_idx < before_vars.size(); ++var_idx) {
const auto& before_var = before_vars.at(var_idx);
const auto& after_var = after_vars.at(var_idx);
EXPECT_EQ(before_var->Name(), after_var->Name());
EXPECT_EQ(before_var->GetType(), after_var->GetType());
}
}
void CheckOpInputsEqual(const OpDesc* before_op, const OpDesc* after_op) {
const auto& before_inputs = before_op->InputNames();
const auto& after_inputs = after_op->InputNames();
EXPECT_EQ(before_inputs.size(), after_inputs.size());
for (size_t in_idx = 0; in_idx < before_inputs.size(); ++in_idx) {
const auto& before_in_arg = before_inputs[in_idx];
const auto& after_in_arg = after_inputs[in_idx];
EXPECT_EQ(before_in_arg, after_in_arg);
const auto& before_in_vars = before_op->Input(before_in_arg);
const auto& after_in_vars = after_op->Input(after_in_arg);
EXPECT_EQ(before_in_vars, after_in_vars);
}
}
void CheckOpOutputsEqual(const OpDesc* before_op, const OpDesc* after_op) {
const auto& before_outputs = before_op->OutputNames();
const auto& after_outputs = after_op->OutputNames();
EXPECT_EQ(before_outputs.size(), after_outputs.size());
for (size_t out_idx = 0; out_idx < before_outputs.size(); ++out_idx) {
const auto& before_out_arg = before_outputs[out_idx];
const auto& after_out_arg = after_outputs[out_idx];
EXPECT_EQ(before_out_arg, after_out_arg);
const auto& before_out_vars = before_op->Output(before_out_arg);
const auto& after_out_vars = after_op->Output(after_out_arg);
EXPECT_EQ(before_out_vars, after_out_vars);
}
}
void CheckOpAttrsEqual(const OpDesc* before_op, const OpDesc* after_op) {
const auto& before_attrs = before_op->AttrNames();
const auto& after_attrs = after_op->AttrNames();
EXPECT_EQ(before_attrs.size(), after_attrs.size());
for (size_t attr_idx = 0; attr_idx < before_attrs.size(); ++attr_idx) {
const auto& before_attr = before_attrs[attr_idx];
const auto& after_attr = after_attrs[attr_idx];
EXPECT_EQ(before_attr, after_attr);
EXPECT_EQ(before_op->GetAttrType(before_attr),
after_op->GetAttrType(after_attr));
}
}
void CheckBlockOpsEqual(const BlockDesc& before_block,
const BlockDesc& after_block) {
EXPECT_EQ(before_block.OpSize(), after_block.OpSize());
// op's order must be the same
for (size_t op_idx = 0; op_idx < before_block.OpSize(); ++op_idx) {
const auto& before_op = before_block.Op(static_cast<int>(op_idx));
const auto& after_op = after_block.Op(static_cast<int>(op_idx));
EXPECT_EQ(before_op->Type(), after_op->Type());
// Step4.2.1 : check each op's input
CheckOpInputsEqual(before_op, after_op);
// Step4.2.2 : check each op's output
CheckOpOutputsEqual(before_op, after_op);
// Step4.2.3 : check each op's attribute
CheckOpAttrsEqual(before_op, after_op);
}
}
TEST(GraphToProgramPass, MultiBlock) {
// Set FLAGS_convert_all_blocks to true to make sure this test works.
bool flag_temp = FLAGS_convert_all_blocks;
FLAGS_convert_all_blocks = true;
// Step1: Build a program with multi block
ProgramDesc before_prog;
BuildProgramWithMultiBlock(&before_prog);
// Step2: Convert program into graph
std::unique_ptr<Graph> g(new ir::Graph(before_prog));
// Step3 : Convert graph back to program
auto pass = paddle::framework::ir::PassRegistry::Instance().Get(
"graph_to_program_pass");
ProgramDesc after_prog;
pass->SetNotOwned<paddle::framework::ProgramDesc>("program", &after_prog);
pass->Apply(g.get());
// Step4 : Check tow program equal
EXPECT_EQ(before_prog.Size(), after_prog.Size());
for (size_t block_idx = 0; block_idx < before_prog.Size(); ++block_idx) {
const auto& before_block = before_prog.Block(block_idx);
const auto& after_block = after_prog.Block(block_idx);
EXPECT_EQ(before_block.ID(), after_block.ID());
// Step4.1 : check each block's var
CheckBlockVarsEqual(before_block, after_block);
// Step4.2 : check each block's op
CheckBlockOpsEqual(before_block, after_block);
}
// Recover FLAGS_convert_all_blocks.
FLAGS_convert_all_blocks = flag_temp;
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(graph_to_program_pass);
@@ -0,0 +1,116 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
TEST(identity_op_clean_pass, assign) {
ProgramDesc program;
auto* x_var = program.MutableBlock(0)->Var("assign_x");
auto* out_var = program.MutableBlock(0)->Var("assign_out");
out_var->SetName(x_var->Name());
OpDesc* assign_op = program.MutableBlock(0)->AppendOp();
assign_op->SetType("assign");
assign_op->SetInput("X", {x_var->Name()});
assign_op->SetOutput("Out", {out_var->Name()});
std::unique_ptr<Graph> graph(new Graph(program));
auto pass = PassRegistry::Instance().Get("identity_op_clean_pass");
graph.reset(pass->Apply(graph.release()));
int assign_num = GetNumOpNodes(graph, "assign");
PADDLE_ENFORCE_EQ(
assign_num,
0,
common::errors::PreconditionNotMet(
"graph should have 0 assign after identity_op_clean_pass, "
"but actually has %d.",
assign_num));
}
TEST(identity_op_clean_pass, scale) {
ProgramDesc program;
auto* x_var = program.MutableBlock(0)->Var("scale_x");
auto* out_var = program.MutableBlock(0)->Var("scale_out");
OpDesc* scale_op = program.MutableBlock(0)->AppendOp();
scale_op->SetType("scale");
scale_op->SetInput("X", {x_var->Name()});
scale_op->SetOutput("Out", {out_var->Name()});
scale_op->SetAttr("scale", 1.f);
scale_op->SetAttr("bias", 0.f);
std::unique_ptr<Graph> graph(new Graph(program));
auto pass = PassRegistry::Instance().Get("identity_op_clean_pass");
graph.reset(pass->Apply(graph.release()));
int scale_num = GetNumOpNodes(graph, "scale");
PADDLE_ENFORCE_EQ(
scale_num,
0,
common::errors::PreconditionNotMet(
"graph should have 0 scale op after identity_op_clean_pass, "
"but actually has %d.",
scale_num));
}
TEST(identity_op_clean_pass, cast) {
ProgramDesc program;
auto* x_var = program.MutableBlock(0)->Var("cast_x");
auto* out_var = program.MutableBlock(0)->Var("cast_out");
OpDesc* cast_op = program.MutableBlock(0)->AppendOp();
cast_op->SetType("cast");
cast_op->SetInput("X", {x_var->Name()});
cast_op->SetOutput("Out", {out_var->Name()});
cast_op->SetAttr("in_dtype", 5);
cast_op->SetAttr("out_dtype", 5);
std::unique_ptr<Graph> graph(new Graph(program));
auto pass = PassRegistry::Instance().Get("identity_op_clean_pass");
graph.reset(pass->Apply(graph.release()));
int cast_num = GetNumOpNodes(graph, "cast");
PADDLE_ENFORCE_EQ(
cast_num,
0,
common::errors::PreconditionNotMet(
"graph should have 0 cast after identity_op_clean_pass, "
"but actually has %d.",
cast_num));
}
TEST(identity_op_clean_pass, concat) {
ProgramDesc program;
auto* x_var = program.MutableBlock(0)->Var("concat_x");
auto* out_var = program.MutableBlock(0)->Var("concat_out");
OpDesc* concat_op = program.MutableBlock(0)->AppendOp();
concat_op->SetType("concat");
concat_op->SetInput("X", {x_var->Name()});
concat_op->SetOutput("Out", {out_var->Name()});
std::unique_ptr<Graph> graph(new Graph(program));
auto pass = PassRegistry::Instance().Get("identity_op_clean_pass");
graph.reset(pass->Apply(graph.release()));
int concat_num = GetNumOpNodes(graph, "concat");
PADDLE_ENFORCE_EQ(
concat_num,
0,
common::errors::PreconditionNotMet(
"graph should have 0 concat after identity_op_clean_pass, "
"but actually has %d.",
concat_num));
}
} // namespace paddle::framework::ir
USE_PASS(identity_op_clean_pass);
@@ -0,0 +1,177 @@
// Copyright (c) 2018 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/fluid/framework/ir/is_test_pass.h"
#if defined _WIN32 || defined __APPLE__
#undef FALSE
#undef TRUE
#endif
namespace paddle {
namespace framework {
namespace ir {
enum class ISTEST_STATE { FALSE, TRUE, UNSET };
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
bool use_onednn = false,
ISTEST_STATE is_test = ISTEST_STATE::UNSET) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
op->SetAttr("name", name);
op->SetInput("X", inputs);
op->SetOutput("Out", outputs);
op->SetAttr("use_onednn", use_onednn);
if (is_test == ISTEST_STATE::UNSET)
op->MutableAttrMap()->erase("is_test");
else if (is_test == ISTEST_STATE::FALSE)
op->SetAttr("is_test", false);
else
op->SetAttr("is_test", true);
}
// a->pool2d->b
// b->relu->c
// c,weights1)->conv2d->d
//
// d->pool2d->e
// e->hard_sigmoid->f
// (f,weights2)->conv2d->g
//
// g->pool2d->h
// h->tanh->i
// (i,weights3)->conv2d->j
ProgramDesc BuildProgramDesc() {
ProgramDesc prog;
for (auto& v : std::vector<std::string>({"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"weights1",
"weights2",
"weights3"})) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::SELECTED_ROWS);
if (v == "weights1" || v == "weights2" || v == "weights3") {
var->SetPersistable(true);
}
}
SetOp(&prog,
"pool2d",
"pooling1",
std::vector<std::string>({"a"}),
std::vector<std::string>({"b"}),
true,
ISTEST_STATE::TRUE);
SetOp(&prog,
"relu",
"activation1",
std::vector<std::string>({"b"}),
std::vector<std::string>({"c"}),
true,
ISTEST_STATE::TRUE);
SetOp(&prog,
"conv2d",
"conv1",
std::vector<std::string>({"c", "weights1"}),
std::vector<std::string>({"d"}),
true,
ISTEST_STATE::TRUE);
SetOp(&prog,
"pool2d",
"pooling2",
std::vector<std::string>({"d"}),
std::vector<std::string>({"e"}),
false,
ISTEST_STATE::FALSE);
SetOp(&prog,
"hard_sigmoid",
"activation2",
std::vector<std::string>({"e"}),
std::vector<std::string>({"f"}),
false,
ISTEST_STATE::FALSE);
SetOp(&prog,
"conv2d",
"conv2",
std::vector<std::string>({"f", "weights2"}),
std::vector<std::string>({"g"}),
false,
ISTEST_STATE::FALSE);
SetOp(&prog,
"pool2d",
"pooling3",
std::vector<std::string>({"g"}),
std::vector<std::string>({"h"}),
false,
ISTEST_STATE::UNSET);
SetOp(&prog,
"tanh",
"activation3",
std::vector<std::string>({"h"}),
std::vector<std::string>({"i"}),
true,
ISTEST_STATE::UNSET);
SetOp(&prog,
"conv2d",
"conv3",
std::vector<std::string>({"i", "weights3"}),
std::vector<std::string>({"j"}),
false,
ISTEST_STATE::UNSET);
return prog;
}
TEST(IsTestPass, basic) {
auto prog = BuildProgramDesc();
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("is_test_pass");
graph.reset(pass->Apply(graph.release()));
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
auto* op = node->Op();
auto op_name = PADDLE_GET_CONST(std::string, op->GetAttr("name"));
if (op_name == "conv3") {
ASSERT_FALSE(op->HasAttr("is_test"));
} else {
ASSERT_TRUE(op->HasAttr("is_test"));
EXPECT_TRUE(PADDLE_GET_CONST(bool, op->GetAttr("is_test")));
}
}
}
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(is_test_pass);
@@ -0,0 +1,149 @@
/* Copyright (c) 2019 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/fluid/framework/ir/multihead_matmul_fuse_pass.h" // NOLINT
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/op_version_registry.h"
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
tensor->mutable_data<float>(phi::CPUPlace());
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope(param_scope, "weights0", {768, 768});
AddVarToScope(param_scope, "weights1", {768, 768});
AddVarToScope(param_scope, "weights2", {768, 768});
AddVarToScope(param_scope, "bias_0", {768});
AddVarToScope(param_scope, "bias_1", {768});
AddVarToScope(param_scope, "bias_2", {768});
AddVarToScope(param_scope, "biasqk", {768});
AddVarToScope(param_scope, "weightsl", {768, 768});
return param_scope;
}
TEST(MultiHeadMatmulFusePass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x) layer_norm -> layer_norm_out
// (layer_norm_out, weights_0) mul -> mul_out0
// (layer_norm_out, weights_1) mul -> mul_out1
// (layer_norm_out, weights_2) mul -> mul_out2
// (mul_out0, bias_0) elementwise_add -> eltadd_0
// (mul_out1, bias_1) elementwise_add -> eltadd_1
// (mul_out2, bias_2) elementwise_add -> eltadd_2
// (eltadd_0) reshape2 -> reshape_0
// (eltadd_1) reshape2 -> reshape_1
// (eltadd_2) reshape2 -> reshape_2
// (reshape_0) transpose2 -> transpose_0
// (reshape_1) transpose2 -> transpose_1
// (reshape_2) transpose2 -> transpose_2
// (transpose_0) scale -> scale_0
// (scale_0, transpose_1) matmul -> matmul_qk
// (matmul_qk, bias_qk) elementwise_add -> eltadd_qk
// (eltadd_qk) softmax -> softmax_qk
// (softmax_qk, transpose_2) matmul -> matmul_qkv
// (matmul_qkv) transpose -> transpose_qkv
// (transpose_qkv) reshape -> reshape_qkv
// (reshape_qkv) mul -> mul_qkv
Layers layers;
auto* x = layers.data("x", {1, 128, 768});
auto out = layers.layer_norm(x);
auto* layer_out = out[0];
auto* weights_0 = layers.data("weights0", {768, 768}, true);
auto* weights_1 = layers.data("weights1", {768, 768}, true);
auto* weights_2 = layers.data("weights2", {768, 768}, true);
auto* mul_out_0 = layers.mul(layer_out, weights_0, nullptr, 2);
auto* mul_out_1 = layers.mul(layer_out, weights_1, nullptr, 2);
auto* mul_out_2 = layers.mul(layer_out, weights_2, nullptr, 2);
auto* b0 = layers.data("bias_0", {768}, true);
auto* b1 = layers.data("bias_1", {768}, true);
auto* b2 = layers.data("bias_2", {768}, true);
auto* elementwise_out_0 = layers.elementwise_add(mul_out_0, b0, nullptr, 2);
auto* elementwise_out_1 = layers.elementwise_add(mul_out_1, b1, nullptr, 2);
auto* elementwise_out_2 = layers.elementwise_add(mul_out_2, b2, nullptr, 2);
std::vector<int> shape = {1, 128, 12, 64};
auto* reshape_0 = layers.reshape2(elementwise_out_0, shape, true);
auto* reshape_1 = layers.reshape2(elementwise_out_1, shape, true);
auto* reshape_2 = layers.reshape2(elementwise_out_2, shape, true);
std::vector<int> axis = {0, 2, 1, 3};
auto* transpose_0 = layers.transpose2(reshape_0, axis, true);
auto* transpose_1 = layers.transpose2(reshape_1, axis, true);
auto* transpose_2 = layers.transpose2(reshape_2, axis, true);
auto* scale_0 = layers.scale(transpose_0, 0.125, 0, false);
auto* matmul_qk = layers.matmul(scale_0, transpose_1, nullptr, false, true);
auto* bqk = layers.data("biasqk", {1, 12, 128, 128}, true);
auto* elementwise_qk = layers.elementwise_add(matmul_qk, bqk);
auto* softmax_qk = layers.softmax(elementwise_qk, -1);
auto* matmul_qkv = layers.matmul(softmax_qk, transpose_2);
auto* transpose_qkv = layers.transpose2(matmul_qkv, {0, 2, 1, 3}, true);
auto* reshape_qkv_out = layers.reshape2(transpose_qkv, {1, 128, 768}, true);
auto* weights_l = layers.data("weightsl", {768, 768}, true);
layers.mul(reshape_qkv_out, weights_l, nullptr, 2);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
auto pass = PassRegistry::Instance().Get("multihead_matmul_fuse_pass_v2");
if (pass.get() == nullptr) LOG(INFO) << "asdfasdf";
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fused_nodes_after = GetNumOpNodes(graph, "multihead_matmul");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(
num_nodes_before,
num_nodes_after + 39,
common::errors::InvalidArgument(
"After the multihead_matmul pass, The node num in graph "
"should be %d, but the result is %d",
num_nodes_before - 39,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"After the multihead_matmul pass, there should be one "
"multihead_matmul op, but the result is %d",
num_fused_nodes_after));
}
TEST(MultiHeadMatmulFusePass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("multihead_matmul_fuse_pass_v2"));
}
} // namespace paddle::framework::ir
USE_PASS(multihead_matmul_fuse_pass);
USE_PASS(multihead_matmul_fuse_pass_v2);
+104
View File
@@ -0,0 +1,104 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/ir/node.h"
#include "gtest/gtest.h"
#include "paddle/fluid/framework/var_desc.h"
namespace paddle::framework::ir {
class Node;
class RunnableOp {
public:
RunnableOp(Node* node, bool* alive) : node_(node), alive_(alive) {
node_->WrappedBy(this);
}
virtual ~RunnableOp() { *alive_ = false; }
private:
Node* node_;
bool* alive_;
};
class RunnableOp2 {
public:
RunnableOp2(Node* node, bool* alive) : node_(node), alive_(alive) {
node_->WrappedBy(this);
}
virtual ~RunnableOp2() { *alive_ = false; }
private:
Node* node_;
bool* alive_;
};
TEST(NodeTest, Basic) {
bool alive1 = true;
bool alive2 = true;
std::unique_ptr<Node> n1(CreateNodeForTest("n1", Node::Type::kVariable));
std::unique_ptr<Node> n2(CreateNodeForTest("n2", Node::Type::kVariable));
EXPECT_FALSE(n1->IsWrappedBy<RunnableOp>());
EXPECT_FALSE(n1->IsWrappedBy<RunnableOp2>());
EXPECT_FALSE(n2->IsWrappedBy<RunnableOp>());
EXPECT_FALSE(n2->IsWrappedBy<RunnableOp2>());
new RunnableOp(n1.get(), &alive1);
new RunnableOp2(n2.get(), &alive2);
EXPECT_TRUE(n1->IsWrappedBy<RunnableOp>());
EXPECT_FALSE(n1->IsWrappedBy<RunnableOp2>());
EXPECT_FALSE(n2->IsWrappedBy<RunnableOp>());
EXPECT_TRUE(n2->IsWrappedBy<RunnableOp2>());
EXPECT_TRUE(alive1);
EXPECT_TRUE(alive2);
n1.reset(nullptr);
n2.reset(nullptr);
EXPECT_FALSE(alive1);
EXPECT_FALSE(alive2);
}
TEST(NodeTest, ToString) {
VarDesc var_desc("n2");
OpDesc op_desc;
op_desc.SetType("test_op");
op_desc.SetInput("X", {"x1", "x2", "x3"});
op_desc.SetOutput("Y", {"y1", "y2"});
std::unique_ptr<Node> n1(CreateNodeForTest("n1", Node::Type::kVariable));
std::unique_ptr<Node> n2(CreateNodeForTest(&var_desc));
std::unique_ptr<Node> n3(CreateNodeForTest("n3", Node::Type::kOperation));
std::unique_ptr<Node> n4(CreateNodeForTest(&op_desc));
EXPECT_EQ(n1->ToString(), "n1");
EXPECT_EQ(n2->ToString(), "n2");
EXPECT_EQ(n3->Op(), nullptr);
EXPECT_EQ(n3->ToString(), "{} = n3()");
EXPECT_NE(n4->Op(), nullptr);
EXPECT_EQ(n4->ToString(), "{Y=[y1 ,y2]} = test_op(X=[x1 ,x2 ,x3])");
n3->inputs.push_back(n1.get());
n3->outputs.push_back(n2.get());
EXPECT_EQ(n3->Op(), nullptr);
EXPECT_EQ(n3->ToString(), "{n2} = n3(n1)");
}
} // namespace paddle::framework::ir
@@ -0,0 +1,62 @@
# OneDNN IR Pass Tests
cc_test(
test_depthwise_conv_onednn_pass
SRCS depthwise_conv_onednn_pass_test.cc
DEPS depthwise_conv_onednn_pass)
cc_test(
test_int8_scale_calculation_onednn_pass
SRCS int8_scale_calculation_onednn_pass_test.cc
DEPS int8_scale_calculation_onednn_pass pass_test_util)
cc_test(
test_params_quantization_onednn_pass
SRCS params_quantization_onednn_pass_test.cc
DEPS params_quantization_onednn_pass)
cc_test(
test_onednn_placement_pass
SRCS onednn_placement_pass_test.cc
DEPS onednn_placement_pass)
cc_test(
test_compute_propagate_scales_onednn_pass
SRCS compute_propagate_scales_onednn_pass_test.cc
DEPS compute_propagate_scales_onednn_pass naive_executor)
if(WITH_ONNXRUNTIME AND WIN32)
# Copy onnxruntime for some c++ test in Windows, since the test will
# be build only in CI, so suppose the generator in Windows is Ninja.
copy_onnx(test_compute_propagate_scales_onednn_pass)
endif()
cc_test(
test_cpu_quantize_placement_pass
SRCS cpu_quantize_placement_pass_test.cc
DEPS cpu_quantize_placement_pass)
cc_test(
test_cpu_quantize_pass
SRCS cpu_quantize_pass_test.cc
DEPS cpu_quantize_pass naive_executor)
cc_test(
test_cpu_quantize_squash_pass
SRCS cpu_quantize_squash_pass_test.cc
DEPS cpu_quantize_squash_pass naive_executor)
cc_test(
test_shuffle_channel_onednn_detect_pass
SRCS shuffle_channel_onednn_detect_pass_test.cc
DEPS shuffle_channel_onednn_detect_pass)
cc_test(
test_cpu_bfloat16_placement_pass
SRCS cpu_bfloat16_placement_pass_test.cc
DEPS cpu_bfloat16_placement_pass)
cc_test(
test_cpu_bfloat16_pass
SRCS cpu_bfloat16_pass_test.cc
DEPS cpu_bfloat16_pass)
@@ -0,0 +1,347 @@
// Copyright (c) 2022 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 <unordered_map>
#include "paddle/fluid/framework/ir/onednn/compute_propagate_scales_onednn_pass.h"
#include "paddle/fluid/framework/naive_executor.h"
#include "paddle/phi/common/place.h"
namespace paddle::framework::ir {
const std::array<float, 10> positive_and_negative_values = {-0.0482659,
-0.0102493,
-0.00794221,
-0.00387115,
-0.00674586,
-0.0495346,
0.0629528,
-0.00531285,
-0.0230353,
0.0269089};
const std::vector<std::vector<float>> wx = {
{0.04347931, -0.5643393, 0.7551297, 0.26713502, 0.8055306, 0.91144973},
{0.01707571, 0.12741385, 0.15419468, 0.66127586, 0.46821925, 0.9665961},
{0.40393898, 0.884427, -0.5853097, 0.5840954, 0.9170512, 0.98245513}};
const std::vector<std::vector<float>> wh = {
{0.42484227, -0.9025513, 0.17087583, 0.8403284, 0.03325734, 0.92331886},
{0.32630175, 0.41691914, 0.99848574, 0.3504407, 0.06707559, 0.62239844}};
const std::vector<double> gru_scales = {
2.35381475, 1.08304947, 1.32427582, 1.19001095, 1.00151656, 1.01785819};
const std::vector<double> lstm_scales = {
2.35381475, 1.10797026, 1.00151656, 1.19001095, 1.09045166, 1.01785819};
static const std::initializer_list<std::string> conv_variable_names{
"conv_in", "filter", "bias", "conv_out"};
static const std::initializer_list<std::string> rnn_variable_names{
"x", "wx", "wh", "b", "h", "c"};
class ComputePropagateScalesOnednnPassTest : public testing::Test {
public:
ComputePropagateScalesOnednnPassTest() { // NOLINT
pass = std::make_unique<ComputePropagateScalesOnednnPass>();
}
std::vector<float> GetScales(phi::DenseTensor* tensor, int axis) const {
return pass->GetScales(tensor, axis);
}
void ComputeVarScales(ir::Graph* graph,
Scope* scope,
const std::unordered_set<std::string> ops,
const std::string& weight_name,
const int axis,
StringPairMap* var_quant_scales) const {
pass->ComputeVarScales(
graph, scope, ops, weight_name, axis, var_quant_scales);
}
void ComputeGruWeightScales(ir::Graph* graph,
Scope* scope,
const std::string& wx_name,
const std::string& wh_name,
StringPairMap* var_quant_scales) const {
pass->ComputeGruWeightScales(
graph, scope, wx_name, wh_name, var_quant_scales);
}
void ComputeLstmWeightScales(ir::Graph* graph,
Scope* scope,
std::string wx_name,
std::string wh_name,
StringPairMap* var_quant_scales) const {
pass->ComputeLstmWeightScales(
graph, scope, wx_name, wh_name, var_quant_scales);
}
void UpdateReluOutputScales(ir::Graph* graph,
StringPairMap* var_quant_scales) const {
pass->UpdateReluOutputScales(graph, var_quant_scales);
}
void InitTensorHolder(Scope* scope,
const phi::Place& place,
const std::string& var_name) {
auto x = scope->Var(var_name);
auto tensor = x->GetMutable<phi::DenseTensor>();
auto tensor_size = 1;
if (var_name == "filter") {
tensor_size = positive_and_negative_values.size();
} else if (var_name == "wx") {
tensor_size = wx.size();
} else if (var_name == "wh") {
tensor_size = wh.size();
}
tensor->mutable_data(
place, phi::TransToPhiDataType(proto::VarType::FP32), tensor_size);
}
void PrepareGraph(ir::Graph* graph,
const ProgramDesc& prog,
Scope* scope,
const std::initializer_list<std::string>& variable_names) {
auto place = phi::CPUPlace();
NaiveExecutor exe{place};
exe.CreateVariables(prog, 0, true, scope);
for (auto& v : variable_names) {
InitTensorHolder(scope, place, v.c_str());
}
graph->SetNotOwned(kParamScopeAttr, scope);
}
void ComputeRnnWeightScalesTest(const std::string& type,
const framework::ProgramDesc& prog,
std::vector<double> scales) {
ir::Graph* graph(new ir::Graph(prog));
Scope scope;
PrepareGraph(graph, prog, &scope, rnn_variable_names);
std::string wx_name = "WeightX";
std::string wh_name = "WeightH";
std::string wx_var_names = "wx";
std::string wh_var_names = "wh";
StringPairMap var_quant_scales;
auto* wx_var = scope.FindVar(wx_var_names);
auto* wx_tensor = wx_var->GetMutable<phi::DenseTensor>();
wx_tensor->Resize(common::make_dim(wx.size(), wx[0].size()));
for (size_t i = 0; i < wx.size(); i++)
std::copy(
begin(wx[i]),
end(wx[i]),
wx_tensor->mutable_data<float>(phi::CPUPlace()) + i * wx[0].size());
auto* wh_var = scope.FindVar(wh_var_names);
auto* wh_tensor = wh_var->GetMutable<phi::DenseTensor>();
wh_tensor->Resize(common::make_dim(wh.size(), wh[0].size()));
for (size_t i = 0; i < wh.size(); i++)
std::copy(
begin(wh[i]),
end(wh[i]),
wh_tensor->mutable_data<float>(phi::CPUPlace()) + i * wh[0].size());
if (type == "gru") { // NOLINT
ComputeGruWeightScales(
graph, &scope, wx_name, wh_name, &var_quant_scales);
} else {
ComputeLstmWeightScales(
graph, &scope, wx_name, wh_name, &var_quant_scales);
}
bool is_unsigned;
phi::DenseTensor wx_result_tensor;
std::tie(is_unsigned, wx_result_tensor) = var_quant_scales[wx_var_names];
ASSERT_EQ(is_unsigned, false);
ASSERT_EQ(wx_result_tensor.numel(), static_cast<int64_t>(scales.size()));
for (int64_t i = 0; i < wx_result_tensor.numel(); i++) {
ASSERT_FLOAT_EQ(wx_result_tensor.data<float>()[i], scales[i]);
}
}
void UpdateReluOutputScaleTest(
const framework::ProgramDesc& prog,
StringPairMap* var_quant_scales,
const std::initializer_list<std::string>& variable_names) {
ir::Graph* graph(new ir::Graph(prog));
Scope scope;
PrepareGraph(graph, prog, &scope, conv_variable_names);
UpdateReluOutputScales(graph, var_quant_scales);
for (auto& var_name : variable_names) {
auto iter = var_quant_scales->find(var_name);
ASSERT_NE(iter, var_quant_scales->end());
ASSERT_EQ((*var_quant_scales)[var_name].first, true);
}
}
private:
std::unique_ptr<ComputePropagateScalesOnednnPass> pass;
};
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::unordered_map<std::string, std::string>& attrs = {}) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
op->SetAttr("use_onednn", true);
op->SetAttr("name", name);
if (!attrs.empty())
for (auto& attr : attrs) op->SetAttr(attr.first, attr.second);
if (type == "conv2d") {
op->SetInput("Input", {inputs[0]});
if (inputs.size() > 1) op->SetInput("Filter", {inputs[1]});
if (inputs.size() > 2) op->SetInput("Bias", {inputs[2]});
op->SetOutput("Output", {outputs[0]});
} else if (type == "fusion_gru" || type == "fusion_lstm") {
op->SetInput("X", {inputs[0]});
op->SetInput("WeightX", {inputs[1]});
op->SetInput("WeightH", {inputs[2]});
op->SetOutput("Hidden", {outputs[0]});
if (type == "fusion_lstm") op->SetOutput("Cell", {outputs[1]});
}
}
ProgramDesc BuildConv2dProgramDesc() {
ProgramDesc prog;
for (auto& v : conv_variable_names) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "conv2d", "Conv2d", {"conv_in", "filter", "bias"}, {"conv_out"});
return prog;
}
ProgramDesc BuildConv2dReluProgramDesc() {
ProgramDesc prog;
for (auto& v : conv_variable_names) {
prog.MutableBlock(0)->Var(v);
}
std::unordered_map<std::string, std::string> attrs = {
{"fuse_activation", "relu"}};
SetOp(&prog,
"conv2d",
"Conv2d",
{"conv_in", "filter", "bias"},
{"conv_out"},
attrs);
return prog;
}
ProgramDesc BuildFusionGruProgramDesc() {
ProgramDesc prog;
for (auto& v : rnn_variable_names) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "fusion_gru", "Fusion_gru", {"x", "wx", "wh"}, {"h"});
return prog;
}
ProgramDesc BuildFusionLstmProgramDesc() {
ProgramDesc prog;
for (auto& v : rnn_variable_names) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "fusion_lstm", "Fusion_lstm", {"x", "wx", "wh"}, {"h", "c"});
return prog;
}
TEST_F(ComputePropagateScalesOnednnPassTest, get_scales_function) {
const auto& values = positive_and_negative_values;
float max_val = *std::max_element(values.begin(), values.end());
phi::DenseTensor var_tensor;
var_tensor.Resize(common::make_dim(values.size(), 1));
std::copy(begin(values),
end(values),
var_tensor.mutable_data<float>(phi::CPUPlace()));
std::vector<float> results = GetScales(&var_tensor, 0);
ASSERT_EQ(results.size(), std::size_t(1));
ASSERT_EQ(results[0], (1.f / max_val));
}
TEST_F(ComputePropagateScalesOnednnPassTest, compute_var_scales) {
auto prog = BuildConv2dProgramDesc();
const auto& values = positive_and_negative_values;
ir::Graph* graph(new ir::Graph(prog));
Scope scope;
PrepareGraph(graph, prog, &scope, conv_variable_names);
std::initializer_list<std::string> ops = {"conv2d", "depthwise_conv2d"};
std::string weight_name = "Filter";
std::string weight_var_name = "filter";
auto axis = 1;
StringPairMap var_quant_scales;
auto* var = scope.FindVar(weight_var_name);
auto* weight_tensor = var->GetMutable<phi::DenseTensor>();
weight_tensor->Resize(common::make_dim(1, values.size()));
std::copy(begin(values),
end(values),
weight_tensor->mutable_data<float>(phi::CPUPlace()));
auto max_val = *std::max_element(values.begin(), values.end());
ComputeVarScales(graph, &scope, ops, weight_name, axis, &var_quant_scales);
bool is_unsigned;
phi::DenseTensor result_tensor;
std::tie(is_unsigned, result_tensor) = var_quant_scales[weight_var_name];
ASSERT_EQ(is_unsigned, false);
ASSERT_EQ(result_tensor.numel(), 1);
ASSERT_FLOAT_EQ(result_tensor.data<float>()[0], (1.0 / max_val));
}
TEST_F(ComputePropagateScalesOnednnPassTest, compute_gru_weight_scales) {
ComputeRnnWeightScalesTest("gru", BuildFusionGruProgramDesc(), gru_scales);
}
TEST_F(ComputePropagateScalesOnednnPassTest, compute_lstm_weight_scales) {
ComputeRnnWeightScalesTest("lstm", BuildFusionLstmProgramDesc(), lstm_scales);
}
TEST_F(ComputePropagateScalesOnednnPassTest, update_relu_output_scales) {
StringPairMap var_quant_scales;
for (auto& var_name : conv_variable_names) {
phi::DenseTensor tensor;
auto* data = tensor.mutable_data<float>({1}, phi::CPUPlace());
data[0] = 10;
auto pair = std::make_pair(false, tensor);
var_quant_scales.insert(std::make_pair(var_name, pair));
}
UpdateReluOutputScaleTest(
BuildConv2dReluProgramDesc(), &var_quant_scales, {"conv_out"});
}
} // namespace paddle::framework::ir
@@ -0,0 +1,233 @@
// Copyright (c) 2020 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/fluid/framework/ir/onednn/cpu_bfloat16_pass.h"
#include "paddle/fluid/imperative/type_defs.h"
namespace paddle::framework::ir {
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
bool use_onednn,
const std::string& onednn_data_type = "float32") {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
op->SetAttr("use_onednn", use_onednn);
op->SetAttr("name", name);
if (type == "conv2d") {
op->SetInput("Input", {inputs[0]});
op->SetOutput("Output", {outputs[0]});
op->SetAttr("onednn_data_type", onednn_data_type);
} else if (type == "pool2d" || type == "transpose2" || type == "reshape2" ||
type == "dropout") {
op->SetInput("X", {inputs[0]});
op->SetOutput("Out", {outputs[0]});
if (type != "dropout") op->SetAttr("onednn_data_type", onednn_data_type);
} else if (type == "fc") {
op->SetInput("Input", {inputs[0]});
op->SetOutput("Out", {outputs[0]});
op->SetAttr("onednn_data_type", onednn_data_type);
} else if (type == "concat" || type == "sum" || type == "split") {
op->SetInput("X", inputs);
op->SetOutput("Out", outputs);
op->SetAttr("onednn_data_type", onednn_data_type);
} else if (type == "matmul" || type == "elementwise_add" ||
type == "elementwise_mul") {
op->SetInput("X", {inputs[0]});
if (inputs.size() > 1) op->SetInput("Y", {inputs[1]});
op->SetOutput("Out", {outputs[0]});
op->SetAttr("onednn_data_type", onednn_data_type);
} else if (type == "layer_norm") {
op->SetInput("X", {inputs[0]});
op->SetOutput("Y", {outputs[0]});
op->SetAttr("onednn_data_type", onednn_data_type);
}
}
static const std::initializer_list<std::string> variable_names{
"z", "a", "b", "c", "d", "e", "f", "g", "h", "i"};
void MainTest(const ProgramDesc& prog,
const int& quant_count,
const int& dequant_count,
const int& added_nodes_count) {
auto graph = std::make_unique<ir::Graph>(prog);
auto pass = PassRegistry::Instance().Get("cpu_bfloat16_pass");
int original_nodes_num = static_cast<int>(graph->Nodes().size());
graph.reset(pass->Apply(graph.release()));
int current_nodes_num = static_cast<int>(graph->Nodes().size());
int quantize_nodes_count = 0;
int dequantize_nodes_count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
auto* op = node->Op();
if (op->Type() == "quantize") {
quantize_nodes_count++;
} else if (op->Type() == "dequantize") {
dequantize_nodes_count++;
}
}
}
EXPECT_EQ(quantize_nodes_count, quant_count);
EXPECT_EQ(dequantize_nodes_count, dequant_count);
EXPECT_EQ(original_nodes_num + added_nodes_count, current_nodes_num);
}
ProgramDesc BuildProgramDescConv(bool use_onednn) {
ProgramDesc prog;
for (auto& v : variable_names) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "dropout", "Dropout", {"a"}, {"b"}, use_onednn, "float32");
SetOp(&prog, "conv2d", "Conv1", {"b"}, {"c"}, use_onednn, "bfloat16");
SetOp(&prog, "pool2d", "Pool", {"c"}, {"d"}, use_onednn, "bfloat16");
SetOp(&prog, "conv2d", "Conv2", {"d"}, {"e"}, use_onednn, "bfloat16");
SetOp(&prog, "transpose2", "Transpose", {"e"}, {"f"}, use_onednn, "float32");
return prog;
}
TEST(CpuBfloat16Pass, convolution) {
bool use_onednn = true;
int quant_op = 3;
int dequant_op = 3;
// each added op consists of 2 nodes
int added_nodes = quant_op * 2 + dequant_op * 2;
MainTest(BuildProgramDescConv(use_onednn), quant_op, dequant_op, added_nodes);
}
ProgramDesc BuildProgramDescDoubleInput(bool use_onednn) {
ProgramDesc prog;
for (auto& v : variable_names) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "dropout", "Dropout", {"a"}, {"b"}, use_onednn, "float32");
SetOp(&prog, "matmul", "Matmul", {"b", "b"}, {"c"}, use_onednn, "bfloat16");
SetOp(&prog, "transpose2", "Transpose", {"d"}, {"e"}, use_onednn, "float32");
SetOp(&prog,
"elementwise_add",
"ElementwiseAdd",
{"c", "e"},
{"f"},
use_onednn,
"bfloat16");
SetOp(&prog, "reshape2", "Reshape", {"f"}, {"g"}, use_onednn, "bfloat16");
return prog;
}
TEST(CpuBfloat16Pass, double_input_ops) {
bool use_onednn = true;
int quant_op = 4;
int dequant_op = 3;
// each added op consists of 2 nodes
int added_nodes = quant_op * 2 + dequant_op * 2;
MainTest(BuildProgramDescDoubleInput(use_onednn),
quant_op,
dequant_op,
added_nodes);
}
ProgramDesc BuildProgramDescDuplicatedInput(bool use_onednn) {
ProgramDesc prog;
for (auto& v : variable_names) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "dropout", "Dropout1", {"a"}, {"b"}, use_onednn, "float32");
SetOp(&prog, "dropout", "Dropout2", {"c"}, {"d"}, use_onednn, "float32");
SetOp(&prog, "concat", "Concat", {"b", "d"}, {"e"}, use_onednn, "bfloat16");
SetOp(&prog, "transpose2", "Transpose", {"f"}, {"g"}, use_onednn, "float32");
SetOp(&prog, "sum", "Sum", {"e", "g"}, {"h"}, use_onednn, "bfloat16");
SetOp(&prog, "reshape2", "Reshape", {"h"}, {"i"}, use_onednn, "bfloat16");
return prog;
}
TEST(CpuBfloat16Pass, duplicated_input_ops) {
bool use_onednn = true;
int quant_op = 5;
int dequant_op = 3;
// each added op consists of 2 nodes
int added_nodes = quant_op * 2 + dequant_op * 2;
MainTest(BuildProgramDescDuplicatedInput(use_onednn),
quant_op,
dequant_op,
added_nodes);
}
ProgramDesc BuildProgramDescDuplicatedOutput(bool use_onednn) {
ProgramDesc prog;
for (auto& v : variable_names) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "dropout", "Dropout", {"a"}, {"b"}, use_onednn, "float32");
SetOp(&prog, "split", "Split", {"b"}, {"c", "d"}, use_onednn, "bfloat16");
SetOp(&prog, "transpose2", "Transpose", {"c"}, {"e"}, use_onednn, "float32");
SetOp(&prog, "reshape2", "Reshape", {"d"}, {"f"}, use_onednn, "bfloat16");
return prog;
}
TEST(CpuBfloat16Pass, duplicated_output_ops) {
bool use_onednn = true;
int quant_op = 2;
int dequant_op = 3;
// each added op consists of 2 nodes
int added_nodes = quant_op * 2 + dequant_op * 2;
MainTest(BuildProgramDescDuplicatedOutput(use_onednn),
quant_op,
dequant_op,
added_nodes);
}
ProgramDesc BuildProgramDescDoubleOutputs(bool use_onednn) {
ProgramDesc prog;
for (auto& v : variable_names) {
prog.MutableBlock(0)->Var(v);
}
SetOp(
&prog, "layer_norm", "LayerNorm1", {"a"}, {"b"}, use_onednn, "bfloat16");
SetOp(&prog, "dropout", "Dropout1", {"b"}, {"c"}, use_onednn, "float32");
SetOp(&prog, "transpose2", "Transpose", {"b"}, {"d"}, use_onednn, "bfloat16");
SetOp(
&prog, "layer_norm", "LayerNorm2", {"d"}, {"e"}, use_onednn, "bfloat16");
SetOp(&prog, "reshape2", "Reshape", {"e"}, {"f"}, use_onednn, "float32");
SetOp(&prog, "dropout", "Dropout2", {"e"}, {"g"}, use_onednn, "float32");
return prog;
}
TEST(CpuBfloat16Pass, double_outputs_ops) {
bool use_onednn = true;
int quant_op = 3;
int dequant_op = 3;
// each added op consists of 2 nodes
int added_nodes = quant_op * 2 + dequant_op * 2;
MainTest(BuildProgramDescDoubleOutputs(use_onednn),
quant_op,
dequant_op,
added_nodes);
}
} // namespace paddle::framework::ir
USE_PASS(cpu_bfloat16_pass);
@@ -0,0 +1,177 @@
// Copyright (c) 2020 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/fluid/framework/ir/onednn/cpu_bfloat16_placement_pass.h"
#include "paddle/fluid/platform/onednn_helper.h"
namespace paddle::framework::ir {
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::string& onednn_data_type = "float32",
const bool use_onednn = true) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
if (type != "reshape2") op->SetAttr("use_onednn", use_onednn);
op->SetAttr("onednn_data_type", onednn_data_type);
if (type == "conv2d") {
op->SetAttr("name", name);
op->SetInput("Input", {inputs[0]});
} else if (type == "gelu") {
op->SetInput("X", inputs);
} else if (type == "concat") {
op->SetAttr("axis", 1);
op->SetInput("X", {inputs[0], inputs[1]});
} else if (type == "pool2d") {
op->SetInput("X", {inputs[0]});
} else if (type == "transpose2") {
op->SetInput("X", {inputs[0]});
} else if (type == "reshape2") {
op->SetInput("X", {inputs[0]});
} else if (type == "sum") {
op->SetInput("X", {inputs[0], inputs[1]});
} else {
FAIL() << "Unexpected operator type.";
}
op->SetOutput("Out", {outputs[0]});
}
// operator onednn_data_type
// ---------------------------------------
// (a,b)->concat->c float32
// c->conv->f float32
// f->relu->g float32
// g->pool->h float32
// h->conv->k float32
// k->pool->l float32
ProgramDesc BuildProgramDesc() {
ProgramDesc prog;
for (auto& v : std::vector<std::string>({"a",
"b",
"c",
"f",
"g",
"h",
"k",
"l",
"m",
"n",
"o",
"p",
"r",
"s"})) {
prog.MutableBlock(0)->Var(v)->SetDataType(proto::VarType::FP32);
}
SetOp(&prog, "concat", "concat1", {"a", "b"}, {"c"});
SetOp(&prog, "conv2d", "conv1", {"c"}, {"f"});
SetOp(&prog, "gelu", "gelu1", {"f"}, {"g"});
SetOp(&prog, "pool2d", "pool1", {"g"}, {"h"});
SetOp(&prog, "conv2d", "conv2", {"h"}, {"k"});
SetOp(&prog, "pool2d", "pool2", {"k"}, {"l"});
SetOp(&prog, "concat", "concat2", {"l", "m"}, {"n"});
SetOp(&prog, "transpose2", "transpose", {"n"}, {"o"});
SetOp(&prog, "reshape2", "reshape", {"o"}, {"p"});
SetOp(&prog, "sum", "sum", {"p", "r"}, {"s"});
return prog;
}
void MainTest(std::initializer_list<std::string> bfloat16_enabled_op_types,
unsigned expected_bfloat16_data_type_count,
const ProgramDesc& prog) {
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("cpu_bfloat16_placement_pass");
pass->Set("bfloat16_enabled_op_types",
new std::unordered_set<std::string>(bfloat16_enabled_op_types));
graph.reset(pass->Apply(graph.release()));
unsigned bfloat16_data_type_count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
if (platform::HasOpBFLOAT16DataType(node->Op())) {
++bfloat16_data_type_count;
}
}
}
EXPECT_EQ(bfloat16_data_type_count, expected_bfloat16_data_type_count);
}
void DefaultAttrTest(unsigned expected_bfloat16_data_type_count,
const ProgramDesc& prog) {
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("cpu_bfloat16_placement_pass");
graph.reset(pass->Apply(graph.release()));
unsigned bfloat16_data_type_count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
if (platform::HasOpBFLOAT16DataType(node->Op())) {
++bfloat16_data_type_count;
}
}
}
EXPECT_EQ(bfloat16_data_type_count, expected_bfloat16_data_type_count);
}
TEST(Bfloat16PlacementPass, enable_all) {
MainTest(
{"conv2d", "pool2d", "gelu", "concat", "sum"}, 8, BuildProgramDesc());
}
TEST(Bfloat16PlacementPass, enabled_conv_and_pool) {
// 2 conv2d + 2 pool2 - 1 orphaned conv2d
MainTest({"conv2d", "pool2d"}, 3, BuildProgramDesc());
}
TEST(Bfloat16PlacementPass, default_attr_value) {
DefaultAttrTest(10, BuildProgramDesc());
}
ProgramDesc BuildProgramDescWithDataType() {
ProgramDesc prog;
for (auto& v : std::vector<std::string>({"a", "b", "c", "d", "e"})) {
if (v == "a") {
prog.MutableBlock(0)->Var(v)->SetDataType(proto::VarType::INT32);
} else {
prog.MutableBlock(0)->Var(v)->SetDataType(proto::VarType::FP32);
}
}
SetOp(&prog, "conv2d", "conv1", {"a"}, {"b"});
SetOp(&prog, "pool2d", "pool1", {"b"}, {"c"});
SetOp(&prog, "concat", "concat1", {"c", "d"}, {"e"});
return prog;
}
TEST(Bfloat16PlacementPass, check_data_types) {
DefaultAttrTest(2, BuildProgramDescWithDataType());
}
} // namespace paddle::framework::ir
USE_PASS(cpu_bfloat16_placement_pass);
@@ -0,0 +1,911 @@
// Copyright (c) 2019 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 <unordered_map>
#include "paddle/fluid/framework/ir/onednn/cpu_quantize_pass.h" // NOLINT
#include "paddle/fluid/framework/naive_executor.h"
#include "paddle/fluid/imperative/type_defs.h"
#include "paddle/phi/common/place.h"
namespace paddle {
namespace framework {
namespace ir {
static float const SCALE = 2.f;
static int const S8_MAX = 127;
static int const U8_MAX = 255;
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
bool use_onednn,
const std::string& onednn_data_type = "float32") {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
op->SetAttr("use_onednn", use_onednn);
op->SetAttr("name", name);
if (type != "dropout" && type != "quantize" && type != "dequantize") {
op->SetAttr("onednn_data_type", onednn_data_type);
}
if (type == "conv2d") {
op->SetInput("Input", {inputs[0]});
op->SetInput("Filter", {inputs[1]});
if (inputs.size() > 2)
op->SetInput("Bias", {inputs[2]});
else
op->SetInput("Bias", {});
if (inputs.size() > 3) {
op->SetInput("ResidualData", {inputs[3]});
op->SetAttr("fuse_residual_connection", true);
} else {
op->SetInput("ResidualData", {});
op->SetAttr("fuse_residual_connection", false);
}
op->SetOutput("Output", {outputs[0]});
} else if (type == "pool2d" || type == "fused_transpose" ||
type == "reshape2" || type == "nearest_interp" ||
type == "nearest_interp_v2" || type == "dropout") {
op->SetInput("X", {inputs[0]});
op->SetOutput("Out", {outputs[0]});
} else if (type == "slice") {
op->SetInput("Input", {inputs[0]});
op->SetOutput("Out", {outputs[0]});
} else if (type == "split") {
op->SetInput("X", {inputs[0]});
op->SetOutput("Out", {outputs});
} else if (type == "fc") {
op->SetInput("Input", {inputs[0]});
if (inputs.size() > 1) op->SetInput("W", {inputs[1]});
if (inputs.size() > 2) op->SetInput("Bias", {inputs[2]});
op->SetOutput("Out", {outputs[0]});
op->SetAttr("Scale_in", 1.0f);
op->SetAttr("Scale_out", 1.0f);
op->SetAttr("Scale_weights", std::vector<float>{1.0f});
} else if (type == "concat") {
op->SetInput("X", inputs);
op->SetOutput("Out", outputs);
} else if (type == "dequantize") {
op->SetInput("Input", {inputs[0]});
op->SetOutput("Output", {outputs[0]});
op->SetAttr("Scale", 1.0f);
} else if (type == "matmul" || type == "matmul_v2" ||
type == "fused_matmul") {
op->SetInput("X", {inputs[0]});
if (inputs.size() > 1) op->SetInput("Y", {inputs[1]});
if (inputs.size() > 2) op->SetInput("ResidualData", {inputs[2]});
op->SetOutput("Out", {outputs[0]});
op->SetAttr("Scale_x", 1.0f);
op->SetAttr("Scale_y", 1.0f);
op->SetAttr("Scale_out", 1.0f);
} else if (type == "fused_elementwise_add" ||
type == "fused_elementwise_sub" ||
type == "fused_elementwise_mul") {
op->SetInput("X", {inputs[0]});
if (inputs.size() > 1) op->SetInput("Y", {inputs[1]});
op->SetOutput("Out", {outputs[0]});
op->SetAttr("scale_x", 1.0f);
op->SetAttr("scale_y", 1.0f);
op->SetAttr("scale_out", 1.0f);
} else if (type == "fusion_gru") {
op->SetInput("X", {inputs[0]});
op->SetInput("Bias", {inputs[1]});
op->SetInput("WeightX", {inputs[2]});
op->SetInput("WeightH", {inputs[3]});
op->SetOutput("Hidden", {outputs[0]});
op->SetAttr("Scale_data", 1.0f);
op->SetAttr("Shift_data", 0.0f);
op->SetAttr("Weight_scale", std::vector<float>{1.0f});
} else if (type == "fusion_lstm") {
op->SetInput("X", {inputs[0]});
op->SetInput("Bias", {inputs[1]});
op->SetInput("WeightX", {inputs[2]});
op->SetInput("WeightH", {inputs[3]});
op->SetOutput("Hidden", {outputs[0]});
op->SetOutput("Cell", {outputs[1]});
op->SetAttr("Scale_data", 1.0f);
op->SetAttr("Shift_data", 0.0f);
op->SetAttr("Weight_scale", std::vector<float>{1.0f});
}
}
void InitTensorHolder(Scope* scope,
const phi::Place& place,
const char* var_name) {
auto x = scope->Var(var_name);
auto tensor = x->GetMutable<phi::DenseTensor>();
tensor->mutable_data(place, phi::TransToPhiDataType(proto::VarType::FP32), 1);
}
void PreparePass(std::unique_ptr<ir::Graph>* graph,
const ProgramDesc& prog,
const std::vector<std::string> variable_names,
int* original_nodes_num,
int* current_nodes_num,
std::string var_without_scale = "",
std::string var_signed = "") {
auto place = phi::CPUPlace();
NaiveExecutor exe{place};
Scope scope;
exe.CreateVariables(prog, 0, true, &scope);
auto* scales = new VarQuantScale();
for (auto& v : variable_names) {
if (v.compare(var_without_scale) == 0) continue;
InitTensorHolder(&scope, place, v.c_str());
phi::DenseTensor tensor;
tensor.Resize({1});
auto* ptr = tensor.mutable_data<double>(place);
ptr[0] = SCALE;
(*scales)[v] = std::make_pair(v == var_signed, std::move(tensor));
}
(*graph)->SetNotOwned(kParamScopeAttr, &scope);
std::unique_ptr<Pass> pass =
PassRegistry::Instance().Get("cpu_quantize_pass");
pass->Set("quant_var_scales", scales);
*original_nodes_num = (*graph)->Nodes().size();
(*graph).reset(pass->Apply((*graph).release()));
*current_nodes_num = (*graph)->Nodes().size();
}
void CheckScales(const OpDesc* op, float scale, float shift) {
std::string type = op->Type();
std::vector<std::string> scale_names;
if (type == "conv2d" || type == "fused_conv2d" || type == "fc") {
EXPECT_EQ(op->GetAttrIfExists<std::vector<float>>("Scale_weights")[0],
scale);
scale_names.push_back("Scale_in");
scale_names.push_back("Scale_out");
} else if (type == "fused_matmul") {
scale_names.push_back("Scale_x");
scale_names.push_back("Scale_y");
scale_names.push_back("Scale_out");
auto const& names = op->InputNames();
if (std::find(names.begin(), names.end(), "ResidualData") != names.end())
scale_names.push_back("Scale_in_eltwise");
} else if (type == "fused_elementwise_add" ||
type == "fused_elementwise_sub" ||
type == "fused_elementwise_mul") {
scale_names.push_back("scale_x");
scale_names.push_back("scale_y");
scale_names.push_back("scale_out");
} else if (type == "fusion_gru" || type == "fusion_lstm") {
EXPECT_EQ(op->GetAttrIfExists<float>("Shift_data"), shift);
EXPECT_EQ(op->GetAttrIfExists<std::vector<float>>("Scale_weights")[0],
scale);
EXPECT_EQ(op->GetAttrIfExists<bool>("force_fp32_output"), true);
scale_names.push_back("Scale_data");
}
for (auto const& scale_name : scale_names) {
EXPECT_EQ(op->GetAttrIfExists<float>(scale_name), scale);
}
}
void MainTest(const ProgramDesc& prog,
const std::vector<std::string> variable_names,
std::unordered_map<std::string, int> expected_operators,
const int added_nodes_count,
float scale = 1.f,
float shift = 1.f,
std::string var_without_scale = "",
std::string var_signed = "") {
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
int original_nodes_num, current_nodes_num;
PreparePass(&graph,
prog,
variable_names,
&original_nodes_num,
&current_nodes_num,
var_without_scale,
var_signed);
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
auto* op = node->Op();
if (expected_operators.count(op->Type()) > 0) {
expected_operators[op->Type()]--;
if (op->GetAttrIfExists<std::string>("onednn_data_type") == "int8")
CheckScales(op, scale, shift);
}
}
}
for (auto const& pair : expected_operators) {
EXPECT_EQ(pair.second, 0);
}
EXPECT_EQ(original_nodes_num + added_nodes_count, current_nodes_num);
}
static const std::initializer_list<std::string> variable_names{"a",
"w1",
"c",
"d",
"w2",
"e",
"f",
"g",
"h",
"w3",
"b1",
"i",
"j",
"w4",
"b2",
"w5",
"b3"};
// (a,w1)->Conv1->c and c->Pool1->d
//
// (d,w2)->Conv2->e and e->Pool2->f
//
// d->Dropout1->g and (g, w5, b3)->Fc1->h and (h,w3,b1,i)->Conv3->j
//
// (d,w4, b2)->Conv4->i
ProgramDesc BuildProgramDesc(bool use_onednn,
const std::string& onednn_data_type) {
ProgramDesc prog;
for (auto& v : variable_names) {
auto* var = prog.MutableBlock(0)->Var(v);
if (v.find("w") == 0 || v.find("b") == 0) {
var->SetPersistable(true);
}
}
SetOp(&prog,
"conv2d",
"Conv1",
{"a", "w1"},
{"c"},
use_onednn,
onednn_data_type);
SetOp(&prog, "pool2d", "Pool1", {"c"}, {"d"}, use_onednn, onednn_data_type);
SetOp(&prog,
"conv2d",
"Conv2",
{"d", "w2"},
{"e"},
use_onednn,
onednn_data_type);
SetOp(&prog, "pool2d", "Pool2", {"e"}, {"f"}, use_onednn, onednn_data_type);
SetOp(&prog, "dropout", "Dropout1", {"d"}, {"g"}, use_onednn);
SetOp(&prog,
"fc",
"Fc1",
{"g", "w5", "b3"},
{"h"},
use_onednn,
onednn_data_type);
SetOp(&prog,
"conv2d",
"Conv3",
{"h", "w3", "b1", "i"},
{"j"},
use_onednn,
onednn_data_type);
SetOp(&prog,
"conv2d",
"Conv4",
{"c", "w4", "b2"},
{"i"},
use_onednn,
onednn_data_type);
return prog;
}
TEST(CpuQuantizePass, quantize) {
bool use_onednn = true;
std::string onednn_data_type = "int8";
// (a->QUANT1->IN1,w1)->Conv1->OUT1->DEQUANT1->c and
// c->QUANT2->IN2->Pool1->OUT2->DEQUANT2->d
//
// (d->QUANT3->IN3,w2)->Conv2->OUT3->DEQUANT3->e and
// e->QUANT4->IN4->Pool2->OUT4->DEQUANT4->f
//
// d->Dropout1->g and (g->QUANT8->IN8,w5,b3)->Fc1->OUT7->DEQUANT7->h and
// (h->QUANT5->IN5,w3,b1,i->QUANT6->IN6)->Conv3->OUT5->DEQUANT5->j
//
// (d->QUANT7->IN7,w4, b2)->Conv4->DEQUANT6->OUT6->i
// Insert nodes: 8 Quant + 8 IN + 7 OUT + 7 DEQUANT
int added_nodes = 8 + 8 + 7 + 7;
std::unordered_map<std::string, int> expected_operators = {
{"fused_conv2d", 4}, {"pool2d", 2}, {"quantize", 8}, {"dequantize", 7}};
MainTest(BuildProgramDesc(use_onednn, onednn_data_type),
variable_names,
expected_operators,
added_nodes,
SCALE * S8_MAX);
}
TEST(CpuQuantizePass, do_not_quantize) {
bool use_onednn = true;
std::string onednn_data_type = "float32";
int added_nodes = 0;
std::unordered_map<std::string, int> expected_operators = {
{"fused_conv2d", 4}, {"pool2d", 2}, {"quantize", 0}, {"dequantize", 0}};
MainTest(BuildProgramDesc(use_onednn, onednn_data_type),
variable_names,
expected_operators,
added_nodes,
1.0f);
}
static const std::initializer_list<std::string> variable_names_concat = {
"a1", "b1", "a2", "b2", "c", "d"};
// a1->Pool1->b1
// a2->Pool2->b2
// (b1,b2)->Concat->c
// c->Pool3->d
ProgramDesc BuildProgramDescConcat() {
ProgramDesc prog;
SetOp(&prog, "pool2d", "Pool1", {"a1"}, {"b1"}, true, "float32");
SetOp(&prog, "pool2d", "Pool2", {"a2"}, {"b2"}, true, "float32");
SetOp(&prog, "concat", "Concat", {"b1", "b2"}, {"c"}, true, "int8");
SetOp(&prog, "pool2d", "Pool3", {"c"}, {"d"}, true, "float32");
return prog;
}
TEST(CpuQuantizePass, concat) {
// a1->Pool1->b1
// a2->Pool2->b2
// (b1->QUANT1->IN1, b2->QUANT2->IN2)->Concat->c
// c->OUT1->DEQUANT1->Pool3->d
int added_nodes = 6;
std::unordered_map<std::string, int> expected_operators = {
{"pool2d", 3}, {"concat", 1}, {"quantize", 2}, {"dequantize", 1}};
MainTest(BuildProgramDescConcat(),
variable_names_concat,
expected_operators,
added_nodes);
}
static const std::initializer_list<std::string> variable_names_fusion_gru = {
"x", "wx", "wh", "b", "h"};
// (x, wx, wh, b)->Fusion_gru->h
ProgramDesc BuildProgramDescFusionGru() {
ProgramDesc prog;
for (auto& v : variable_names_fusion_gru) {
auto* var = prog.MutableBlock(0)->Var(v);
if (v.find("wx") == 0 || v.find("wh") || v.find("b")) {
var->SetPersistable(true);
}
}
SetOp(&prog,
"fusion_gru",
"Fusion_gru",
{"x", "wx", "wh", "b"},
{"h"},
true,
"int8");
return prog;
}
static const std::initializer_list<std::string> variable_names_fusion_lstm = {
"x", "wx", "wh", "b", "h", "c"};
// (x, wx, wh, b)->Fusion_lstm_1->h
ProgramDesc BuildProgramDescFusionLSTM() {
ProgramDesc prog;
for (auto& v : variable_names_fusion_lstm) {
auto* var = prog.MutableBlock(0)->Var(v);
if (v.find("wx") == 0 || v.find("wh") || v.find("b")) {
var->SetPersistable(true);
}
}
SetOp(&prog,
"fusion_lstm",
"Fusion_lstm_1",
{"x", "wx", "wh", "b"},
{"h", "c"},
true,
"int8");
return prog;
}
TEST(CpuQuantizePass, fusion_gru) {
// (x, wx, wh, b)->Fusion_gru->h
// 1 Quant + 1 IN + 0 DeQuant + 0 OUT
int added_nodes = 1 + 1 + 0 + 0;
std::unordered_map<std::string, int> expected_operators = {
{"fusion_gru", 1}, {"quantize", 1}, {"dequantize", 0}};
MainTest(BuildProgramDescFusionGru(),
variable_names_fusion_gru,
expected_operators,
added_nodes,
SCALE * S8_MAX,
128);
}
TEST(CpuQuantizePass, fusion_lstm) {
// (x, wx, wh, b)->Fusion_lstm->h
// 1 Quant + 1 IN + 0 DeQuant + 0 OUT
int added_nodes = 1 + 1 + 0 + 0;
std::unordered_map<std::string, int> expected_operators = {
{"fusion_lstm", 1}, {"quantize", 1}, {"dequantize", 0}};
MainTest(BuildProgramDescFusionLSTM(),
variable_names_fusion_lstm,
expected_operators,
added_nodes,
SCALE * S8_MAX,
128.);
}
static const std::initializer_list<std::string> variable_names_immutable_ops = {
"a", "w1", "b", "c", "d", "e", "f", "g"};
// a->Dequantize->b
// b->Tested Op->c
// c->Dropout->d
void TestImmutableOp(const std::string tested_op) {
ProgramDesc prog;
for (auto& v : variable_names_immutable_ops) {
prog.MutableBlock(0)->Var(v)->SetDataType(proto::VarType::FP32);
}
SetOp(&prog, "dequantize", "Dequantize1", {"a"}, {"b"}, true);
SetOp(&prog, tested_op, tested_op, {"b"}, {"c"}, true, "int8");
SetOp(&prog, "dropout", "Dropout", {"c"}, {"d"}, true, "float32");
// a->Dequantize->b
// b2->Quant->b3->Tested Op->c1->Dequant->c2
// c2->Dropout->d
// 1 Quant + 1 IN + 1 DeQuant + 1 OUT
int added_nodes = 4;
std::unordered_map<std::string, int> expected_operators = {
{tested_op, 1}, {"quantize", 1}, {"dequantize", 2}};
MainTest(prog,
variable_names_immutable_ops,
expected_operators,
added_nodes,
SCALE * S8_MAX);
}
// a->Dropout1->b
// b->Tested Op->c
// c->Dropout2->d
void TestImmutableOpBetweenNonQuantizedOp(const std::string tested_op) {
ProgramDesc prog;
for (auto& v : variable_names_immutable_ops) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "dropout", "Dropout1", {"a"}, {"b"}, true, "float32");
SetOp(&prog, tested_op, tested_op, {"b"}, {"c"}, true, "int8");
SetOp(&prog, "dropout", "Dropout2", {"c"}, {"d"}, true, "float32");
// 0 Quant + 0 IN + 0 DeQuant + 0 OUT
int added_nodes = 0;
std::unordered_map<std::string, int> expected_operators = {
{tested_op, 1}, {"dropout", 2}, {"quantize", 0}, {"dequantize", 0}};
MainTest(prog,
variable_names_immutable_ops,
expected_operators,
added_nodes,
SCALE * S8_MAX);
}
// a->Dropout1->b
// b->TestedOp1(won't be quantized)->c
// c->Dropout2->d
// c->TestedOp2(will be quantized)->e
// e->Pool2d1(will be quantized)->f
// e->Pool2d2(will be quantized)->g
void TestImmutableOpWithManyOutputs(const std::string tested_op) {
ProgramDesc prog;
for (auto& v : variable_names_immutable_ops) {
prog.MutableBlock(0)->Var(v)->SetDataType(proto::VarType::FP32);
}
SetOp(&prog, "dropout", "Dropout1", {"a"}, {"b"}, true, "float32");
SetOp(&prog,
tested_op,
std::string(tested_op + "1"),
{"b"},
{"c"},
true,
"int8");
SetOp(&prog, "dropout", "Dropout2", {"c"}, {"d"}, true, "float32");
SetOp(&prog,
tested_op,
std::string(tested_op + "2"),
{"c"},
{"e"},
true,
"int8");
SetOp(&prog, "pool2d", "Pool2d1", {"e"}, {"f"}, true, "int8");
SetOp(&prog, "pool2d", "Pool2d2", {"e"}, {"g"}, true, "int8");
// 3 Quant + 3 IN + 3 DeQuant + 3 OUT
int added_nodes = 12;
std::unordered_map<std::string, int> expected_operators = {{tested_op, 2},
{"dropout", 2},
{"pool2d", 2},
{"quantize", 3},
{"dequantize", 3}};
MainTest(prog,
variable_names_immutable_ops,
expected_operators,
added_nodes,
SCALE * S8_MAX);
}
const std::vector<std::string> immutables = {"reshape2",
"fused_transpose",
"slice",
"nearest_interp",
"nearest_interp_v2",
"split"};
class TestImmutables : public testing::TestWithParam<std::string> {};
TEST_P(TestImmutables, immutable_basic) { // NOLINT
TestImmutableOp(GetParam());
}
TEST_P(TestImmutables, immutable_between_non_quantized) { // NOLINT
TestImmutableOpBetweenNonQuantizedOp(GetParam());
}
TEST_P(TestImmutables, immutable_many_outputs) { // NOLINT
TestImmutableOpWithManyOutputs(GetParam());
}
INSTANTIATE_TEST_CASE_P(
CpuQuantizePass,
TestImmutables,
testing::ValuesIn(immutables),
[](const ::testing::TestParamInfo<TestImmutables::ParamType>& info) {
std::string name = info.param;
return name;
});
static const std::initializer_list<std::string> variable_names_matmul = {
"a", "b", "c", "d", "e", "f", "g", "h"};
ProgramDesc BuildProgramDescMatmul() {
ProgramDesc prog;
for (auto& v : variable_names_matmul) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "dequantize", "Dequantize1", {"a"}, {"b"}, true);
SetOp(&prog, "dequantize", "Dequantize2", {"c"}, {"d"}, true);
SetOp(&prog, "fused_matmul", "FusedMatmul", {"b", "d"}, {"e"}, true, "int8");
SetOp(&prog, "dropout", "Dropout", {"e"}, {"f"}, true, "float32");
return prog;
}
ProgramDesc BuildProgramDescMatmulResidual() {
ProgramDesc prog;
for (auto& v : variable_names_matmul) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "dequantize", "Dequantize1", {"a"}, {"b"}, true);
SetOp(&prog, "dequantize", "Dequantize2", {"c"}, {"d"}, true);
SetOp(&prog, "dequantize", "Dequantize3", {"e"}, {"f"}, true);
SetOp(&prog,
"fused_matmul",
"FusedMatmul",
{"b", "d", "f"},
{"g"},
true,
"int8");
SetOp(&prog, "dropout", "Dropout", {"g"}, {"h"}, true, "float32");
return prog;
}
TEST(CpuQuantizePass, matmul) {
// 2 Quant + 2 IN + 1 DeQuant + 1 OUT
int added_nodes = 6;
std::unordered_map<std::string, int> expected_operators = {
{"fused_matmul", 1}, {"quantize", 2}, {"dequantize", 3}};
MainTest(BuildProgramDescMatmul(),
variable_names_matmul,
expected_operators,
added_nodes,
SCALE * S8_MAX);
}
TEST(CpuQuantizePass, matmul_residual) {
// 3 Quant + 3 IN + 1 DeQuant + 1 OUT
int added_nodes = 8;
std::unordered_map<std::string, int> expected_operators = {
{"fused_matmul", 1}, {"quantize", 3}, {"dequantize", 4}};
MainTest(BuildProgramDescMatmulResidual(),
variable_names_matmul,
expected_operators,
added_nodes,
SCALE * S8_MAX);
}
static const std::initializer_list<std::string> variable_names_elementwise = {
"a", "b", "c", "d", "e", "f"};
ProgramDesc BuildProgramDescElementwise(const std::string elementwise_type,
const std::string elementwise_name) {
ProgramDesc prog;
for (auto& v : variable_names_elementwise) {
prog.MutableBlock(0)->Var(v);
}
SetOp(&prog, "dequantize", "Dequantize1", {"a"}, {"b"}, true);
SetOp(&prog, "dequantize", "Dequantize2", {"c"}, {"d"}, true);
SetOp(&prog,
elementwise_type,
elementwise_name,
{"b", "d"},
{"e"},
true,
"int8");
SetOp(&prog, "dropout", "Dropout", {"e"}, {"f"}, true, "float32");
return prog;
}
void TestElementwise(std::vector<std::string> elementwise) {
// 2 Quant + 2 IN + 1 DeQuant + 1 OUT
int added_nodes = 6;
std::unordered_map<std::string, int> expected_operators = {
{elementwise[0], 1}, {"quantize", 2}, {"dequantize", 3}};
MainTest(BuildProgramDescElementwise(elementwise[0], elementwise[1]),
variable_names_elementwise,
expected_operators,
added_nodes,
SCALE * S8_MAX);
}
void TestElementwiseOutputScaleMissing(std::vector<std::string> elementwise) {
int added_nodes = 0;
std::unordered_map<std::string, int> expected_operators = {
{elementwise[0], 1}, {"quantize", 0}, {"dequantize", 2}};
MainTest(BuildProgramDescElementwise(elementwise[0], elementwise[1]),
variable_names_elementwise,
expected_operators,
added_nodes,
1.f,
1.f,
"e");
}
void TestElementwiseUnsignedAndSignedInput(
std::vector<std::string> elementwise) {
int added_nodes = 0;
std::unordered_map<std::string, int> expected_operators = {
{elementwise[0], 1}, {"quantize", 0}, {"dequantize", 2}};
MainTest(BuildProgramDescElementwise(elementwise[0], elementwise[1]),
variable_names_elementwise,
expected_operators,
added_nodes,
1.f,
1.f,
"",
"b");
}
const std::vector<std::vector<std::string>> elementwises = {
{"fused_elementwise_add", "FusedElementwiseAdd"},
{"fused_elementwise_mul", "FusedElementwiseMul"},
{"fused_elementwise_sub", "FusedElementwiseSub"}};
class TestElementwises
: public testing::TestWithParam<std::vector<std::string>> {};
TEST_P(TestElementwises, elementwise_basic) { // NOLIN
TestElementwise(GetParam());
}
TEST_P(TestElementwises, elementwise_output_scale_missing) { // NOLINT
TestElementwiseOutputScaleMissing(GetParam());
}
TEST_P(TestElementwises, elementwise_unsigned_and_signed_input) { // NOLINT
TestElementwiseUnsignedAndSignedInput(GetParam());
}
INSTANTIATE_TEST_CASE_P(
CpuQuantizePass,
TestElementwises,
testing::ValuesIn(elementwises),
[](const ::testing::TestParamInfo<TestElementwises::ParamType>& info) {
std::string name = info.param[0];
return name;
});
const std::vector<std::string> churn_out_vars(ProgramDesc* prog,
const std::string& prefix,
int number) {
auto v = std::vector<std::string>();
for (int i = 0; i < number; ++i) {
auto name = prefix + std::to_string(i);
prog->MutableBlock(0)->Var(name);
v.push_back(name);
}
return v;
}
void create_vars(ProgramDesc* prog,
const std::initializer_list<std::string>& names) {
for (auto const& name : names) prog->MutableBlock(0)->Var(name);
}
void SetMultiGruOp(ProgramDesc* prog,
const std::string x,
const std::vector<std::string> wx,
const std::vector<std::string> wh,
const std::vector<std::string> b,
const std::string h,
int layers) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType("multi_gru");
op->SetInput("X", {x});
op->SetInput("WeightX", wx);
op->SetInput("WeightH", wh);
op->SetInput("Bias", b);
op->SetOutput("Hidden", {h});
op->SetAttr("layers", layers);
op->SetAttr("origin_mode", false);
op->SetAttr("use_onednn", true);
op->SetAttr("name", std::string("Multi_gru"));
op->SetAttr("onednn_data_type", std::string("int8"));
op->SetAttr("Scale_data", 1.0f);
op->SetAttr("Shift_data", 0.0f);
}
void MainTestMultiGru(int layers) {
ProgramDesc prog;
// Create variables
create_vars(&prog, {"x", "h"});
const std::vector<std::string> wx = churn_out_vars(&prog, "wx", 2 * layers);
const std::vector<std::string> wh = churn_out_vars(&prog, "wh", 2 * layers);
const std::vector<std::string> b = churn_out_vars(&prog, "b", 2 * layers);
std::vector<std::string> all_vars;
all_vars.reserve(wx.size() + wh.size() + b.size() + 2);
all_vars.insert(all_vars.end(), wx.begin(), wx.end());
all_vars.insert(all_vars.end(), wh.begin(), wh.end());
all_vars.insert(all_vars.end(), b.begin(), b.end());
all_vars.push_back("x");
all_vars.push_back("h");
// Prepare program descriptor
SetMultiGruOp(&prog, "x", wx, wh, b, "h", layers);
// Prepare and run the pass
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
int original_nodes_num, current_nodes_num;
PreparePass(&graph, prog, all_vars, &original_nodes_num, &current_nodes_num);
// Verify graph after quantization
float scale = 2 * 127;
float shift = 128;
int quantize_nodes_count = 0;
int dequantize_nodes_count = 0;
int multi_gru_nodes_count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
auto* op = node->Op();
if (op->Type() == "multi_gru") {
multi_gru_nodes_count++;
auto op_name = PADDLE_GET_CONST(std::string, op->GetAttr("name"));
EXPECT_EQ(PADDLE_GET_CONST(float, op->GetAttr("Scale_data")), scale)
<< "Scale_data for node '" + op_name + "'.";
EXPECT_EQ(PADDLE_GET_CONST(float, op->GetAttr("Shift_data")), shift)
<< "Shift_data for node '" + op_name + "'.";
EXPECT_EQ(op->Input("Scale_weights").size(), 2u * layers)
<< "Scale_weights for node '" + op_name + "'.";
EXPECT_EQ(PADDLE_GET_CONST(bool, op->GetAttr("force_fp32_output")),
true)
<< "force_fp32_output for node '" + op_name + "'.";
} else if (op->Type() == "quantize") {
quantize_nodes_count++;
} else if (op->Type() == "dequantize") {
dequantize_nodes_count++;
}
}
}
int multi_gru_count = 1;
int quant_count = 1;
int quant_out_count = 1;
int dequant_count = 0;
int dequant_out_count = 0;
int scale_weights_count = 2 * layers;
int added_nodes_count = quant_count + quant_out_count + scale_weights_count +
dequant_count + dequant_out_count;
EXPECT_EQ(multi_gru_nodes_count, multi_gru_count);
EXPECT_EQ(quantize_nodes_count, quant_count);
EXPECT_EQ(dequantize_nodes_count, dequant_count);
EXPECT_EQ(original_nodes_num + added_nodes_count, current_nodes_num);
}
TEST(CpuQuantizePass, multi_gru_1) {
int layers = 1;
MainTestMultiGru(layers);
}
TEST(CpuQuantizePass, multi_gru_2) {
int layers = 2;
MainTestMultiGru(layers);
}
TEST(CpuQuantizePass, multi_gru_3) {
int layers = 3;
MainTestMultiGru(layers);
}
static const std::initializer_list<std::string>
variable_names_multi_inputs_outputs = {"a", "b", "c1", "c2", "d", "e"};
// a->Pool->b
// b->Split->c1, c2
// (c1, c2, c1, c2)->Concat->d
// d->Pool->e
ProgramDesc BuildProgramDescMulti() {
ProgramDesc prog;
for (auto& v : variable_names_multi_inputs_outputs) {
prog.MutableBlock(0)->Var(v)->SetDataType(proto::VarType::FP32);
}
SetOp(&prog, "pool2d", "Pool", {"a"}, {"b"}, true, "float32");
SetOp(&prog, "split", "Split", {"b"}, {"c1", "c2"}, true, "int8");
SetOp(
&prog, "concat", "Concat", {"c1", "c2", "c1", "c2"}, {"d"}, true, "int8");
SetOp(&prog, "pool2d", "Pool2", {"d"}, {"e"}, true, "float32");
return prog;
}
TEST(CpuQuantizePass, multi_inputs_outputs_ops) {
// a->QUANT1->Split
// b1->DEQUANT->OUT->QUANT
// b2->DEQUANT->OUT->QUANT
// (b1, b2, b1, b2)->Concat->c->DEQUANT->Pool->d
int added_nodes = 6 * 2;
std::unordered_map<std::string, int> expected_operators = {{"pool2d", 2},
{"concat", 1},
{"split", 1},
{"quantize", 3},
{"dequantize", 3}};
MainTest(BuildProgramDescMulti(),
variable_names_multi_inputs_outputs,
expected_operators,
added_nodes);
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(cpu_quantize_pass);
@@ -0,0 +1,185 @@
// Copyright (c) 2019 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/fluid/framework/ir/onednn/cpu_quantize_placement_pass.h"
#include "paddle/fluid/platform/onednn_helper.h"
namespace paddle {
namespace framework {
namespace ir {
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
const std::string& onednn_data_type = "float32") {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
op->SetAttr("use_onednn", true);
op->SetAttr("onednn_data_type", onednn_data_type);
if (type == "conv2d") {
op->SetAttr("name", name);
op->SetInput("Input", {inputs[0]});
op->SetInput("Filter", {inputs[1]});
op->SetInput("Bias", {inputs[2]});
} else if (type == "relu") {
op->SetInput("X", inputs);
} else if (type == "concat") {
op->SetAttr("axis", 1);
op->SetInput("X", {inputs[0], inputs[1]});
} else if (type == "pool2d") {
op->SetInput("X", {inputs[0]});
} else {
FAIL() << "Unexpected operator type.";
}
op->SetOutput("Out", {outputs[0]});
}
// operator onednn_data_type
// ---------------------------------------
// (a,b)->concat->c none
// (c,weights,bias)->conv->f false
// f->relu->g none
// g->pool->h false
// (h,weights2,bias2)->conv->k false
// k->pool->l false
ProgramDesc BuildProgramDesc() {
ProgramDesc prog;
for (auto& v : std::vector<std::string>({"a",
"b",
"c",
"weights",
"bias",
"f",
"g",
"h",
"weights2",
"bias2",
"k",
"l"})) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::SELECTED_ROWS);
if (v == "weights" || v == "bias") {
var->SetPersistable(true);
}
}
SetOp(&prog, "concat", "concat1", {"a", "b"}, {"c"}, "float32");
SetOp(&prog, "conv2d", "conv1", {"c", "weights", "bias"}, {"f"}, "float32");
SetOp(&prog, "relu", "relu1", {"f"}, {"g"}, "float32");
SetOp(&prog, "pool2d", "pool1", {"g"}, {"h"}, "float32");
SetOp(&prog, "conv2d", "conv2", {"h", "weights2", "bias2"}, {"k"}, "float32");
SetOp(&prog, "pool2d", "pool2", {"k"}, {"l"}, "float32");
return prog;
}
void MainTest(std::initializer_list<std::string> quantize_enabled_op_types,
std::initializer_list<int> quantize_excluded_op_ids,
unsigned expected_int8_data_type_count) {
auto prog = BuildProgramDesc();
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("cpu_quantize_placement_pass");
pass->Set("quantize_enabled_op_types",
new std::unordered_set<std::string>(quantize_enabled_op_types));
pass->Set("quantize_excluded_op_ids",
new std::unordered_set<int>(quantize_excluded_op_ids));
graph.reset(pass->Apply(graph.release()));
unsigned int8_data_type_count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
if (platform::HasOpINT8DataType(node->Op())) {
++int8_data_type_count;
}
}
}
EXPECT_EQ(int8_data_type_count, expected_int8_data_type_count);
}
void DefaultAttrTest(unsigned expected_int8_data_type_count) {
auto prog = BuildProgramDesc();
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("cpu_quantize_placement_pass");
graph.reset(pass->Apply(graph.release()));
unsigned int8_data_type_count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
if (platform::HasOpINT8DataType(node->Op())) {
++int8_data_type_count;
}
}
}
EXPECT_EQ(int8_data_type_count, expected_int8_data_type_count);
}
TEST(QuantizerPlacementPass, enabled_pool) { MainTest({"pool2d"}, {}, 2); }
TEST(QuantizerPlacementPass, enabled_conv_excluded_one) {
MainTest({"conv2d"}, {4}, 1);
}
TEST(QuantizerPlacementPass, empty_list) {
// all operators except relu should be quantized
MainTest({}, {}, 5);
}
TEST(QuantizerPlacementPass, default_attr_value) {
// all operators except relu should be quantized
DefaultAttrTest(5);
}
void EnabledOpTypesTest(
std::initializer_list<std::string> quantize_enabled_op_types,
std::string missing_op) {
auto prog = BuildProgramDesc();
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("cpu_quantize_placement_pass");
pass->Set("quantize_enabled_op_types",
new std::unordered_set<std::string>(quantize_enabled_op_types));
try {
graph.reset(pass->Apply(graph.release()));
} catch (paddle::platform::EnforceNotMet& err) {
std::string ex_msg = err.what();
std::string expected_msg =
"Pass attribute quantize_enabled_op_types contains operator " +
missing_op + " that is not supported by OneDNN quantization.";
EXPECT_TRUE(ex_msg.find(expected_msg) != std::string::npos);
}
}
TEST(QuantizerPlacementPass, unsupported_op_type) {
// Dropout op is not supported by OneDNN quantization
EnabledOpTypesTest({"conv2d", "dropout"}, "dropout");
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(cpu_quantize_placement_pass);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,157 @@
// Copyright (c) 2018 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/fluid/framework/ir/onednn/depthwise_conv_onednn_pass.h"
#include "paddle/fluid/framework/op_version_registry.h"
namespace paddle::framework::ir {
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
bool use_onednn = false) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
op->SetAttr("use_onednn", use_onednn);
op->SetAttr("name", name);
op->SetAttr("groups", 1);
op->SetAttr("padding_algorithm", std::string("EXPLICIT"));
op->SetAttr("data_format", std::string("NCHW"));
op->SetAttr("strides", std::vector<int>({1, 1}));
op->SetAttr("dilations", std::vector<int>({1, 1}));
op->SetAttr("paddings", std::vector<int>({0, 0}));
op->SetInput("Input", {inputs[0]});
op->SetInput("Filter", {inputs[1]});
op->SetInput("Bias", {inputs[2]});
op->SetOutput("Output", outputs);
}
// (a, weights, bias)->depthwise conv onednn->b
// (b, weights2, bias2)->depthwise conv no onednn->c
// (c, weights3, bias3)->conv onednn->d
// (d, weights3, bias3)->conv no onednn->e
ProgramDesc BuildProgramDesc() {
ProgramDesc prog;
for (auto& v : std::vector<std::string>({"a",
"b",
"c",
"d",
"e",
"weights",
"bias",
"weights2",
"bias2",
"weights3",
"bias3",
"weights4",
"bias4"})) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::SELECTED_ROWS);
if (v == "weights" || v == "bias" || v == "weights2" || v == "bias2" ||
v == "weights3" || v == "bias3" || v == "weights4" || v == "bias4") {
var->SetPersistable(true);
}
}
// depthwise conv with MKL-DNN
SetOp(&prog,
"depthwise_conv2d",
"conv1",
std::vector<std::string>({"a", "weights", "bias"}),
std::vector<std::string>({"b"}),
true);
// depthwise conv without MKL-DNN
SetOp(&prog,
"depthwise_conv2d",
"conv2",
std::vector<std::string>({"b", "weights2", "bias2"}),
std::vector<std::string>({"c"}),
false);
// conv with MKL-DNN
SetOp(&prog,
"conv2d",
"conv3",
std::vector<std::string>({"c", "weights3", "bias3"}),
std::vector<std::string>({"d"}),
true);
// conv without MKL-dNN
SetOp(&prog,
"conv2d",
"conv4",
std::vector<std::string>({"d", "weights4", "bias4"}),
std::vector<std::string>({"e"}),
false);
return prog;
}
TEST(DepthwiseConvOneDNNPass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("depthwise_conv_onednn_pass"));
}
TEST(DepthwiseConvOneDNNPass, basic) {
auto prog = BuildProgramDesc();
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("depthwise_conv_onednn_pass");
struct counters {
int onednn_depthwise_conv_nodes;
int other_depthwise_conv_nodes;
int onednn_conv_nodes;
int other_conv_nodes;
};
counters before{1, 1, 1, 1};
graph.reset(pass->Apply(graph.release()));
// initialize counters before loop
counters after{0, 0, 0, 0};
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
auto* op = node->Op();
if (op->Type() == "conv2d") {
if (PADDLE_GET_CONST(bool, op->GetAttr("use_onednn")))
after.onednn_conv_nodes++;
else
after.other_conv_nodes++;
} else if (op->Type() == "depthwise_conv2d") {
if (PADDLE_GET_CONST(bool, op->GetAttr("use_onednn")))
after.onednn_depthwise_conv_nodes++;
else
after.other_depthwise_conv_nodes++;
}
}
}
EXPECT_EQ(after.other_depthwise_conv_nodes,
before.other_depthwise_conv_nodes);
EXPECT_EQ(after.other_conv_nodes, before.other_conv_nodes);
EXPECT_EQ(after.onednn_depthwise_conv_nodes,
before.onednn_depthwise_conv_nodes - 1);
EXPECT_EQ(after.onednn_conv_nodes, before.onednn_conv_nodes + 1);
}
} // namespace paddle::framework::ir
USE_PASS(depthwise_conv_onednn_pass);
@@ -0,0 +1,152 @@
// Copyright (c) 2022 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/fluid/framework/ir/onednn/int8_scale_calculation_onednn_pass.h"
namespace paddle::framework::ir {
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
std::vector<float> scale_weights = {1.5f}) { // NOLINT
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
if (type == "conv2d") {
op->SetAttr("use_onednn", true);
op->SetAttr("name", name);
op->SetAttr("strides", std::vector<int>({1, 1}));
op->SetAttr("groups", 1);
op->SetAttr("paddings", std::vector<int>({0, 0}));
op->SetAttr("padding_algorithm", std::string("EXPLICIT"));
op->SetAttr("dilations", std::vector<int>({1, 1}));
op->SetAttr("data_format", std::string("NCHW"));
op->SetInput("Input", {inputs[0]});
op->SetInput("Filter", {inputs[1]});
if (inputs.size() > 2)
op->SetInput("Bias", {inputs[2]});
else
op->SetInput("Bias", {});
op->SetOutput("Output", outputs);
op->SetAttr("Scale_in", 1.0f);
op->SetAttr("Scale_out", 1.0f);
op->SetAttr("Scale_weights", scale_weights);
op->SetAttr("use_onednn", true);
op->SetAttr("onednn_data_type", std::string("int8"));
} else {
FAIL() << "Unexpected operator type.";
}
}
ProgramDesc BuildProgramDesc(bool convWithExistingBias,
std::vector<float> scale_weights = {1.5f}) {
ProgramDesc prog;
std::vector<std::string> nodes{"c", "weights", "f"};
if (convWithExistingBias) nodes.push_back("conv_bias");
for (auto& v : nodes) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::DENSE_TENSOR);
if (v == "weights") {
var->SetPersistable(true);
var->SetShape({1, static_cast<int>(scale_weights.size()), 1, 1});
}
}
if (convWithExistingBias || scale_weights.size() > 1) {
SetOp(&prog,
"conv2d",
"conv",
std::vector<std::string>({"c", "weights", "conv_bias"}),
std::vector<std::string>({"f"}),
scale_weights);
} else {
SetOp(&prog,
"conv2d",
"conv",
std::vector<std::string>({"c", "weights"}),
std::vector<std::string>({"f"}));
}
return prog;
}
void MainTest(bool convWithExistingBias,
int removed_nodes_count,
float scale,
std::vector<float> scale_weights = {1.5f}) { // NOLINT
auto prog = BuildProgramDesc(convWithExistingBias, scale_weights);
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass =
PassRegistry::Instance().Get("int8_scale_calculation_onednn_pass");
int original_nodes_num = graph->Nodes().size();
graph.reset(pass->Apply(graph.release()));
int current_nodes_num = graph->Nodes().size();
EXPECT_EQ(original_nodes_num, current_nodes_num);
for (auto* node : graph->Nodes()) {
if (node->IsOp() && node->Op()->Type() == "conv2d") {
auto* op = node->Op();
ASSERT_TRUE(op->HasAttr("use_mkldnn") || op->HasAttr("use_onednn"));
EXPECT_EQ(op->GetAttrIfExists<std::vector<float>>("Scale_weights"),
scale_weights);
EXPECT_EQ(op->GetAttrIfExists<float>("Scale_in"), scale);
EXPECT_EQ(op->GetAttrIfExists<float>("Scale_out"), scale);
EXPECT_EQ(op->GetAttrIfExists<float>("Sum_scale"), scale);
EXPECT_EQ(
op->GetAttrIfExists<std::vector<float>>("Output_shift_scale")[0],
scale / scale_weights[0]);
EXPECT_EQ(op->GetAttrIfExists<float>("Activation_scale"), scale);
if (convWithExistingBias) {
EXPECT_EQ(op->GetAttrIfExists<std::vector<float>>("Bias_scales")[0],
scale * scale_weights[0]);
}
}
}
EXPECT_EQ(original_nodes_num - removed_nodes_count, current_nodes_num);
}
TEST(Int8ScaleCalculationOnednnPass, int8_scale_calculation_with_no_bias) {
auto scale = 1.0f;
int removed_nodes_count = 0;
auto scale_weights = {1.5f};
MainTest(false, removed_nodes_count, scale, scale_weights);
}
TEST(Int8ScaleCalculationOnednnPass, int8_scale_calculation_with_bias) {
auto scale = 1.0f;
int removed_nodes_count = 0;
auto scale_weights = {1.5f};
MainTest(true, removed_nodes_count, scale, scale_weights);
}
TEST(Int8ScaleCalculationOnednnPass,
int8_scale_calculation_with_bias_scale_weights) {
auto scale = 1.0f;
int removed_nodes_count = 0;
std::vector<float> scale_weights = {1.5f, 2.3f};
MainTest(true, removed_nodes_count, scale, scale_weights);
}
} // namespace paddle::framework::ir
USE_PASS(int8_scale_calculation_onednn_pass);
@@ -0,0 +1,187 @@
// Copyright (c) 2019 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/fluid/framework/ir/onednn/onednn_placement_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/utils/tribool.h"
namespace paddle::framework::ir {
class PlacementPassTest {
private:
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs,
paddle::tribool use_onednn) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
if (!paddle::indeterminate(use_onednn))
op->SetAttr("use_onednn", use_onednn);
if (type == "conv2d") {
op->SetAttr("name", name);
op->SetInput("Input", {inputs[0]});
op->SetInput("Filter", {inputs[1]});
op->SetInput("Bias", {inputs[2]});
} else if (type == "relu") {
op->SetInput("X", inputs);
} else if (type == "concat") {
op->SetAttr("axis", 1);
op->SetInput("X", {inputs[0], inputs[1]});
} else if (type == "pool2d") {
op->SetInput("X", {inputs[0]});
} else {
FAIL() << "Unexpected operator type.";
}
op->SetOutput("Out", {outputs[0]});
}
// operator use_onednn
// ---------------------------------------
// (a,b)->concat->c none
// (c,weights,bias)->conv->f none
// f->relu->g false
// g->pool->h false
// (h,weights2,bias2)->conv->k true
// k->relu->l true
ProgramDesc BuildProgramDesc() {
ProgramDesc prog;
for (auto& v : std::vector<std::string>({"a",
"b",
"c",
"weights",
"bias",
"f",
"g",
"h",
"weights2",
"bias2",
"k",
"l"})) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::SELECTED_ROWS);
var->SetDataType(framework::proto::VarType::FP32);
if (v == "weights" || v == "bias") {
var->SetPersistable(true);
}
}
SetOp(&prog,
"concat",
"concat1",
std::vector<std::string>({"a", "b"}),
std::vector<std::string>({"c"}),
paddle::indeterminate);
SetOp(&prog,
"conv2d",
"conv1",
std::vector<std::string>({"c", "weights", "bias"}),
std::vector<std::string>({"f"}),
paddle::indeterminate);
SetOp(&prog,
"relu",
"relu1",
std::vector<std::string>({"f"}),
std::vector<std::string>({"g"}),
false);
SetOp(&prog,
"pool2d",
"pool1",
std::vector<std::string>({"g"}),
std::vector<std::string>({"h"}),
false);
SetOp(&prog,
"conv2d",
"conv2",
std::vector<std::string>({"h", "weights2", "bias2"}),
std::vector<std::string>({"k"}),
true);
SetOp(&prog,
"relu",
"relu2",
std::vector<std::string>({"k"}),
std::vector<std::string>({"l"}),
true);
return prog;
}
public:
void MainTest(std::initializer_list<std::string> onednn_enabled_op_types,
unsigned expected_use_onednn_true_count) {
auto prog = BuildProgramDesc();
RegisterOpKernel({"conv2d", "pool2d", "concat", "relu"});
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("onednn_placement_pass");
pass->Set("onednn_enabled_op_types",
new std::unordered_set<std::string>(onednn_enabled_op_types));
graph.reset(pass->Apply(graph.release()));
unsigned use_onednn_true_count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
auto* op = node->Op();
if ((op->HasAttr("use_mkldnn") &&
PADDLE_GET_CONST(bool, op->GetAttr("use_mkldnn"))) ||
(op->HasAttr("use_onednn") &&
PADDLE_GET_CONST(bool, op->GetAttr("use_onednn")))) {
++use_onednn_true_count;
}
}
}
EXPECT_EQ(use_onednn_true_count, expected_use_onednn_true_count);
}
void PlacementNameTest() {
auto pass = PassRegistry::Instance().Get("onednn_placement_pass");
EXPECT_EQ(static_cast<PlacementPassBase*>(pass.get())->GetPlacementName(),
"ONEDNN");
}
};
TEST(ONEDNNPlacementPass, enable_conv_relu) {
// 2 conv (1 conv is always true) + 2 relu (1 relu is always true) + 0 pool
PlacementPassTest().MainTest({"conv2d", "relu"}, 4);
}
TEST(ONEDNNPlacementPass, enable_relu_pool) {
// 1 conv (1 conv is always true) + 2 relu (1 relu is always true) + 1 pool
PlacementPassTest().MainTest({"relu", "pool2d"}, 4);
}
TEST(ONEDNNPlacementPass, enable_all) {
// 2 conv (1 conv is always true) + 2 relu (1 relu is always true) + 1 pool +
// 1 concat
PlacementPassTest().MainTest({}, 6);
}
TEST(ONEDNNPlacementPass, placement_name) {
PlacementPassTest().PlacementNameTest();
}
} // namespace paddle::framework::ir
USE_PASS(onednn_placement_pass);
@@ -0,0 +1,383 @@
// Copyright (c) 2022 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/fluid/framework/ir/onednn/params_quantization_onednn_pass.h" // NOLINT
#include "paddle/fluid/imperative/type_defs.h"
#include "paddle/phi/common/place.h"
namespace paddle::framework::ir {
namespace {
struct Data {
Data() = default;
Data(std::vector<int64_t>&& data_shape, std::vector<float>&& raw_data)
: shape(std::move(data_shape)), data(std::move(raw_data)) {
auto size_from_shape = std::accumulate(
shape.begin(), shape.end(), 1, std::multiplies<int64_t>());
PADDLE_ENFORCE_EQ(
size_from_shape,
data.size(),
common::errors::InvalidArgument("Shape size doesn't match data size."));
}
const std::vector<int64_t>& getShape() const { return shape; }
const std::vector<float>& getData() const { return data; }
private:
const std::vector<int64_t> shape{};
const std::vector<float> data{};
};
struct TestScope {
void CreateTensor(const std::string& var_name, const Data& data) {
auto variable = scope.Var(var_name);
auto tensor = variable->GetMutable<phi::DenseTensor>();
tensor->Resize(common::make_ddim(data.getShape()));
auto dptr = tensor->mutable_data<float>(place);
std::copy(data.getData().begin(), data.getData().end(), dptr);
}
const phi::DenseTensor& GetTensor(const std::string& input) const {
Variable* var = scope.FindVar(input);
return var->Get<phi::DenseTensor>();
}
framework::Scope* Scope() { return &scope; }
private:
framework::Scope scope;
CPUPlace place;
};
struct ProgramStrategy {
virtual ~ProgramStrategy() = default;
std::unique_ptr<Graph> CreateGraph() {
CreateProgram();
auto graph = std::make_unique<ir::Graph>(program);
graph->SetNotOwned(kParamScopeAttr, test_scope.Scope());
return graph;
}
void CheckGraph(const std::unique_ptr<ir::Graph>& graph) const {
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
CheckOp(*node->Op());
}
}
}
protected:
virtual void CreateProgram() = 0;
virtual void CheckOp(const OpDesc& op) const = 0;
VarDesc* AddInput(OpDesc* op,
std::string input_name,
const Data& data,
const std::string user_var_name = "") {
std::string var_name = user_var_name;
if (var_name.empty()) {
var_name = input_name + "_var";
}
op->SetInput(input_name, {var_name});
auto var = program.MutableBlock(0)->Var(var_name);
var->SetShape(data.getShape());
test_scope.CreateTensor(var_name, data);
return var;
}
void AddOutput(OpDesc* op,
std::string output_name,
const Data& data,
const std::string user_var_name = "") {
std::string var_name = user_var_name;
if (var_name.empty()) {
var_name = output_name + "_var";
}
op->SetOutput(output_name, {var_name});
program.MutableBlock(0)->Var(var_name);
test_scope.CreateTensor(var_name, data);
}
protected:
TestScope test_scope;
ProgramDesc program;
};
struct ConvProgramStrategy : public ProgramStrategy {
ConvProgramStrategy(Data&& input,
Data&& filter,
Data&& output,
std::vector<float>&& scale_weights,
int groups = 1,
Data&& bias = Data(),
std::vector<float>&& scale_bias = {},
bool share_weight = false)
: input(std::move(input)),
filter(std::move(filter)),
output(std::move(output)),
scale_weights(std::move(scale_weights)),
groups(std::move(groups)),
bias(std::move(bias)),
scale_bias(std::move(scale_bias)),
share_weight(std::move(share_weight)) {}
protected:
OpDesc* CreateBasicConvOp(const std::string conv_name = "Conv1") {
auto op = program.MutableBlock(0)->AppendOp();
op->SetType("fused_conv2d");
op->SetAttr("use_onednn", true);
op->SetAttr("name", conv_name);
op->SetAttr("onednn_data_type", std::string{"int8"});
op->SetAttr("data_format", std::string{"NCHW"});
op->SetAttr("dilations", std::vector<int>({1, 1}));
op->SetAttr("paddings", std::vector<int>({1, 1}));
op->SetAttr("strides", std::vector<int>({1, 1}));
return op;
}
protected:
void CreateProgram() override {
OpDesc* op = CreateBasicConvOp();
AddInput(op, "Input", input);
AddInput(op, "Filter", filter)->SetPersistable(true);
AddOutput(op, "Output", output);
op->SetAttr("Scale_weights", scale_weights);
op->SetAttr("Scale_in", 1.0f);
op->SetAttr("groups", groups);
if (HasBias()) {
AddInput(op, "Bias", bias);
op->SetAttr("Bias_scales", scale_bias);
}
if (share_weight) {
OpDesc* op2 = CreateBasicConvOp("Conv2");
AddInput(op2, "Input", input);
AddInput(op2, "Filter", filter)->SetPersistable(true);
AddOutput(op2, "Output", output, "output2");
op2->SetAttr("Scale_weights", scale_weights);
op2->SetAttr("Scale_in", 1.0f);
op2->SetAttr("groups", groups);
if (HasBias()) {
AddInput(op2, "Bias", bias, "Bias2");
op2->SetAttr("Bias_scales", scale_bias);
}
}
}
void CheckOp(const OpDesc& op) const override {
CheckFilter(op);
if (HasBias()) {
CheckBias(op);
}
}
bool HasBias() const { return !bias.getData().empty(); }
void CheckFilter(const OpDesc& op) const {
EXPECT_EQ(op.GetAttrIfExists<std::vector<float>>("Scale_weights"),
std::vector<float>(1, 1));
auto filter_inputs = op.Input("Filter");
ASSERT_EQ(filter_inputs.size(), 1ul);
auto tensor = test_scope.GetTensor(filter_inputs[0]);
ASSERT_EQ(tensor.dtype(), phi::DataType::INT8);
auto filter_ptr = tensor.data<int8_t>();
ASSERT_NE(filter_ptr, nullptr);
auto length = tensor.numel() / scale_weights.size();
for (int64_t i = 0; i < tensor.numel(); i++) {
EXPECT_EQ(filter_ptr[i],
static_cast<int8_t>(std::round(filter.getData()[i] *
scale_weights[i / length])));
}
}
void CheckBias(const OpDesc& op) const {
EXPECT_EQ(op.GetAttrIfExists<std::vector<float>>("Bias_scales"),
std::vector<float>(1, 1));
auto bias_inputs = op.Input("Bias");
ASSERT_EQ(bias_inputs.size(), 1ul);
auto tensor = test_scope.GetTensor(bias_inputs[0]);
auto bias_ptr = tensor.data<int32_t>();
ASSERT_NE(bias_ptr, nullptr);
auto length = tensor.numel() / scale_bias.size();
for (int64_t i = 0; i < tensor.numel(); i++) {
EXPECT_EQ(bias_ptr[i],
static_cast<int32_t>(
std::round(bias.getData()[i] * scale_bias[i / length])));
}
}
private:
const Data input;
const Data filter;
const Data output;
const std::vector<float> scale_weights;
const int groups;
const Data bias;
const std::vector<float> scale_bias;
const bool share_weight;
};
struct ParamsQuantizationOnednnPassTestFixture : public ::testing::Test {
void RunPassTest(std::unique_ptr<ProgramStrategy> program) {
auto graph = program->CreateGraph();
auto pass = PassRegistry::Instance().Get("params_quantization_onednn_pass");
graph.reset(pass->Apply(graph.release()));
program->CheckGraph(graph);
}
};
Data GenericInput() { return Data({1, 4, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f}); }
Data GenericOutput() { return GenericInput(); }
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_without_bias_o1i1h1w1) {
auto program =
std::make_unique<ConvProgramStrategy>(GenericInput(),
Data({1, 1, 1, 1}, {1.5f}),
GenericOutput(),
std::vector<float>{2.f});
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_without_bias_2o1i1h1w) {
auto program =
std::make_unique<ConvProgramStrategy>(GenericInput(),
Data({2, 1, 1, 1}, {1.5f, 1.5f}),
GenericOutput(),
std::vector<float>{2.f, 4.f});
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_without_bias_2o2i2h2w) {
auto program =
std::make_unique<ConvProgramStrategy>(GenericInput(),
Data({2, 2, 2, 2},
{1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f,
1.5f}),
GenericOutput(),
std::vector<float>{2.f, 4.f});
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_without_bias_2g2o2i1h1w) {
auto program = std::make_unique<ConvProgramStrategy>(
GenericInput(),
Data({2, 2, 2, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f}),
GenericOutput(),
std::vector<float>{2.f, 2.f, 2.f, 2.f},
2);
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_without_bias_2g2o1i1h1w) {
auto program = std::make_unique<ConvProgramStrategy>(
GenericInput(),
Data({2, 2, 1, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f}),
GenericOutput(),
std::vector<float>{2.f, 2.f, 2.f, 2.f},
2);
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_with_bias_1o1i1h1w) {
auto program =
std::make_unique<ConvProgramStrategy>(GenericInput(),
Data({1, 1, 1, 1}, {1.5f}),
GenericOutput(),
std::vector<float>{2.f},
1,
Data({1, 1, 1, 1}, {1.5f}),
std::vector<float>{2.f});
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_with_bias_2o1i1h1w) {
auto program =
std::make_unique<ConvProgramStrategy>(GenericInput(),
Data({2, 1, 1, 1}, {1.5f, 1.5f}),
GenericOutput(),
std::vector<float>{2.f, 4.f},
1,
Data({2, 1, 1, 1}, {1.5f, 1.5f}),
std::vector<float>{2.f, 4.f});
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_with_bias_2g2o1i1h1w) {
auto program = std::make_unique<ConvProgramStrategy>(
GenericInput(),
Data({4, 1, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f}),
GenericOutput(),
std::vector<float>{2.f, 2.f, 4.f, 4.f},
2,
Data({4, 1, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f}),
std::vector<float>{2.f, 2.f, 4.f, 4.f});
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_with_bias_2g2o2i1h1w) {
auto program = std::make_unique<ConvProgramStrategy>(
GenericInput(),
Data({2, 2, 2, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f}),
GenericOutput(),
std::vector<float>{2.f, 2.f, 4.f, 4.f},
2,
Data({2, 2, 1, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f}),
std::vector<float>{2.f, 2.f, 4.f, 4.f});
RunPassTest(std::move(program));
}
TEST_F(ParamsQuantizationOnednnPassTestFixture, conv_with_bias_2g2o2i1h1ws) {
auto program = std::make_unique<ConvProgramStrategy>(
GenericInput(),
Data({2, 2, 2, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f, 1.5f}),
GenericOutput(),
std::vector<float>{2.f, 2.f, 4.f, 4.f},
2,
Data({2, 2, 1, 1, 1}, {1.5f, 1.5f, 1.5f, 1.5f}),
std::vector<float>{2.f, 2.f, 4.f, 4.f},
true);
RunPassTest(std::move(program));
}
} // namespace
} // namespace paddle::framework::ir
USE_PASS(params_quantization_onednn_pass);
@@ -0,0 +1,84 @@
// Copyright (c) 2022 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 <vector>
#include "paddle/fluid/framework/ir/onednn/shuffle_channel_onednn_detect_pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle::framework::ir {
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
tensor->mutable_data<float>(phi::CPUPlace());
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope(param_scope, "prog_x", {1, 128, 52, 52});
return param_scope;
}
void MainTest() {
Layers layers;
auto prog_x = layers.data("prog_x", {1, 128, 52, 52});
auto first_reshape2 = layers.reshape2(prog_x, {-1, 2, 64, 52, 52}, true);
first_reshape2->SetShape({-1, 2, 64, 52, 52});
auto transpose2 = layers.transpose2(first_reshape2, {0, 2, 1, 3, 4}, true);
transpose2->SetShape({-1, 64, 2, 52, 52});
auto second_reshape2 = layers.reshape2(transpose2, {-1, 128, 52, 52}, true);
second_reshape2->SetShape({-1, 128, 52, 52});
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set("__param_scope__", CreateParamScope());
int added_nodes = 1; // shuffle_channel
int removed_nodes = 5; // 2 * reshape, reshape_out, transpose, transpose_out
int original_nodes_num = graph->Nodes().size();
auto pass =
PassRegistry::Instance().Get("shuffle_channel_onednn_detect_pass");
graph.reset(pass->Apply(graph.release()));
int current_nodes_num = graph->Nodes().size();
EXPECT_EQ(current_nodes_num,
original_nodes_num + added_nodes - removed_nodes);
EXPECT_EQ(GetNumOpNodes(graph, "reshape2"), 0);
EXPECT_EQ(GetNumOpNodes(graph, "transpose2"), 0);
EXPECT_EQ(GetNumOpNodes(graph, "shuffle_channel"), 1);
for (const auto* node : graph->Nodes()) {
if (node->IsOp() && node->Op()->Type() == "shuffle_channel") {
const auto* op = node->Op();
ASSERT_TRUE(op->HasAttr("use_mkldnn") || op->HasAttr("use_onednn"));
EXPECT_TRUE((op->HasAttr("use_mkldnn") &&
PADDLE_GET_CONST(bool, op->GetAttr("use_mkldnn"))) ||
(op->HasAttr("use_onednn") &&
PADDLE_GET_CONST(bool, op->GetAttr("use_onednn"))));
}
}
}
TEST(ShuffleChannelOneDNNDetectPass, ShuffleChannelOneDNNDetectPassTest) {
MainTest();
}
} // namespace paddle::framework::ir
USE_PASS(shuffle_channel_onednn_detect_pass);
@@ -0,0 +1,293 @@
/* Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/ir/op_compat_sensible_pass.h"
#include "gtest/gtest.h"
#include "paddle/fluid/framework/op_info.h"
#include "paddle/fluid/framework/program_desc.h"
namespace paddle::framework::ir {
TEST(OpCompatSensiblePass, compatOp) {
auto lambda = [](const std::string& str) { return str == "tanh"; };
OpCompat compat("fc_test");
compat.AddAttr("in_num_col_dims")
.IsIntIn({1, 2})
.IsNumLE(1)
.End()
.AddAttr("activation_type")
.IsStringIn({"tanh", "sigmoid"})
.IsStringMatch(lambda)
.End()
.AddAttr("test_attr")
.IsBoolEQ(true)
.End()
.AddInput("Input")
.IsTensor()
.End()
.AddInput("W")
.IsTensor()
.End()
.AddInput("Bias")
.IsTensor()
.IsOptional()
.End()
.AddInput("Test")
.IsOptional()
.End()
.AddOutput("Out")
.IsTensor()
.End();
OpDesc fc_op;
std::unordered_map<std::string, Attribute> attr_map;
attr_map["in_num_col_dims"] = 1;
attr_map["activation_type"] = std::string("tanh");
attr_map["test_attr"] = true;
fc_op.SetAttrMap(attr_map);
fc_op.SetInput("Input", std::vector<std::string>{"test_input"});
fc_op.SetInput("W", std::vector<std::string>{"test_input_0"});
fc_op.SetInput("Bias", std::vector<std::string>{"test_input_1"});
fc_op.SetOutput("Out", std::vector<std::string>{"test_output"});
OpInfo info;
info.proto_ = new proto::OpProto;
info.proto_->set_type("fc_test");
info.proto_->set_comment("");
auto* attr = info.proto_->add_attrs();
attr->set_name("in_num_col_dims");
attr = info.proto_->add_attrs();
attr->set_name("test_attr");
OpInfoMap::Instance().Insert("fc_test", info);
EXPECT_STREQ(compat.Name().c_str(), "fc_test");
EXPECT_TRUE(compat.Judge(fc_op, "test_pass"));
delete info.proto_;
OpInfoMap::Instance().mutable_map()->erase("fc_test");
}
TEST(OpCompatSensiblePass, compatOpAttribute) {
OpCompat compat("fc_test");
OpDesc fc_op;
std::unordered_map<std::string, Attribute> attr_map;
attr_map["in_num_col_dims"] = 1;
fc_op.SetAttrMap(attr_map);
OpInfo info;
info.proto_ = new proto::OpProto;
info.proto_->set_type("fc_test");
info.proto_->set_comment("");
auto* attr = info.proto_->add_attrs();
attr->set_name("in_num_col_dims");
info.checker_ = new OpAttrChecker();
OpInfoMap::Instance().Insert("fc_test", info);
EXPECT_FALSE(compat.Judge(fc_op, "test_pass"));
OpCompat compat_1("fc_test");
info.checker_->AddAttrChecker<int>("in_num_col_dims", nullptr).SetDefault(1);
EXPECT_TRUE(compat_1.Judge(fc_op, "test_pass"));
delete info.checker_;
delete info.proto_;
OpInfoMap::Instance().mutable_map()->erase("fc_test");
}
TEST(OpCompatSensiblePass, opDefNotFound) {
OpCompat compat("fc_test");
OpDesc fc_op;
OpInfo info;
info.proto_ = new proto::OpProto;
info.proto_->set_type("fc_test");
info.proto_->set_comment("");
OpInfoMap::Instance().Insert("fc_test", info);
compat.Judge(fc_op, "test_pass");
delete info.proto_;
OpInfoMap::Instance().mutable_map()->erase("fc_test");
}
TEST(OpCompatSensiblePass, compatOpAttributeOptional) {
OpCompat compat("fc_test");
compat.AddAttr("activation_type")
.IsOptional()
.IsStringIn({"tanh", "sigmoid"});
OpDesc fc_op;
OpInfo info;
info.proto_ = new proto::OpProto;
info.proto_->set_type("fc_test");
info.proto_->set_comment("");
auto* attr = info.proto_->add_attrs();
attr->set_name("activation_type");
OpInfoMap::Instance().Insert("fc_test", info);
EXPECT_TRUE(compat.Judge(fc_op, "test_pass"));
delete info.proto_;
OpInfoMap::Instance().mutable_map()->erase("fc_test");
}
TEST(OpCompatSensiblePass, compatOpInput) {
OpInfo info;
info.proto_ = new proto::OpProto;
info.proto_->set_type("fc_test");
info.proto_->set_comment("");
OpInfoMap::Instance().Insert("fc_test", info);
OpCompat compat("fc_test");
OpDesc fc_op;
fc_op.SetInput("Input", std::vector<std::string>{"test_input"});
EXPECT_FALSE(compat.Judge(fc_op, "test_pass"));
compat.AddInput("Input").IsTensor().End().AddInput("Bias").IsTensor().End();
EXPECT_FALSE(compat.Judge(fc_op, "test_pass"));
fc_op.SetInput("Bias", std::vector<std::string>{"test_input", ""});
EXPECT_FALSE(compat.Judge(fc_op, "test_pass"));
delete info.proto_;
OpInfoMap::Instance().mutable_map()->erase("fc_test");
}
TEST(OpCompatSensiblePass, compatOutput) {
OpInfo info;
info.proto_ = new proto::OpProto;
info.proto_->set_type("fc_test");
info.proto_->set_comment("");
OpInfoMap::Instance().Insert("fc_test", info);
OpCompat compat("fc_test");
OpDesc fc_op;
fc_op.SetOutput("Output", std::vector<std::string>{"test_output"});
EXPECT_FALSE(compat.Judge(fc_op, "test_pass"));
compat.AddOutput("Output")
.IsTensor()
.End()
.AddOutput("Output_2")
.IsTensor()
.End();
EXPECT_FALSE(compat.Judge(fc_op, "test_pass"));
fc_op.SetOutput("Output_2", std::vector<std::string>{"test_output", ""});
EXPECT_FALSE(compat.Judge(fc_op, "test_pass"));
delete info.proto_;
OpInfoMap::Instance().mutable_map()->erase("fc_test");
}
class OpCompatSensiblePassTest : public OpCompatSensiblePass {
public:
OpCompatSensiblePassTest();
bool TestIsCompat(const OpDesc& op_desc) { return IsCompat(op_desc); }
bool TestIsCompat(const GraphPatternDetector::subgraph_t& subgraph,
Graph* g) {
return IsCompat(subgraph, g);
}
};
OpCompatSensiblePassTest::OpCompatSensiblePassTest() {
AddOpCompat(OpCompat("fc_test"))
.AddAttr("in_num_col_dims")
.IsNumLE(1)
.End()
.AddAttr("activation_type")
.IsStringIn({"tanh", "sigmoid"})
.End()
.AddInput("Input")
.IsTensor()
.End()
.AddInput("W")
.IsTensor()
.End()
.AddInput("Bias")
.IsTensor()
.IsOptional()
.End()
.AddOutput("Out")
.IsTensor();
}
TEST(OpCompatSensiblePass, IsCompat) {
OpInfo info;
info.proto_ = new proto::OpProto;
info.proto_->set_type("fc_test");
info.proto_->set_comment("");
auto* attr = info.proto_->add_attrs();
attr->set_name("in_num_col_dims");
attr = info.proto_->add_attrs();
attr->set_name("activation_type");
OpInfoMap::Instance().Insert("fc_test", info);
OpCompatSensiblePassTest test;
OpDesc fc_op;
fc_op.SetType("fc_test");
std::unordered_map<std::string, Attribute> attr_map;
attr_map["in_num_col_dims"] = 1;
attr_map["activation_type"] = std::string("tanh");
fc_op.SetAttrMap(attr_map);
fc_op.SetInput("Input", std::vector<std::string>{"test_input"});
fc_op.SetInput("W", std::vector<std::string>{"test_input_0"});
fc_op.SetInput("Bias", std::vector<std::string>{"test_input_1"});
fc_op.SetOutput("Out", std::vector<std::string>{"test_output"});
EXPECT_TRUE(test.TestIsCompat(fc_op));
delete info.proto_;
OpInfoMap::Instance().mutable_map()->erase("fc_test");
}
TEST(OpCompatSensiblePass, IsCompatFail) {
OpInfo info;
info.proto_ = new proto::OpProto;
info.proto_->set_type("fc_test");
info.proto_->set_comment("");
auto* attr = info.proto_->add_attrs();
attr->set_name("activation_type");
attr = info.proto_->add_attrs();
attr->set_name("in_num_col_dims");
OpInfoMap::Instance().Insert("fc_test", info);
OpInfoMap::Instance().Insert("op2", info);
OpCompatSensiblePassTest test;
GraphPatternDetector::subgraph_t subgraph;
PDPattern pattern;
PDNode* pd_node = pattern.NewNode();
ProgramDesc prog;
Graph g(prog);
OpDesc fc_op;
std::unordered_map<std::string, Attribute> attr_map;
attr_map["in_num_col_dims"] = 1;
attr_map["activation_type"] = std::string("tanh");
fc_op.SetAttrMap(attr_map);
fc_op.SetType("fc_test");
subgraph[pd_node] = g.CreateOpNode(&fc_op);
EXPECT_FALSE(test.TestIsCompat(subgraph, &g));
fc_op.SetType("op2");
subgraph[pd_node] = g.CreateOpNode(&fc_op);
EXPECT_TRUE(test.TestIsCompat(subgraph, &g));
delete info.proto_;
OpInfoMap::Instance().mutable_map()->erase("fc_test");
OpInfoMap::Instance().mutable_map()->erase("op2");
}
} // namespace paddle::framework::ir
+288
View File
@@ -0,0 +1,288 @@
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
#include "paddle/fluid/framework/ir/pass.h"
#include <string>
#include "gtest/gtest.h"
namespace paddle::framework::ir {
class Graph;
class Node;
void BuildCircleGraph(Graph* g) {
ir::Node* o1 = g->CreateEmptyNode("op1", Node::Type::kOperation);
ir::Node* o2 = g->CreateEmptyNode("op2", Node::Type::kOperation);
ir::Node* v1 = g->CreateEmptyNode("var1", Node::Type::kVariable);
ir::Node* v2 = g->CreateEmptyNode("var2", Node::Type::kVariable);
o1->outputs.push_back(v1);
o2->inputs.push_back(v1);
v1->inputs.push_back(o1);
v1->outputs.push_back(o2);
o2->outputs.push_back(v2);
o1->inputs.push_back(v2);
v2->inputs.push_back(o2);
v2->outputs.push_back(o1);
}
class TestPass : public Pass {
protected:
void ApplyImpl(ir::Graph* graph) const override {
graph->Set<int>("copy_test_pass_attr", new int);
graph->Set<int>("copy_test_graph_attr", new int);
int test_pass_attr = this->Get<int>("test_pass_attr");
graph->Get<int>("copy_test_pass_attr") = test_pass_attr + 1;
int test_graph_attr = graph->Get<int>("test_graph_attr");
graph->Get<int>("copy_test_graph_attr") = test_graph_attr + 1;
}
};
TEST(PassTest, TestPassAttrCheck) {
ProgramDesc prog;
auto pass = PassRegistry::Instance().Get("test_pass");
std::unique_ptr<Graph> graph(new Graph(prog));
std::string exception;
try {
graph.reset(pass->Apply(graph.release()));
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
ASSERT_TRUE(exception.find("Required attribute test_pass_attr for pass < "
"test_pass > is not set") != exception.npos);
int val = 1;
graph = std::make_unique<Graph>(prog);
pass->SetNotOwned<int>("test_pass_attr", &val);
for (std::string try_type : {"bool", "const int", "std::string"}) {
try {
if (try_type == "bool") {
pass->Get<bool>("test_pass_attr");
} else if (try_type == "const int") {
pass->Get<const int>("test_pass_attr");
} else if (try_type == "std::string") {
pass->Get<std::string>("test_pass_attr");
}
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
std::string msg =
"Invalid type for attribute test_pass_attr, expected: " + try_type +
", actual: int";
ASSERT_TRUE(exception.find(msg) != exception.npos);
}
try {
graph.reset(pass->Apply(graph.release()));
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
ASSERT_TRUE(exception.find(
"Required attribute test_graph_attr for graph is not set") !=
exception.npos);
graph = std::make_unique<Graph>(prog);
graph->Set<int>("test_graph_attr", new int);
graph->Get<int>("test_graph_attr") = 1;
graph.reset(pass->Apply(graph.release()));
ASSERT_EQ(graph->Get<int>("copy_test_pass_attr"), 2);
ASSERT_EQ(graph->Get<int>("copy_test_graph_attr"), 2);
// Allow apply more than once.
graph = std::make_unique<Graph>(prog);
graph->Set<int>("test_graph_attr", new int);
graph.reset(pass->Apply(graph.release()));
pass = PassRegistry::Instance().Get("test_pass");
pass->SetNotOwned<int>("test_pass_attr", &val);
graph = std::make_unique<Graph>(prog);
BuildCircleGraph(graph.get());
graph->Set<int>("test_graph_attr", new int);
graph->Get<int>("test_graph_attr") = 2;
try {
pass->Apply(graph.release());
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
ASSERT_TRUE(exception.find("shouldn't contain cycle") != exception.npos);
pass = PassRegistry::Instance().Get("test_pass");
pass->Set<int>("test_pass_attr", new int);
try {
pass->Set<int>("test_pass_attr", new int);
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
ASSERT_TRUE(
exception.find("Attribute test_pass_attr already set in the pass") !=
exception.npos);
}
TEST(PassTest, TestPassAttrCheckConvertAllBlocks) {
// Set FLAGS_convert_all_blocks to true to make sure this test works.
bool flag_temp = FLAGS_convert_all_blocks;
FLAGS_convert_all_blocks = true;
ProgramDesc prog;
auto pass = PassRegistry::Instance().Get("test_pass");
std::unique_ptr<Graph> graph(new Graph(prog));
std::string exception;
try {
graph.reset(pass->Apply(graph.release()));
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
ASSERT_TRUE(exception.find("Required attribute test_pass_attr for pass < "
"test_pass > is not set") != exception.npos);
int val = 1;
graph = std::make_unique<Graph>(prog);
pass->SetNotOwned<int>("test_pass_attr", &val);
for (std::string try_type : {"bool", "const int", "std::string"}) {
try {
if (try_type == "bool") {
pass->Get<bool>("test_pass_attr");
} else if (try_type == "const int") {
pass->Get<const int>("test_pass_attr");
} else if (try_type == "std::string") {
pass->Get<std::string>("test_pass_attr");
}
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
std::string msg =
"Invalid type for attribute test_pass_attr, expected: " + try_type +
", actual: int";
ASSERT_TRUE(exception.find(msg) != exception.npos);
}
try {
graph.reset(pass->Apply(graph.release()));
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
ASSERT_TRUE(exception.find(
"Required attribute test_graph_attr for graph is not set") !=
exception.npos);
graph = std::make_unique<Graph>(prog);
graph->Set<int>("test_graph_attr", new int);
graph->Get<int>("test_graph_attr") = 1;
graph.reset(pass->Apply(graph.release()));
ASSERT_EQ(graph->Get<int>("copy_test_pass_attr"), 2);
ASSERT_EQ(graph->Get<int>("copy_test_graph_attr"), 2);
// Allow apply more than once.
graph = std::make_unique<Graph>(prog);
graph->Set<int>("test_graph_attr", new int);
graph.reset(pass->Apply(graph.release()));
pass = PassRegistry::Instance().Get("test_pass");
pass->SetNotOwned<int>("test_pass_attr", &val);
graph = std::make_unique<Graph>(prog);
BuildCircleGraph(graph.get());
graph->Set<int>("test_graph_attr", new int);
graph->Get<int>("test_graph_attr") = 2;
try {
pass->Apply(graph.release());
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
ASSERT_TRUE(exception.find("shouldn't contain cycle") != exception.npos);
pass = PassRegistry::Instance().Get("test_pass");
pass->Set<int>("test_pass_attr", new int);
try {
pass->Set<int>("test_pass_attr", new int);
} catch (paddle::platform::EnforceNotMet& e) {
exception = std::string(e.what());
}
ASSERT_TRUE(
exception.find("Attribute test_pass_attr already set in the pass") !=
exception.npos);
// Recover FLAGS_convert_all_blocks.
FLAGS_convert_all_blocks = flag_temp;
}
class TestPassWithDefault : public Pass {
protected:
void ApplyImpl(ir::Graph* graph) const override {
graph->Set<int>("copy_default_attr", new int);
int test_pass_attr = this->Get<int>("default_attr");
graph->Get<int>("copy_default_attr") = test_pass_attr + 1;
}
};
TEST(PassTest, TestPassDefaultAttrCheck) {
ProgramDesc prog;
// check if default value is set
auto pass = PassRegistry::Instance().Get("test_pass_default_attr");
std::unique_ptr<Graph> graph(new Graph(prog));
ASSERT_EQ(pass->Get<int>("default_attr"), 1);
graph.reset(pass->Apply(graph.release()));
ASSERT_EQ(graph->Get<int>("copy_default_attr"), 2);
// check if new value overrides default value
pass = PassRegistry::Instance().Get("test_pass_default_attr");
pass->Set<int>("default_attr", new int{3});
ASSERT_EQ(pass->Get<int>("default_attr"), 3);
}
TEST(PassTest, TestPassDefaultAttrCheckConvertAllBlocks) {
// Set FLAGS_convert_all_blocks to true to make sure this test works.
bool flag_temp = FLAGS_convert_all_blocks;
FLAGS_convert_all_blocks = true;
ProgramDesc prog;
// check if default value is set
auto pass = PassRegistry::Instance().Get("test_pass_default_attr");
std::unique_ptr<Graph> graph(new Graph(prog));
ASSERT_EQ(pass->Get<int>("default_attr"), 1);
graph.reset(pass->Apply(graph.release()));
ASSERT_EQ(graph->Get<int>("copy_default_attr"), 2);
// check if new value overrides default value
pass = PassRegistry::Instance().Get("test_pass_default_attr");
pass->Set<int>("default_attr", new int{3});
ASSERT_EQ(pass->Get<int>("default_attr"), 3);
// Recover FLAGS_convert_all_blocks.
FLAGS_convert_all_blocks = flag_temp;
}
TEST(PassTest, TestPassRegistrarDeconstructor) {
auto pass_registrary =
new PassRegistrar<paddle::framework::ir::TestPassWithDefault>(
"test_deconstructor");
pass_registrary->DefaultPassAttr("deconstructor_attr", new int{1});
pass_registrary->~PassRegistrar();
}
} // namespace paddle::framework::ir
REGISTER_PASS(test_pass, paddle::framework::ir::TestPass)
.RequirePassAttr("test_pass_attr")
.RequireGraphAttr("test_graph_attr");
REGISTER_PASS(test_pass_default_attr,
paddle::framework::ir::TestPassWithDefault)
.DefaultPassAttr("default_attr", new int{1});
@@ -0,0 +1,63 @@
// Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/fluid/framework/ir/memory_optimize_pass/reference_count_pass_helper.h"
#include <memory>
#include <vector>
#include "gtest/gtest.h"
#include "paddle/fluid/framework/ir/node.h"
#include "paddle/fluid/framework/var_desc.h"
#include "paddle/phi/common/place.h"
namespace paddle::framework::ir {
TEST(ReferenceCountPassHelperTest, TryGetLatestVarDescSkipsTrailingNullDesc) {
VarDesc old_var_desc("x_old");
VarDesc latest_var_desc("x_latest");
std::unique_ptr<Node> old_node(CreateNodeForTest(&old_var_desc));
std::unique_ptr<Node> latest_node(CreateNodeForTest(&latest_var_desc));
std::unique_ptr<Node> trailing_empty_node(
CreateNodeForTest("x_empty", Node::Type::kVariable));
// VarHandleBase registers itself as the Node wrapper; Node owns the wrapper.
auto* old_var_handle =
new details::VarHandle(old_node.get(), 0, 0, "x", phi::CPUPlace());
auto* latest_var_handle =
new details::VarHandle(latest_node.get(), 1, 0, "x", phi::CPUPlace());
auto* trailing_empty_var_handle = new details::VarHandle(
trailing_empty_node.get(), 2, 0, "x", phi::CPUPlace());
std::vector<details::VarHandle*> vars{
old_var_handle, latest_var_handle, trailing_empty_var_handle};
EXPECT_EQ(TryGetLatestVarDesc(vars)->Name(), "x_latest");
}
TEST(ReferenceCountPassHelperTest,
TryGetLatestVarDescReturnsNullptrWhenAbsent) {
std::unique_ptr<Node> empty_node(
CreateNodeForTest("x_empty", Node::Type::kVariable));
// VarHandleBase registers itself as the Node wrapper; Node owns the wrapper.
auto* empty_var_handle =
new details::VarHandle(empty_node.get(), 0, 0, "x", phi::CPUPlace());
std::vector<details::VarHandle*> vars{empty_var_handle};
EXPECT_EQ(TryGetLatestVarDesc(vars), nullptr);
}
} // namespace paddle::framework::ir
@@ -0,0 +1,66 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/platform/enforce.h"
namespace paddle::framework::ir {
template <typename T = float>
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims,
T value = 0) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
auto* cpu_ctx = static_cast<phi::CPUContext*>(
phi::DeviceContextPool::Instance().Get(phi::CPUPlace()));
auto* data = cpu_ctx->Alloc<T>(tensor);
for (int64_t i = 0; i < tensor->numel(); i++) {
data[i] = value;
}
}
TEST(Relu6FusePass, basic) {
Layers layers;
auto* in_x = layers.data("in_x", {1, 32, 112, 112});
auto* clip_min = layers.data("clip_x", {1}, true);
auto* clip_max = layers.data("clip_y", {1}, true);
layers.clip(in_x, clip_min, clip_max);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto* param_scope = new Scope();
graph->Set("__param_scope__", param_scope);
AddVarToScope(param_scope, clip_min->Name(), {1}, 0.f);
AddVarToScope(param_scope, clip_max->Name(), {1}, 6.f);
auto pass = PassRegistry::Instance().Get("relu6_fuse_pass");
VLOG(3) << DebugString(graph);
pass->Apply(graph.get());
VLOG(3) << DebugString(graph);
auto clip_num = GetNumOpNodes(graph, "clip");
PADDLE_ENFORCE_EQ(clip_num,
0,
common::errors::PreconditionNotMet(
"clip should be mapped to relu6 after pass."));
}
} // namespace paddle::framework::ir
USE_PASS(relu6_fuse_pass);
@@ -0,0 +1,81 @@
/* Copyright (c) 2019 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/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/ir/repeated_fc_relu_fuse_pass.h"
namespace paddle::framework {
class VarDesc;
} // namespace paddle::framework
namespace paddle::framework::ir {
void TestMain(int num_fc) {
// inputs operator output
// -------------------------------------------------------------
// (x, filters, bias_0) conv2d -> conv2d_out
// (conv2d_out, fc_weights_0, fc_bias_0) fc -> fc_out_0
// (fc_out_0, fc_weights_1, fc_bias_1) fc -> fc_out_1
// ...
Layers layers;
VarDesc* x = layers.data("x");
VarDesc* filters = layers.data("filters", {}, true);
VarDesc* bias_0 = layers.data("bias_0", {}, true);
VarDesc* conv2d_out = layers.conv2d(x, filters, bias_0);
VarDesc* fc_in = conv2d_out;
for (int i = 0; i < num_fc; ++i) {
VarDesc* weights_i =
layers.data("fc_weights_" + std::to_string(i), {}, true);
VarDesc* bias_i = layers.data("fc_bias_" + std::to_string(i), {}, true);
std::string activation_type = i < (num_fc - 1) ? "relu" : "";
VarDesc* fc_out = layers.fc(fc_in, weights_i, bias_i, 1, activation_type);
fc_in = fc_out;
}
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("repeated_fc_relu_fuse_pass");
int num_nodes_before = static_cast<int>(graph->Nodes().size());
int num_fc_nodes_before = GetNumOpNodes(graph, "fc");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fused_nodes_after = GetNumOpNodes(graph, "fusion_repeated_fc_relu");
VLOG(3) << DebugString(graph);
// Delete (num_fc_nodes_before - 1) fc ops
PADDLE_ENFORCE_EQ(
num_nodes_before - (num_fc_nodes_before - 1) + 1,
num_nodes_after,
common::errors::InvalidArgument(
"num_nodes_before = %d, num_fc_nodes_before = %d, num_nodes_after = "
"%d.",
num_nodes_before,
num_fc_nodes_before,
num_nodes_after));
PADDLE_ENFORCE_EQ(num_fused_nodes_after,
1,
common::errors::InvalidArgument(
"num_fused_nodes_after = %d.", num_fused_nodes_after));
}
TEST(RepeatedFCReluFusePass, basic_3) { TestMain(3); }
TEST(RepeatedFCReluFusePass, basic_9) { TestMain(9); }
} // namespace paddle::framework::ir
USE_PASS(repeated_fc_relu_fuse_pass);
@@ -0,0 +1,216 @@
// Copyright (c) 2018 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/fluid/framework/ir/seqpool_concat_fuse_pass.h"
#include "paddle/fluid/framework/op_proto_maker.h"
namespace paddle::framework::ir {
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
if (type == "sequence_pool") {
op->SetInput("X", {inputs[0]});
std::string pooltype = "SUM";
op->SetAttr("pooltype", pooltype);
op->SetOutput("MaxIndex", {outputs[0]});
op->SetOutput("Out", {outputs[1]});
} else if (type == "concat") {
op->SetInput("X", inputs);
op->SetAttr("axis", 1);
op->SetOutput("Out", {outputs[0]});
} else {
op->SetInput("X", inputs);
op->SetOutput("Out", outputs);
}
op->SetAttr(OpProtoAndCheckerMaker::OpRoleAttrName(),
static_cast<int>(OpRole::kForward));
}
int CountOpType(const ir::Graph* graph,
const std::string& op_type = "fusion_seqpool_concat") {
int count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp() && node->Op()->Type() == op_type) {
++count;
}
}
return count;
}
std::unique_ptr<ir::Graph> GetNumNodesOfBeforeAfter(
std::unique_ptr<ir::Graph> graph,
int* before,
int* after,
const std::string& pass_type = "seqpool_concat_fuse_pass") {
auto pass = PassRegistry::Instance().Get(pass_type);
*before = static_cast<int>(graph->Nodes().size());
graph.reset(pass->Apply(graph.release()));
*after = static_cast<int>(graph->Nodes().size());
return graph;
}
/*
* Before fuse:
* a b c
* | | |
* op1 op2 op3
* / \ / \ / \
* d e f g h i
* \ | /
* concat
* |
* j
* Type of op1, op2 and op3 are sequence_pool, with "SUM" pooltype attr
*
* After fuse:
* a b c
* \ | /
* fusion_seqpool_concat
* |
* j
*/
TEST(SeqPoolConcatFusePass, basic) {
ProgramDesc prog;
for (auto& v : std::vector<std::string>(
{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"})) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::DENSE_TENSOR);
}
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"a"}),
std::vector<std::string>({"d", "e"}));
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"b"}),
std::vector<std::string>({"f", "g"}));
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"c"}),
std::vector<std::string>({"h", "i"}));
SetOp(&prog,
"concat",
std::vector<std::string>({"e", "g", "i"}),
std::vector<std::string>({"j"}));
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
int before = 0, after = 0;
graph = GetNumNodesOfBeforeAfter(std::move(graph), &before, &after);
// Remove 10 Nodes: op1, op2, op3, d, e, f, g, h, i, concat_op
// Add 1 Node: fusion_seqpool_concat
EXPECT_EQ(after, before - 9);
EXPECT_EQ(CountOpType(graph.get()), 1);
}
/*
* Before fuse:
* a b
* | / \
* op1 op2 op3
* / \ / \ \
* c d e f g
* \ /
* concat
* |
* h
* Type of op1 and op2 are sequence_pool, with "SUM" pooltype attr
*
* After fuse:
* a b
* \ / \
* fusion_seqpool_concat op3
* | |
* h g
*/
TEST(SeqPoolConcatFusePass, advanced) {
ProgramDesc prog;
for (auto& v :
std::vector<std::string>({"a", "b", "c", "d", "e", "f", "g", "h"})) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::DENSE_TENSOR);
}
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"a"}),
std::vector<std::string>({"c", "d"}));
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"b"}),
std::vector<std::string>({"e", "f"}));
SetOp(&prog,
"op3",
std::vector<std::string>({"b"}),
std::vector<std::string>({"g"}));
SetOp(&prog,
"concat",
std::vector<std::string>({"d", "f"}),
std::vector<std::string>({"h"}));
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
int before = 0, after = 0;
graph = GetNumNodesOfBeforeAfter(std::move(graph), &before, &after);
// Remove 7 Nodes: op1, op2, c, d, e, f concat_op
// Add 1 Node: fusion_seqpool_concat
EXPECT_EQ(after, before - 6);
EXPECT_EQ(CountOpType(graph.get()), 1);
}
ProgramDesc BuildProgramDesc(int num_inputs_of_concat) {
ProgramDesc prog;
auto new_var = [&](const std::string& name) {
auto* var = prog.MutableBlock(0)->Var(name);
var->SetType(proto::VarType::DENSE_TENSOR);
};
std::vector<std::string> concat_inputs;
for (int i = 0; i < num_inputs_of_concat; ++i) {
std::string prefix = "seqpool_op_" + std::to_string(i);
new_var(prefix + "in");
new_var(prefix + "out");
new_var(prefix + "out_unused");
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({prefix + "in"}),
std::vector<std::string>({prefix + "out", prefix + "out_unused"}));
concat_inputs.push_back(prefix + "out");
}
SetOp(
&prog, "concat", concat_inputs, std::vector<std::string>({"concat_out"}));
return prog;
}
// test more inputs of concat
TEST(SeqPoolConcatFusePass, more_inputs) {
for (int num : {1, 2, 10}) {
ProgramDesc prog = BuildProgramDesc(num);
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
int before = 0, after = 0;
graph = GetNumNodesOfBeforeAfter(std::move(graph), &before, &after);
// Remove Nodes: n * (seqpool_op, out, out_unused), and concat_op
// Add Node: fusion_seqpool_concat op
EXPECT_EQ(after, before - num * 3);
EXPECT_EQ(CountOpType(graph.get()), 1);
}
}
} // namespace paddle::framework::ir
USE_PASS(seqpool_concat_fuse_pass);
@@ -0,0 +1,278 @@
// Copyright (c) 2018 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/fluid/framework/ir/seqpool_cvm_concat_fuse_pass.h"
#include "paddle/fluid/framework/op_proto_maker.h"
namespace paddle::framework::ir {
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
if (type == "sequence_pool") {
op->SetInput("X", {inputs[0]});
std::string pooltype = "SUM";
op->SetAttr("pooltype", pooltype);
op->SetOutput("MaxIndex", {outputs[0]});
op->SetOutput("Out", {outputs[1]});
} else if (type == "concat") {
op->SetInput("X", inputs);
op->SetAttr("axis", 1);
op->SetOutput("Out", {outputs[0]});
} else if (type == "cvm") {
op->SetInput("X", {inputs[0]});
op->SetInput("CVM", {inputs[1]});
op->SetOutput("Y", {outputs[0]});
op->SetAttr("use_cvm", true);
} else {
op->SetInput("X", inputs);
op->SetOutput("Out", outputs);
}
op->SetAttr(OpProtoAndCheckerMaker::OpRoleAttrName(),
static_cast<int>(OpRole::kForward));
}
int CountOpType(const ir::Graph* graph,
const std::string& op_type = "fusion_seqpool_cvm_concat") {
int count = 0;
for (auto* node : graph->Nodes()) {
if (node->IsOp() && node->Op()->Type() == op_type) {
++count;
}
}
return count;
}
std::unique_ptr<ir::Graph> GetNumNodesOfBeforeAfter(
std::unique_ptr<ir::Graph> graph,
int* before,
int* after,
const std::string& pass_type = "seqpool_cvm_concat_fuse_pass") {
auto pass = PassRegistry::Instance().Get(pass_type);
*before = static_cast<int>(graph->Nodes().size());
graph.reset(pass->Apply(graph.release()));
*after = static_cast<int>(graph->Nodes().size());
return graph;
}
/*
* Before fuse:
*
*
* a b c
* | | |
* op1 op2 op3
* / \ / \ / \
* d e n f g n h i n
* | / | / | /
* op4 op5 op6
* | | |
j k l
* \ | /
* concat
* |
* m
*
* Type of op1, op2 and op3 are sequence_pool, with "SUM" pooltype attr.
* Type of op4, op5 and op6 are cvm, with use_cvm is true.
*
* After fuse:
* a b c n
* \ | | /
* fusion_seqpool_cvm_concat
* |
* m
*/
TEST(SeqPoolCVMConcatFusePass, basic) {
ProgramDesc prog;
for (auto& v : std::vector<std::string>({"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n"})) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::DENSE_TENSOR);
}
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"a"}),
std::vector<std::string>({"d", "e"}));
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"b"}),
std::vector<std::string>({"f", "g"}));
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"c"}),
std::vector<std::string>({"h", "i"}));
SetOp(&prog,
"cvm",
std::vector<std::string>({"e", "n"}),
std::vector<std::string>({"j"}));
SetOp(&prog,
"cvm",
std::vector<std::string>({"g", "n"}),
std::vector<std::string>({"k"}));
SetOp(&prog,
"cvm",
std::vector<std::string>({"i", "n"}),
std::vector<std::string>({"l"}));
SetOp(&prog,
"concat",
std::vector<std::string>({"j", "k", "l"}),
std::vector<std::string>({"m"}));
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
int before = 0, after = 0;
graph = GetNumNodesOfBeforeAfter(std::move(graph), &before, &after);
// Remove 16 Nodes: op1, op2, op3, op4, op5, op6, d, e, f, g, h, i, j, k, l,
// concat_op
// Add 1 Node: fusion_seqpool_cvm_concat
EXPECT_EQ(after, before - 15);
EXPECT_EQ(CountOpType(graph.get()), 1);
}
/*
* Before fuse:
* a b
* | / \
* op1 k op2 k op3
* / \ / / \ / \
* c d e f g
* | |
* op4 op5
* | |
* h i
* \ /
* concat
* |
* j
* Type of op1 and op2 are sequence_pool, with "SUM" pooltype attr.
* Type of op4 and op5 are cvm, with use_cvm is true.
*
* After fuse:
* a k b
* \ | / \
* fusion_seqpool_cvm_concat op3
* | |
* j g
*/
TEST(SeqPoolCVMConcatFusePass, advanced) {
ProgramDesc prog;
for (auto& v : std::vector<std::string>(
{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"})) {
auto* var = prog.MutableBlock(0)->Var(v);
var->SetType(proto::VarType::DENSE_TENSOR);
}
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"a"}),
std::vector<std::string>({"c", "d"}));
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({"b"}),
std::vector<std::string>({"e", "f"}));
SetOp(&prog,
"op3",
std::vector<std::string>({"b"}),
std::vector<std::string>({"g"}));
SetOp(&prog,
"cvm",
std::vector<std::string>({"d", "k"}),
std::vector<std::string>({"h"}));
SetOp(&prog,
"cvm",
std::vector<std::string>({"f", "k"}),
std::vector<std::string>({"i"}));
SetOp(&prog,
"concat",
std::vector<std::string>({"h", "i"}),
std::vector<std::string>({"j"}));
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
int before = 0, after = 0;
graph = GetNumNodesOfBeforeAfter(std::move(graph), &before, &after);
// Remove 11 Nodes: op1, op2, op4, op5, c, d, e, f, h, i, concat_op
// Add 1 Node: fusion_seqpool_cvm_concat
EXPECT_EQ(after, before - 10);
EXPECT_EQ(CountOpType(graph.get()), 1);
}
ProgramDesc BuildProgramDesc(int num_inputs_of_concat) {
ProgramDesc prog;
auto new_var = [&](const std::string& name) {
auto* var = prog.MutableBlock(0)->Var(name);
var->SetType(proto::VarType::DENSE_TENSOR);
};
std::vector<std::string> concat_inputs;
new_var("cvm_in");
for (int i = 0; i < num_inputs_of_concat; ++i) {
std::string seqpool_prefix = "seqpool_op_" + std::to_string(i);
new_var(seqpool_prefix + "in");
new_var(seqpool_prefix + "out");
new_var(seqpool_prefix + "out_unused");
SetOp(&prog,
"sequence_pool",
std::vector<std::string>({seqpool_prefix + "in"}),
std::vector<std::string>(
{seqpool_prefix + "out_unused", seqpool_prefix + "out"}));
std::string cvm_prefix = "cvm_op_" + std::to_string(i);
new_var(cvm_prefix + "out");
SetOp(&prog,
"cvm",
std::vector<std::string>({seqpool_prefix + "out", "cvm_in"}),
std::vector<std::string>({cvm_prefix + "out"}));
concat_inputs.push_back(cvm_prefix + "out");
}
SetOp(
&prog, "concat", concat_inputs, std::vector<std::string>({"concat_out"}));
return prog;
}
// test more inputs of concat
TEST(SeqPoolCVMConcatFusePass, more_inputs) {
for (int num : {1, 2, 10}) {
ProgramDesc prog = BuildProgramDesc(num);
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
int before = 0, after = 0;
graph = GetNumNodesOfBeforeAfter(std::move(graph), &before, &after);
// Remove Nodes: n * (seqpool_op, seqpool_out, out_unused, cvm_op, cvm_out),
// and concat_op
// Add Node: fusion_seqpool_cvm_concat op
EXPECT_EQ(after, before - num * 5);
EXPECT_EQ(CountOpType(graph.get()), 1);
}
}
} // namespace paddle::framework::ir
USE_PASS(seqpool_cvm_concat_fuse_pass);
@@ -0,0 +1,90 @@
/* Copyright (c) 2019 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/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/ir/simplify_with_basic_ops_pass.h"
namespace paddle::framework::ir {
TEST(SimplifyWithBasicOpsPass, dropout) {
for (std::string dropout_implementation :
{"downgrade_in_infer", "upscale_in_train"}) {
for (auto inplace : {false, true}) {
if (dropout_implementation == "downgrade_in_infer" && inplace == true) {
continue;
}
LOG(INFO) << "dropout_implementation: " << dropout_implementation
<< ", inplace: " << inplace;
Layers layers;
// (x, y) -> mul -> tmp_0
// (tmp_0) -> dropout -> (tmp_1)
// (tmp_1, z) -> elementwise_add -> (tmp_2)
// or
// (tmp_1, z) -> elementwise_add -> (tmp_0)
auto* x = layers.data("x");
auto* y = layers.data("y");
auto* z = layers.data("z");
auto* mul_out = layers.mul(x, y);
auto* dropout_out = layers.dropout(mul_out, 0.5f, dropout_implementation);
if (inplace) {
layers.elementwise_add(dropout_out, z, mul_out);
} else {
layers.elementwise_add(dropout_out, z);
}
std::unique_ptr<Graph> graph(new Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("simplify_with_basic_ops_pass");
int num_dropout_nodes_before = GetNumOpNodes(graph, "dropout");
int num_scale_nodes_before = GetNumOpNodes(graph, "scale");
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_dropout_nodes_after = GetNumOpNodes(graph, "dropout");
int num_scale_nodes_after = GetNumOpNodes(graph, "scale");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(
num_dropout_nodes_after,
0,
common::errors::InvalidArgument("num_dropout_nodes_after = %d.",
num_dropout_nodes_after));
if (dropout_implementation == "downgrade_in_infer") {
PADDLE_ENFORCE_EQ(
num_dropout_nodes_before,
num_scale_nodes_after - num_scale_nodes_before,
common::errors::InvalidArgument(
"num_dropout_nodes_before = %d, num_scale_nodes_after = %d, "
"num_scale_nodes_before = %d.",
num_dropout_nodes_before,
num_scale_nodes_after,
num_scale_nodes_before));
} else {
PADDLE_ENFORCE_EQ(
num_scale_nodes_after - num_scale_nodes_before,
0,
common::errors::InvalidArgument(
"num_scale_nodes_after = %d, num_scale_nodes_before = %d.",
num_scale_nodes_after,
num_scale_nodes_before));
}
}
}
}
} // namespace paddle::framework::ir
USE_PASS(simplify_with_basic_ops_pass);
@@ -0,0 +1,68 @@
/* Copyright (c) 2019 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/fluid/framework/ir/pass_tester_helper.h"
#include "paddle/fluid/framework/ir/skip_layernorm_fuse_pass.h"
#include "paddle/fluid/framework/op_version_registry.h"
namespace paddle::framework::ir {
TEST(SkipLayerNormFusePass, basic) {
// inputs operator output
// --------------------------------------------------------------------
// (x, y) elementwise_add -> elementwise_out
// (elementwise_out, scale, bias) layer_norm -> layer_norm_out...
Layers layers;
auto* x = layers.data("x", {128, 768});
auto* y = layers.data("y", {128, 768});
auto* elementwise_out = layers.elementwise_add(x, y);
auto* scale = layers.data("scale", {768}, true);
auto* bias = layers.data("bias", {768}, true);
layers.layer_norm(elementwise_out, scale, bias);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
graph->Set(kEmbEltwiseLayernormPass, new bool(true));
graph->Set(kMultiheadMatmulPass, new bool(true));
auto pass = PassRegistry::Instance().Get("skip_layernorm_fuse_pass");
int num_nodes_before = static_cast<int>(graph->Nodes().size());
VLOG(3) << DebugString(graph);
graph.reset(pass->Apply(graph.release()));
int num_nodes_after = static_cast<int>(graph->Nodes().size());
int num_fused_nodes_after = GetNumOpNodes(graph, "skip_layernorm");
VLOG(3) << DebugString(graph);
PADDLE_ENFORCE_EQ(num_nodes_before,
num_nodes_after + 4,
common::errors::PreconditionNotMet(
"The number of nodes before and after the fuse does "
"not meet expectations"));
PADDLE_ENFORCE_EQ(
num_fused_nodes_after,
1,
common::errors::PreconditionNotMet(
"The number of fusion nodes does not meet expectations after fuse"));
}
TEST(SkipLayerNormFusePass, pass_op_version_check) {
ASSERT_TRUE(
paddle::framework::compatible::PassVersionCheckerRegistrar::GetInstance()
.IsPassCompatible("skip_layernorm_fuse_pass"));
}
} // namespace paddle::framework::ir
USE_PASS(skip_layernorm_fuse_pass);
@@ -0,0 +1,95 @@
// Copyright (c) 2019 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 <string>
#include "paddle/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/program_desc.h"
#include "paddle/fluid/platform/enforce.h"
namespace paddle::framework::ir {
void SetOp(ProgramDesc* prog,
const std::string& type,
const std::string& name,
const std::vector<std::string>& inputs,
const std::vector<std::string>& outputs) {
auto* op = prog->MutableBlock(0)->AppendOp();
op->SetType(type);
op->SetAttr("name", name);
op->SetInput("X", inputs);
op->SetOutput("Out", outputs);
}
// (a, conv_w)->conv2d->b
// (b, bn_scale, bn_bias, mean, var)->batch_norm
// ->(c, mean, var, save_mean, save_inv_var)
ProgramDesc BuildProgramDesc() {
ProgramDesc prog;
for (auto& v : std::vector<std::string>({"a",
"conv_w",
"b",
"bn_scale",
"bn_bias",
"mean",
"var",
"c",
"save_mean",
"save_inv_var"})) {
auto* var = prog.MutableBlock(0)->Var(v);
if (v == "conv_w" || v == "bn_scale" || v == "bn_bias" || v == "mean" ||
v == "var") {
var->SetPersistable(true);
}
}
SetOp(&prog,
"conv2d",
"conv",
std::vector<std::string>({"a", "conv_w"}),
std::vector<std::string>({"b"}));
SetOp(&prog,
"batch_norm",
"bn",
std::vector<std::string>({"b", "bn_scale", "bn_bias", "mean", "var"}),
std::vector<std::string>(
{"c", "mean", "var", "save_mean", "save_inv_var"}));
return prog;
}
TEST(IsTestPass, basic) {
auto prog = BuildProgramDesc();
std::unique_ptr<ir::Graph> graph(new ir::Graph(prog));
auto pass = PassRegistry::Instance().Get("sync_batch_norm_pass");
graph.reset(pass->Apply(graph.release()));
for (auto* node : graph->Nodes()) {
if (node->IsOp()) {
auto* op = node->Op();
auto op_name = PADDLE_GET_CONST(std::string, op->GetAttr("name"));
if (op_name == "bn") {
ASSERT_EQ(op->Type(), "sync_batch_norm");
}
}
}
}
} // namespace paddle::framework::ir
USE_PASS(sync_batch_norm_pass);
@@ -0,0 +1,81 @@
# XPU IR Pass Tests
cc_test(
test_cast_mixed_precision_op_fuse_pass
SRCS cast_mixed_precision_op_fuse_pass_test.cc
DEPS cast_mixed_precision_op_fuse_pass)
cc_test(
test_delete_isolated_node_pass
SRCS delete_isolated_node_pass_test.cc
DEPS delete_isolated_node_pass)
cc_test(
test_fused_multi_transformer_xpu_pass
SRCS fused_multi_transformer_xpu_pass_test.cc
DEPS fused_multi_transformer_xpu_pass)
cc_test(
test_fused_multi_transformer_int8_xpu_quant_pass
SRCS fused_multi_transformer_int8_xpu_quant_pass_test.cc
DEPS fused_multi_transformer_int8_xpu_quant_pass)
cc_test(
test_one_beam_size_fuse_pass
SRCS one_beam_size_fuse_pass_test.cc
DEPS one_beam_size_fuse_pass)
cc_test(
test_stack_fuse_pass
SRCS stack_fuse_pass_test.cc
DEPS stack_fuse_pass)
cc_test(
test_fused_multi_transformer_cachekv_layout_trans_pass
SRCS fused_multi_transformer_cachekv_layout_trans_pass_test.cc
DEPS fused_multi_transformer_cachekv_layout_trans_pass)
cc_test(
test_fused_multi_transformer_int8_cachekv_layout_trans_pass
SRCS fused_multi_transformer_int8_cachekv_layout_trans_pass_test.cc
DEPS fused_multi_transformer_int8_cachekv_layout_trans_pass)
cc_test(
test_multi_encoder_xpu_adaptive_seqlen_fuse_pass
SRCS multi_encoder_xpu_adaptive_seqlen_fuse_pass_test.cc
DEPS multi_encoder_xpu_adaptive_seqlen_fuse_pass)
cc_test(
test_xpu_delete_cast_op_pass
SRCS xpu_delete_cast_op_pass_test.cc
DEPS xpu_delete_cast_op_pass)
cc_test(
test_fold_interp_outsize_fuse_pass
SRCS fold_interp_outsize_fuse_pass_test.cc
DEPS fold_interp_outsize_fuse_pass)
cc_test(
test_fold_two_squeeze2_fuse_pass
SRCS fold_two_squeeze2_fuse_pass_test.cc
DEPS fold_two_squeeze2_fuse_pass)
cc_test(
test_matmul_weight_trans_pass
SRCS matmul_weight_trans_pass_test.cc
DEPS matmul_weight_trans_pass)
cc_test(
test_reshape2_matmul_xpu_fuse_pass
SRCS reshape2_matmul_xpu_fuse_pass_test.cc
DEPS reshape2_matmul_xpu_fuse_pass)
cc_test(
test_fast_where_xpu_fuse_pass
SRCS fast_where_xpu_fuse_pass_test.cc
DEPS fast_where_xpu_fuse_pass)
cc_test(
test_squeeze_excitation_fuse_pass
SRCS squeeze_excitation_fuse_pass_test.cc
DEPS squeeze_excitation_fuse_pass)
@@ -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 <gtest/gtest.h>
#include "paddle/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
namespace ir {
TEST(CastMixedPrecisionOpFusePass, cast_before) {
Layers layers;
auto* block = layers.Block();
auto* cast_in = layers.data("cast_in");
auto* cast_out = layers.cast(cast_in, 5, 4);
OpDesc* conv2d_xpu = block->AppendOp();
conv2d_xpu->SetType("conv2d_xpu");
conv2d_xpu->SetInput("x", {cast_out->Name()});
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("cast_mixed_precision_op_fuse_pass");
pass->Apply(graph.get());
auto num = GetNumOpNodes(graph, "cast");
PADDLE_ENFORCE_EQ(
num,
0,
common::errors::PreconditionNotMet(
"cast op should be removed from graph, but graph still has %d ops.",
num));
}
TEST(CastMixedPrecisionOpFusePass, cast_after) {
Layers layers;
auto* block = layers.Block();
auto* cast_in = layers.data("cast_in");
OpDesc* conv2d_xpu = block->AppendOp();
conv2d_xpu->SetType("conv2d_xpu");
conv2d_xpu->SetOutput("out", {cast_in->Name()});
layers.cast(cast_in, 4, 5);
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("cast_mixed_precision_op_fuse_pass");
pass->Apply(graph.get());
auto num = GetNumOpNodes(graph, "cast");
PADDLE_ENFORCE_EQ(
num,
0,
common::errors::PreconditionNotMet(
"cast op should be removed from graph, but graph still has %d ops.",
num));
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(cast_mixed_precision_op_fuse_pass);
@@ -0,0 +1,181 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
namespace ir {
VarDesc* Data(paddle::framework::BlockDesc* block,
std::string name,
std::vector<int64_t> shape = {},
bool is_persistable = false,
proto::VarType::Type data_type = proto::VarType::FP32) {
auto* var = block->Var(name);
var->SetType(proto::VarType::DENSE_TENSOR);
var->SetDataType(data_type);
var->SetShape(shape);
var->SetPersistable(is_persistable);
return var;
}
void AddVarToScope(Scope* param_scope,
const std::string& name,
const DDim& dims) {
auto* tensor = param_scope->Var(name)->GetMutable<phi::DenseTensor>();
tensor->Resize(dims);
auto* cpu_ctx = static_cast<phi::CPUContext*>(
phi::DeviceContextPool::Instance().Get(phi::CPUPlace()));
auto* data = cpu_ctx->Alloc<float>(tensor);
int64_t numel = tensor->numel();
for (int64_t i = 0; i < numel; ++i) {
data[i] = 1;
}
}
Scope* CreateParamScope() {
auto param_scope = new Scope();
AddVarToScope(param_scope, "matmul0_w", {128, 128});
return param_scope;
}
int WeightNodeNum(ir::Graph* graph) {
int num = 0;
for (auto node : graph->Nodes()) {
if (node->IsVar() && node->Var()->Persistable()) {
num++;
}
}
return num;
}
int WeightTensorNum(Scope* scope) {
int num = 0;
auto vars = scope->LocalVars();
for (auto* var : vars) {
if (var->Get<phi::DenseTensor>().numel() > 0) {
num++;
}
}
return num;
}
TEST(delete_isolated_node_pass, basic) {
paddle::framework::ProgramDesc program;
auto* block0 = program.MutableBlock(0);
auto* block1 = program.AppendBlock(*block0);
auto* matmul0_x = Data(block0, "matmul0_x", {1, 128});
auto* matmul0_w = Data(block0, "matmul0_w", {128, 128}, true);
auto* matmul0_out = Data(block0, "matmul0_out", {1, 128});
OpDesc* matmul_op = block0->AppendOp();
matmul_op->SetType("matmul_v2");
matmul_op->SetInput("X", {matmul0_x->Name()});
matmul_op->SetInput("Y", {matmul0_w->Name()});
matmul_op->SetAttr("trans_x", false);
matmul_op->SetAttr("trans_y", false);
matmul_op->SetOutput("Out", {matmul0_out->Name()});
auto* while_out = Data(block0, "while_out", {1, 128});
auto* while_step_scopes = Data(block0, "while_step_scopes");
auto* while_cond = Data(block0, "while_cond");
OpDesc* while_op = block0->AppendOp();
while_op->SetType("while");
while_op->SetInput("X", {matmul0_w->Name(), matmul0_out->Name()});
while_op->SetInput("Condition", {while_cond->Name()});
while_op->SetOutput("Out", {while_out->Name()});
while_op->SetOutput("StepScopes", {while_step_scopes->Name()});
while_op->SetAttr("sub_block", {block1});
while_op->SetAttr("is_test", true);
auto* matmul1_x = Data(block1, matmul0_out->Name(), matmul0_out->GetShape());
auto* matmul1_w =
Data(block1, matmul0_w->Name(), matmul0_w->GetShape(), true);
auto* matmul1_out = Data(block1, "matmul1_out", {1, 128});
OpDesc* matmul1_op = block1->AppendOp();
matmul1_op->SetType("matmul_v2");
matmul1_op->SetInput("X", {matmul1_x->Name()});
matmul1_op->SetInput("Y", {matmul1_w->Name()});
matmul1_op->SetAttr("trans_x", false);
matmul1_op->SetAttr("trans_y", false);
matmul1_op->SetOutput("Out", {matmul1_out->Name()});
std::unique_ptr<ir::Graph> graph(new ir::Graph(program));
auto* scope = CreateParamScope();
graph->Set("__param_scope__", scope);
auto pass0 = PassRegistry::Instance().Get("fc_xpu_fuse_pass");
pass0->Apply(graph.get());
pass0->Apply(graph->GetSubGraph(1));
int weight_node_num =
WeightNodeNum(graph.get()) + WeightNodeNum(graph->GetSubGraph(1));
PADDLE_ENFORCE_EQ(weight_node_num,
6,
common::errors::PreconditionNotMet(
"Graph should have 6 weight node after "
"fc_xpu_fuse_pass, but actually has %d.",
weight_node_num));
auto pass1 = PassRegistry::Instance().Get("delete_isolated_node_pass");
pass1->Apply(graph.get());
weight_node_num =
WeightNodeNum(graph.get()) + WeightNodeNum(graph->GetSubGraph(1));
PADDLE_ENFORCE_EQ(weight_node_num,
4,
common::errors::PreconditionNotMet(
"Graph should have 4 weight node after "
"delete_isolated_node_pass, but actually has %d.",
weight_node_num));
int weight_tensor_num = WeightTensorNum(scope);
PADDLE_ENFORCE_EQ(weight_tensor_num,
2,
common::errors::PreconditionNotMet(
"Scope should have 2 weight tensor after "
"delete_isolated_node_pass, but actually has %d.",
weight_tensor_num));
for (auto* node : graph->Nodes()) {
if (node->IsOp() && node->Op()->Type() == "while") {
auto while_in_names = node->Op()->Inputs().at("X");
PADDLE_ENFORCE_EQ(while_in_names.size(),
3,
common::errors::PreconditionNotMet(
"While op should have 3 input after "
"delete_isolated_node_pass, but actually has %d.",
while_in_names.size()));
}
}
Scope& scope0 = graph->Get<framework::Scope>("__param_scope__");
Scope& scope1 =
graph->GetSubGraph(1)->Get<framework::Scope>("__param_scope__");
std::vector<std::string> shared_weight_names{matmul0_w->Name() + "_int16",
matmul0_w->Name() + "_max"};
for (auto name : shared_weight_names) {
auto* var0 = scope0.FindVar(name);
auto* var1 = scope1.FindVar(name);
PADDLE_ENFORCE(
var0 == var1,
common::errors::PreconditionNotMet(
"Variables with the same name in two scopes is different."));
}
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(delete_isolated_node_pass);
@@ -0,0 +1,304 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
namespace ir {
#define APPLY_PASS \
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program())); \
auto pass = PassRegistry::Instance().Get("fast_where_xpu_fuse_pass"); \
pass->Apply(graph.get());
#define VERIFY_GRAPH(x, y) \
auto num_op_nodes = GetNumOpNodes(graph); \
PADDLE_ENFORCE_EQ( \
num_op_nodes, \
1, \
common::errors::PreconditionNotMet( \
"The graph contains only one op node, but %d op nodes found.", \
num_op_nodes)); \
auto fast_where_xpu_op_nodes = GetOpNodes(graph, "fast_where_xpu"); \
PADDLE_ENFORCE_EQ(fast_where_xpu_op_nodes.size(), \
1, \
common::errors::PreconditionNotMet( \
"The graph contains only a fast_where_xpu op node, " \
"but %d op nodes found.", \
fast_where_xpu_op_nodes.size())); \
const auto& x_name = fast_where_xpu_op_nodes[0]->Op()->Input("x")[0]; \
PADDLE_ENFORCE_EQ(x_name, \
#x, \
common::errors::PreconditionNotMet( \
"The input 'x' of fast_where_xpu op should be '%s', " \
"but receive '%s'.", \
#x, \
x_name)); \
const auto& y_name = fast_where_xpu_op_nodes[0]->Op()->Input("y")[0]; \
PADDLE_ENFORCE_EQ(y_name, \
#y, \
common::errors::PreconditionNotMet( \
"The input 'y' of fast_where_xpu op should be '%s', " \
"but receive '%s'.", \
#y, \
y_name));
TEST(FastWhereXPUFusePass, one_case0) {
Layers layers;
auto* condition =
layers.data("condition", {20, 1}, false, proto::VarType::BOOL);
auto* x = layers.data("x", {20, 7});
auto* y = layers.data("y", {20, 7});
auto* cast_out = layers.cast(condition, 0, 5);
cast_out->SetShape({20, 1});
auto* scale_out = layers.scale(cast_out, -1.0f, 1.0f, true);
scale_out->SetShape({20, 1});
auto* mul0_out = layers.elementwise_mul(x, scale_out);
mul0_out->SetShape({20, 7});
auto* mul1_out = layers.elementwise_mul(y, cast_out);
mul1_out->SetShape({20, 7});
auto* add_out = layers.elementwise_add(mul0_out, mul1_out);
add_out->SetShape({20, 7});
APPLY_PASS
VERIFY_GRAPH(y, x)
}
TEST(FastWhereXPUFusePass, one_case1) {
Layers layers;
auto* condition =
layers.data("condition", {20, 1}, false, proto::VarType::BOOL);
auto* x = layers.data("x", {20, 7});
auto* y = layers.data("y", {20, 7});
auto* cast_out = layers.cast(condition, 0, 5);
cast_out->SetShape({20, 1});
auto* mul0_out = layers.elementwise_mul(x, cast_out);
mul0_out->SetShape({20, 7});
auto* scale_out = layers.scale(cast_out, -1.0f, 1.0f, true);
scale_out->SetShape({20, 1});
auto* mul1_out = layers.elementwise_mul(y, scale_out);
mul1_out->SetShape({20, 7});
auto* add_out = layers.elementwise_add(mul0_out, mul1_out);
add_out->SetShape({20, 7});
APPLY_PASS
VERIFY_GRAPH(x, y)
}
TEST(FastWhereXPUFusePass, one_case2) {
Layers layers;
auto* condition =
layers.data("condition", {20, 1}, false, proto::VarType::BOOL);
auto* x = layers.data("x", {20, 7});
auto* y = layers.data("y", {20, 7});
auto* cast_out = layers.cast(condition, 0, 5);
cast_out->SetShape({20, 1});
auto* scale_out = layers.scale(cast_out, -1.0f, 1.0f, true);
scale_out->SetShape({20, 1});
auto* mul0_out = layers.elementwise_mul(scale_out, x);
mul0_out->SetShape({20, 7});
auto* mul1_out = layers.elementwise_mul(cast_out, y);
mul1_out->SetShape({20, 7});
auto* add_out = layers.elementwise_add(mul0_out, mul1_out);
add_out->SetShape({20, 7});
APPLY_PASS
VERIFY_GRAPH(y, x)
}
TEST(FastWhereXPUFusePass, one_case3) {
Layers layers;
auto* condition =
layers.data("condition", {20, 1}, false, proto::VarType::BOOL);
auto* x = layers.data("x", {20, 7});
auto* y = layers.data("y", {20, 7});
auto* cast_out = layers.cast(condition, 0, 5);
cast_out->SetShape({20, 1});
auto* mul0_out = layers.elementwise_mul(cast_out, x);
mul0_out->SetShape({20, 7});
auto* scale_out = layers.scale(cast_out, -1.0f, 1.0f, true);
scale_out->SetShape({20, 1});
auto* mul1_out = layers.elementwise_mul(scale_out, y);
mul1_out->SetShape({20, 7});
auto* add_out = layers.elementwise_add(mul0_out, mul1_out);
add_out->SetShape({20, 7});
APPLY_PASS
VERIFY_GRAPH(x, y)
}
TEST(FastWhereXPUFusePass, one_case4) {
Layers layers;
auto* condition =
layers.data("condition", {20, 1}, false, proto::VarType::BOOL);
auto* x = layers.data("x", {20, 7});
auto* y = layers.data("y", {20, 7});
auto* cast_out = layers.cast(condition, 0, 5);
cast_out->SetShape({20, 1});
auto* scale_out = layers.scale(cast_out, -1.0f, 1.0f, true);
scale_out->SetShape({20, 1});
auto* mul0_out = layers.elementwise_mul(scale_out, x);
mul0_out->SetShape({20, 7});
auto* mul1_out = layers.elementwise_mul(y, cast_out);
mul1_out->SetShape({20, 7});
auto* add_out = layers.elementwise_add(mul0_out, mul1_out);
add_out->SetShape({20, 7});
APPLY_PASS
VERIFY_GRAPH(y, x)
}
TEST(FastWhereXPUFusePass, one_case5) {
Layers layers;
auto* condition =
layers.data("condition", {20, 1}, false, proto::VarType::BOOL);
auto* x = layers.data("x", {20, 7});
auto* y = layers.data("y", {20, 7});
auto* cast_out = layers.cast(condition, 0, 5);
cast_out->SetShape({20, 1});
auto* mul0_out = layers.elementwise_mul(cast_out, x);
mul0_out->SetShape({20, 7});
auto* scale_out = layers.scale(cast_out, -1.0f, 1.0f, true);
scale_out->SetShape({20, 1});
auto* mul1_out = layers.elementwise_mul(y, scale_out);
mul1_out->SetShape({20, 7});
auto* add_out = layers.elementwise_add(mul0_out, mul1_out);
add_out->SetShape({20, 7});
APPLY_PASS
VERIFY_GRAPH(x, y)
}
#undef VERIFY_GRAPH
#define VERIFY_GRAPH(logical_op, x, y) \
auto num_op_nodes = GetNumOpNodes(graph); \
PADDLE_ENFORCE_EQ( \
num_op_nodes, \
2, \
common::errors::PreconditionNotMet( \
"The graph contains only two op nodes, but %d op nodes found.", \
num_op_nodes)); \
auto logical_op_nodes = GetOpNodes(graph, #logical_op); \
PADDLE_ENFORCE_EQ( \
logical_op_nodes.size(), \
1, \
common::errors::PreconditionNotMet( \
"The graph contains only a '%s' op node, but %d op nodes found.", \
#logical_op, \
logical_op_nodes.size())); \
auto fast_where_xpu_op_nodes = GetOpNodes(graph, "fast_where_xpu"); \
PADDLE_ENFORCE_EQ(fast_where_xpu_op_nodes.size(), \
1, \
common::errors::PreconditionNotMet( \
"The graph contains only a fast_where_xpu op node, " \
"but %d op nodes found.", \
fast_where_xpu_op_nodes.size())); \
const auto& x_name = fast_where_xpu_op_nodes[0]->Op()->Input("x")[0]; \
PADDLE_ENFORCE_EQ(x_name, \
#x, \
common::errors::PreconditionNotMet( \
"The input 'x' of fast_where_xpu op should be '%s', " \
"but receive '%s'.", \
#x, \
x_name)); \
const auto& y_name = fast_where_xpu_op_nodes[0]->Op()->Input("y")[0]; \
PADDLE_ENFORCE_EQ(y_name, \
#y, \
common::errors::PreconditionNotMet( \
"The input 'y' of fast_where_xpu op should be '%s', " \
"but receive '%s'.", \
#y, \
y_name));
TEST(FastWhereXPUFusePass, cascade_case0) {
Layers layers;
auto* condition0 =
layers.data("condition0", {20, 1}, false, proto::VarType::BOOL);
auto* condition1 =
layers.data("condition1", {20, 1}, false, proto::VarType::BOOL);
auto* x = layers.data("x", {20, 7});
auto* y = layers.data("y", {20, 7});
// fast_where_xpu0
auto* cast0_out = layers.cast(condition0, 0, 5);
cast0_out->SetShape({20, 1});
auto* mul0_out = layers.elementwise_mul(cast0_out, x);
mul0_out->SetShape({20, 7});
auto* scale0_out = layers.scale(cast0_out, -1.0f, 1.0f, true);
scale0_out->SetShape({20, 1});
auto* mul1_out = layers.elementwise_mul(scale0_out, y);
mul1_out->SetShape({20, 7});
auto* add0_out = layers.elementwise_add(mul0_out, mul1_out);
add0_out->SetShape({20, 7});
// fast_where_xpu1
auto* cast1_out = layers.cast(condition1, 0, 5);
cast1_out->SetShape({20, 1});
auto* mul2_out = layers.elementwise_mul(cast1_out, x);
mul2_out->SetShape({20, 7});
auto* scale1_out = layers.scale(cast1_out, -1.0f, 1.0f, true);
scale1_out->SetShape({20, 1});
auto* mul3_out = layers.elementwise_mul(scale1_out, add0_out);
mul3_out->SetShape({20, 7});
auto* add1_out = layers.elementwise_add(mul2_out, mul3_out);
add1_out->SetShape({20, 7});
APPLY_PASS
VERIFY_GRAPH(logical_or, x, y)
}
TEST(FastWhereXPUFusePass, cascade_case1) {
Layers layers;
auto* condition0 =
layers.data("condition0", {20, 1}, false, proto::VarType::BOOL);
auto* condition1 =
layers.data("condition1", {20, 1}, false, proto::VarType::BOOL);
auto* x = layers.data("x", {20, 7});
auto* y = layers.data("y", {20, 7});
// fast_where_xpu0
auto* cast0_out = layers.cast(condition0, 0, 5);
cast0_out->SetShape({20, 1});
auto* mul0_out = layers.elementwise_mul(cast0_out, x);
mul0_out->SetShape({20, 7});
auto* scale0_out = layers.scale(cast0_out, -1.0f, 1.0f, true);
scale0_out->SetShape({20, 1});
auto* mul1_out = layers.elementwise_mul(scale0_out, y);
mul1_out->SetShape({20, 7});
auto* add0_out = layers.elementwise_add(mul0_out, mul1_out);
add0_out->SetShape({20, 7});
// fast_where_xpu1
auto* cast1_out = layers.cast(condition1, 0, 5);
cast1_out->SetShape({20, 1});
auto* mul2_out = layers.elementwise_mul(cast1_out, add0_out);
mul2_out->SetShape({20, 7});
auto* scale1_out = layers.scale(cast1_out, -1.0f, 1.0f, true);
scale1_out->SetShape({20, 1});
auto* mul3_out = layers.elementwise_mul(scale1_out, y);
mul3_out->SetShape({20, 7});
auto* add1_out = layers.elementwise_add(mul2_out, mul3_out);
add1_out->SetShape({20, 7});
APPLY_PASS
VERIFY_GRAPH(logical_and, x, y)
}
#undef APPLY_PASS
#undef VERIFY_GRAPH
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(fast_where_xpu_fuse_pass);
@@ -0,0 +1,58 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
namespace ir {
TEST(FoldInterpOutsizeFusePass, basic) {
Layers layers;
auto* block = layers.Block();
auto* shape_x = layers.data("shape_x", {1, 18, 288, 288});
auto* concat_y =
layers.data("concat_y", {576, 576}, true, proto::VarType::INT64);
auto* shape_out = layers.shape(shape_x);
auto* cast1_out = layers.cast(shape_out, 2, 3);
auto* slice_out = layers.slice(cast1_out, {0}, {0}, {2});
auto* concat_out = layers.concat({slice_out, concat_y}, 0);
auto split_outs = layers.split(concat_out, 0, 0, {2, 2});
auto* split_out_1 = split_outs[1];
auto* cast2_out = layers.cast(split_out_1, 3, 2);
OpDesc* bilinear_interp_v2_op = block->AppendOp();
bilinear_interp_v2_op->SetType("bilinear_interp_v2");
bilinear_interp_v2_op->SetInput("X", {shape_x->Name()});
bilinear_interp_v2_op->SetInput("OutSize", {cast2_out->Name()});
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("fold_interp_outsize_fuse_pass");
pass->Apply(graph.get());
auto ops_num = GetNumOpNodes(graph);
PADDLE_ENFORCE_EQ(
ops_num,
1,
common::errors::PreconditionNotMet(
"graph should only have 2 op nodes, but received %d.", ops_num));
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(fold_interp_outsize_fuse_pass);
@@ -0,0 +1,45 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
namespace ir {
TEST(FoldTwoSqueeze2FusePass, basic) {
Layers layers;
auto* in_x = layers.data("in_x", {64, 1, 74, 1});
auto* squeeze2_1_out = layers.squeeze2(in_x, std::vector<int>{3});
layers.squeeze2(squeeze2_1_out, std::vector<int>{1});
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get("fold_two_squeeze2_fuse_pass");
pass->Apply(graph.get());
auto ops_num = GetNumOpNodes(graph);
PADDLE_ENFORCE_EQ(
ops_num,
1,
common::errors::PreconditionNotMet(
"graph should only have 2 op nodes, but received %d.", ops_num));
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(fold_two_squeeze2_fuse_pass);
@@ -0,0 +1,188 @@
// 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/fluid/framework/ir/pass.h"
#include "paddle/fluid/framework/ir/pass_tester_helper.h"
namespace paddle {
namespace framework {
namespace ir {
VarDesc* Data(paddle::framework::BlockDesc* block,
std::string name,
std::vector<int64_t> shape = {},
bool is_persistable = false,
proto::VarType::Type data_type = proto::VarType::FP32) {
auto* var = block->Var(name);
var->SetType(proto::VarType::DENSE_TENSOR);
var->SetDataType(data_type);
var->SetShape(shape);
var->SetPersistable(is_persistable);
return var;
}
VarDesc* fill_constant(BlockDesc* block, std::vector<VarDesc*> shapes) {
VarDesc* out = Data(block, shapes[0]->Name() + "_out");
OpDesc* op = block->AppendOp();
op->SetType("fill_constant");
std::vector<std::string> shape_names;
for (auto shape : shapes) {
shape_names.push_back(shape->Name());
}
op->SetInput("ShapeTensorList", {shape_names});
op->SetOutput("Out", {out->Name()});
return out;
}
TEST(FillConstantReshapePass, basic) {
paddle::framework::ProgramDesc program;
auto* block = program.MutableBlock(0);
auto* shape0 = Data(block, "shape0");
auto* shape1 = Data(block, "shape1");
auto* shape2 = Data(block, "shape2");
auto* shape3 = Data(block, "shape3");
auto* shape4 = Data(block, "shape4");
auto* shape5 = Data(block, "shape5");
auto* shape6 = Data(block, "shape6");
auto* shape7 = Data(block, "shape7");
auto* shape8 = Data(block, "shape8");
auto* shape9 = Data(block, "shape9");
auto* fill0 = fill_constant(block, {shape0, shape1, shape2, shape3, shape4});
fill0->SetShape({1, 2, 3, 4, 5});
auto* fill1 = fill_constant(block, {shape5, shape6, shape7, shape8, shape9});
fill1->SetShape({1, 2, 3, 4, 5});
OpDesc* fused_multi_transformer = block->AppendOp();
fused_multi_transformer->SetType("fused_multi_transformer");
fused_multi_transformer->SetInput("CacheKV", {fill0->Name(), fill1->Name()});
std::unique_ptr<ir::Graph> graph(new ir::Graph(program));
auto pass = PassRegistry::Instance().Get(
"fused_multi_transformer_cachekv_layout_trans_pass");
pass->Apply(graph.get());
auto fills = GetOpNodes(graph, "fill_constant");
auto fill0_in_names = fills[0]->Op()->Input("ShapeTensorList");
std::vector<std::string> expect_fill0_out_names{
"shape5", "shape6", "shape7", "shape8", "shape9"};
std::vector<std::string> expect_fill1_out_names{
"shape0", "shape1", "shape2", "shape3", "shape4"};
PADDLE_ENFORCE_EQ(fill0_in_names,
expect_fill0_out_names,
common::errors::PreconditionNotMet(
"fill_constant name should not be updated."));
auto fill1_in_names = fills[1]->Op()->Input("ShapeTensorList");
PADDLE_ENFORCE_EQ(fill1_in_names,
expect_fill1_out_names,
common::errors::PreconditionNotMet(
"fill_constant name should not be updated."));
}
TEST(GatherReshapePass, basic) {
Layers layers;
auto* gather0_x = layers.data("gather0_x", {2, 1, 24, 512, 64});
auto* gather0_index = layers.data("gather0_index", {1});
auto* gather0_out = layers.gather(gather0_x, gather0_index, 1);
gather0_out->SetShape({2, 1, 24, 512, 64});
auto* gather1_x = layers.data("gather1_x", {2, 1, 24, 512, 64});
auto* gather1_index = layers.data("gather1_index", {1});
auto* gather1_out = layers.gather(gather1_x, gather1_index, 1);
gather1_out->SetShape({2, 1, 24, 512, 64});
auto* block = layers.Block();
OpDesc* fused_multi_transformer = block->AppendOp();
fused_multi_transformer->SetType("fused_multi_transformer");
fused_multi_transformer->SetInput("CacheKV",
{gather0_out->Name(), gather1_out->Name()});
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get(
"fused_multi_transformer_cachekv_layout_trans_pass");
pass->Apply(graph.get());
auto gathers = GetOpNodes(graph, "gather");
for (auto* gather : gathers) {
PADDLE_ENFORCE_EQ(gather->Op()->GetAttrIfExists<int>("axis"),
1,
common::errors::PreconditionNotMet(
"gather's axis attr should not be updated by pass."));
}
}
TEST(FillConstantAndGatherReshapePass, basic) {
Layers layers;
auto* block = layers.Block();
auto* shape0 = Data(block, "shape0");
auto* shape1 = Data(block, "shape1");
auto* shape2 = Data(block, "shape2");
auto* shape3 = Data(block, "shape3");
auto* shape4 = Data(block, "shape4");
auto* shape5 = Data(block, "shape5");
auto* shape6 = Data(block, "shape6");
auto* shape7 = Data(block, "shape7");
auto* shape8 = Data(block, "shape8");
auto* shape9 = Data(block, "shape9");
auto* fill0 = fill_constant(block, {shape0, shape1, shape2, shape3, shape4});
fill0->SetShape({1, 2, 3, 4, 5});
auto* fill1 = fill_constant(block, {shape5, shape6, shape7, shape8, shape9});
fill1->SetShape({1, 2, 3, 4, 5});
OpDesc* fused_multi_transformer = block->AppendOp();
fused_multi_transformer->SetType("fused_multi_transformer");
fused_multi_transformer->SetInput("CacheKV", {fill0->Name(), fill1->Name()});
auto* gather0_x = layers.data("gather0_x", {2, 1, 24, 512, 64});
auto* gather0_index = layers.data("gather0_index", {1});
auto* gather0_out = layers.gather(gather0_x, gather0_index, 1);
gather0_out->SetShape({2, 1, 24, 512, 64});
auto* gather1_x = layers.data("gather1_x", {2, 1, 24, 512, 64});
auto* gather1_index = layers.data("gather1_index", {1});
auto* gather1_out = layers.gather(gather1_x, gather1_index, 1);
gather1_out->SetShape({2, 1, 24, 512, 64});
OpDesc* fused_multi_transformer1 = block->AppendOp();
fused_multi_transformer1->SetType("fused_multi_transformer");
fused_multi_transformer1->SetInput(
"CacheKV", {gather0_out->Name(), gather1_out->Name()});
std::unique_ptr<ir::Graph> graph(new ir::Graph(layers.main_program()));
auto pass = PassRegistry::Instance().Get(
"fused_multi_transformer_cachekv_layout_trans_pass");
pass->Apply(graph.get());
auto fills = GetOpNodes(graph, "fill_constant");
auto fill0_in_names = fills[0]->Op()->Input("ShapeTensorList");
std::vector<std::string> expect_fill0_out_names{
"shape0", "shape3", "shape1", "shape2", "shape4"};
std::vector<std::string> expect_fill1_out_names{
"shape5", "shape8", "shape6", "shape7", "shape9"};
PADDLE_ENFORCE_EQ(fill0_in_names,
expect_fill0_out_names,
common::errors::PreconditionNotMet(
"fill_constant name should be updated."));
auto fill1_in_names = fills[1]->Op()->Input("ShapeTensorList");
PADDLE_ENFORCE_EQ(fill1_in_names,
expect_fill1_out_names,
common::errors::PreconditionNotMet(
"fill_constant name should be updated."));
auto gathers = GetOpNodes(graph, "gather");
for (auto* gather : gathers) {
PADDLE_ENFORCE_EQ(
gather->Op()->GetAttrIfExists<int>("axis"),
2,
common::errors::PreconditionNotMet(
"gather's axis attr should be updated to 2 by pass."));
}
}
} // namespace ir
} // namespace framework
} // namespace paddle
USE_PASS(fused_multi_transformer_cachekv_layout_trans_pass);

Some files were not shown because too many files have changed in this diff Show More