chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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()
|
||||
@@ -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>();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
¤t_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, ¤t_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);
|
||||
+383
@@ -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
|
||||
@@ -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);
|
||||
+188
@@ -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
Reference in New Issue
Block a user