[GPU][CPU][Transformations] Reuse MoveEltwiseUpThroughDataMov transform (#23312)
### Details: - Moved `MoveEltwiseUpThroughDataMov` transformation from CPU plugin to common scope - Relaxed precision restrictions in this transform and keep old one in CPU callback only - Added an ability to customize list of allowed ops for `MoveEltwiseUpThroughDataMov` pass - Enable this pass in GPU pipeline in addition to internal pass. cldnn pass can't be fully replaced for now as for some ops we may create multiple cldnn primitives which happens after `MoveEltwiseUpThroughDataMov` pass is called
This commit is contained in:
parent
ab5166ffe5
commit
efa2debea2
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (C) 2018-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/core/type.hpp"
|
||||
#include "openvino/pass/graph_rewrite.hpp"
|
||||
#include "transformations_visibility.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace pass {
|
||||
|
||||
class TRANSFORMATIONS_API MoveEltwiseUpThroughDataMov : public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("MoveEltwiseUpThroughDataMov", "0");
|
||||
MoveEltwiseUpThroughDataMov(std::vector<DiscreteTypeInfo> allowed_data_movement_ops = get_default_allowed_ops());
|
||||
|
||||
private:
|
||||
static std::vector<DiscreteTypeInfo> get_default_allowed_ops();
|
||||
};
|
||||
|
||||
} // namespace pass
|
||||
} // namespace ov
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (C) 2018-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "transformations/common_optimizations/move_eltwise_up_data_movement.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <openvino/opsets/opset8.hpp>
|
||||
|
||||
#include "itt.hpp"
|
||||
#include "openvino/core/rt_info.hpp"
|
||||
#include "openvino/core/type.hpp"
|
||||
#include "openvino/pass/pattern/op/wrap_type.hpp"
|
||||
#include "transformations/utils/utils.hpp"
|
||||
|
||||
namespace {
|
||||
bool is_data_movement_operation(const std::shared_ptr<ov::Node>& node,
|
||||
const std::vector<ov::DiscreteTypeInfo>& allowed_data_movement_ops) {
|
||||
for (auto& allowed_type : allowed_data_movement_ops) {
|
||||
if (node->get_type_info().is_castable(allowed_type))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_scalar_like(const std::shared_ptr<ov::Node>& node) {
|
||||
auto constant_op = std::dynamic_pointer_cast<ov::opset8::Constant>(node);
|
||||
return constant_op != nullptr && shape_size(constant_op->get_shape()) == 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::vector<ov::DiscreteTypeInfo> ov::pass::MoveEltwiseUpThroughDataMov::get_default_allowed_ops() {
|
||||
return {
|
||||
ov::op::v0::Squeeze::get_type_info_static(),
|
||||
ov::op::v0::Unsqueeze::get_type_info_static(),
|
||||
ov::op::v1::Reshape::get_type_info_static(),
|
||||
ov::op::v1::Transpose::get_type_info_static(),
|
||||
ov::op::v0::ShuffleChannels::get_type_info_static(),
|
||||
ov::op::v7::Roll::get_type_info_static(),
|
||||
ov::op::v0::ReverseSequence::get_type_info_static(),
|
||||
ov::op::v0::DepthToSpace::get_type_info_static(),
|
||||
ov::op::v1::BatchToSpace::get_type_info_static(),
|
||||
ov::op::v1::Broadcast::get_type_info_static(),
|
||||
ov::op::v3::Broadcast::get_type_info_static(),
|
||||
ov::op::v1::Gather::get_type_info_static(),
|
||||
ov::op::v7::Gather::get_type_info_static(),
|
||||
ov::op::v8::Gather::get_type_info_static(),
|
||||
};
|
||||
}
|
||||
|
||||
ov::pass::MoveEltwiseUpThroughDataMov::MoveEltwiseUpThroughDataMov(
|
||||
std::vector<DiscreteTypeInfo> allowed_data_movement_ops) {
|
||||
MATCHER_SCOPE(MoveEltwiseUpThroughDataMov);
|
||||
auto eltwise_pattern = ov::pass::pattern::wrap_type<ov::op::util::UnaryElementwiseArithmetic,
|
||||
ov::op::util::BinaryElementwiseArithmetic,
|
||||
ov::op::v0::FakeQuantize>(ov::pass::pattern::has_static_rank());
|
||||
|
||||
ov::matcher_pass_callback callback = [OV_CAPTURE_CPY_AND_THIS](ov::pass::pattern::Matcher& m) {
|
||||
const auto& pattern_map = m.get_pattern_value_map();
|
||||
|
||||
auto eltwise = pattern_map.at(eltwise_pattern).get_node_shared_ptr();
|
||||
if (transformation_callback(eltwise)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eltwise->get_output_target_inputs(0).size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < eltwise->get_input_size(); ++i) {
|
||||
if (!is_scalar_like(eltwise->get_input_node_shared_ptr(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto current = eltwise->get_input_node_shared_ptr(0);
|
||||
auto child = eltwise;
|
||||
|
||||
while (is_data_movement_operation(current, allowed_data_movement_ops)) {
|
||||
if (current->get_output_size() != 1 || current->get_output_target_inputs(0).size() != 1 ||
|
||||
current->get_output_element_type(0) != current->get_input_element_type(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
child = current;
|
||||
current = current->get_input_node_shared_ptr(0);
|
||||
}
|
||||
|
||||
// now current is the first not data movement op
|
||||
if (child == eltwise) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eltwise constant shape should match new input shape
|
||||
for (size_t i = 1; i < eltwise->get_input_size(); i++) {
|
||||
if (current->get_output_partial_shape(0).size() != eltwise->get_input_partial_shape(i).size()) {
|
||||
auto old_eltwise_const = ov::as_type_ptr<ov::opset8::Constant>(eltwise->get_input_node_shared_ptr(i));
|
||||
if (old_eltwise_const->get_shape().size() != 0) {
|
||||
auto new_constant = std::make_shared<ov::opset8::Constant>(*old_eltwise_const.get(), ov::Shape{});
|
||||
ov::replace_node_update_name(old_eltwise_const, new_constant);
|
||||
}
|
||||
}
|
||||
}
|
||||
ov::replace_output_update_name(eltwise->output(0), eltwise->input_value(0));
|
||||
|
||||
ov::OutputVector eltwise_inputs = eltwise->input_values();
|
||||
eltwise_inputs[0] = child->input_value(0);
|
||||
auto new_eltwise = eltwise->clone_with_new_inputs(eltwise_inputs);
|
||||
// WA: it's necessary to set empty friendly name here
|
||||
// to avoid name duplication in TypeRelaxed cases
|
||||
new_eltwise->set_friendly_name("");
|
||||
ov::copy_runtime_info(eltwise, new_eltwise);
|
||||
|
||||
ov::OutputVector child_inputs = child->input_values();
|
||||
child_inputs[0] = new_eltwise;
|
||||
auto new_child = child->clone_with_new_inputs(child_inputs);
|
||||
ov::copy_runtime_info(child, new_child);
|
||||
new_child->set_friendly_name(child->get_friendly_name());
|
||||
|
||||
ov::replace_node(child, new_child);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto m = std::make_shared<ov::pass::pattern::Matcher>(eltwise_pattern, matcher_name);
|
||||
register_matcher(m, callback);
|
||||
}
|
||||
|
|
@ -2,20 +2,21 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "transformations/common_optimizations/move_eltwise_up_data_movement.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <openvino/core/model.hpp>
|
||||
#include <openvino/opsets/opset8.hpp>
|
||||
#include "ov_ops/type_relaxed.hpp"
|
||||
#include <transformations/init_node_info.hpp>
|
||||
#include <openvino/pass/manager.hpp>
|
||||
|
||||
#include "common_test_utils/ov_test_utils.hpp"
|
||||
#include <transformations/cpu_opset/common/pass/move_eltwise_up_data_movement.hpp>
|
||||
#include "ov_ops/type_relaxed.hpp"
|
||||
#include "transformations/init_node_info.hpp"
|
||||
|
||||
using namespace testing;
|
||||
|
||||
class MoveEltwiseUpThroughDataMovTest: public TransformationTestsF{};
|
||||
class MoveEltwiseUpThroughDataMovTest : public TransformationTestsF {};
|
||||
|
||||
TEST_F(MoveEltwiseUpThroughDataMovTest, SingleUnaryEltwise) {
|
||||
const ov::Shape shape{1, 3, 224, 224};
|
||||
|
|
@ -24,7 +25,8 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, SingleUnaryEltwise) {
|
|||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(input, transpose_const);
|
||||
|
||||
auto unsqueeze_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {unsqueeze_axis});
|
||||
|
|
@ -33,14 +35,15 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, SingleUnaryEltwise) {
|
|||
auto sigmoid = std::make_shared<ov::opset8::Sigmoid>(unsqueeze);
|
||||
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{sigmoid}, ov::ParameterVector{input});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
||||
auto sigmoid = std::make_shared<ov::opset8::Sigmoid>(input);
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(sigmoid, transpose_const);
|
||||
|
||||
auto unsqueeze_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {unsqueeze_axis});
|
||||
|
|
@ -65,7 +68,7 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, TypeRelaxedEltwise) {
|
|||
auto multiply = std::make_shared<ov::op::TypeRelaxed<ov::opset8::Multiply>>(transpose, mul_const);
|
||||
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{multiply}, ov::ParameterVector{input});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
|
@ -74,11 +77,11 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, TypeRelaxedEltwise) {
|
|||
auto mul_const = ov::opset8::Constant::create(ov::element::f32, {}, {2.f});
|
||||
auto multiply = std::make_shared<ov::op::TypeRelaxed<ov::opset8::Multiply>>(intermediate_op, mul_const);
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(multiply, transpose_const);
|
||||
|
||||
model_ref =
|
||||
std::make_shared<ov::Model>(ov::NodeVector{transpose}, ov::ParameterVector{input});
|
||||
model_ref = std::make_shared<ov::Model>(ov::NodeVector{transpose}, ov::ParameterVector{input});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +95,8 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, EltwiseSequence) {
|
|||
|
||||
auto matmul = std::make_shared<ov::opset8::MatMul>(input_left, input_right);
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(matmul, transpose_const);
|
||||
|
||||
auto relu = std::make_shared<ov::opset8::Relu>(transpose);
|
||||
|
|
@ -103,7 +107,7 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, EltwiseSequence) {
|
|||
auto sigmoid = std::make_shared<ov::opset8::Sigmoid>(unsqueeze);
|
||||
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{sigmoid}, ov::ParameterVector{input_left, input_right});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
{
|
||||
auto input_left = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
|
@ -115,13 +119,15 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, EltwiseSequence) {
|
|||
|
||||
auto sigmoid = std::make_shared<ov::opset8::Sigmoid>(relu);
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(sigmoid, transpose_const);
|
||||
|
||||
auto unsqueeze_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {unsqueeze_axis});
|
||||
auto unsqueeze = std::make_shared<ov::opset8::Unsqueeze>(transpose, unsqueeze_const);
|
||||
|
||||
model_ref = std::make_shared<ov::Model>(ov::NodeVector{unsqueeze}, ov::ParameterVector{input_left, input_right});
|
||||
model_ref =
|
||||
std::make_shared<ov::Model>(ov::NodeVector{unsqueeze}, ov::ParameterVector{input_left, input_right});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +153,7 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, DataMovementTwoConsumers) {
|
|||
auto relu = std::make_shared<ov::opset8::Relu>(transpose);
|
||||
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{sigmoid, relu}, ov::ParameterVector{input_left, input_right});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
|
||||
TEST_F(MoveEltwiseUpThroughDataMovTest, SingleBinaryEltwiseWithScalarOnSecondBranch) {
|
||||
|
|
@ -158,23 +164,29 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, SingleBinaryEltwiseWithScalarOnSecondBra
|
|||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(input, transpose_const);
|
||||
|
||||
auto unsqueeze_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {unsqueeze_axis});
|
||||
auto unsqueeze = std::make_shared<ov::opset8::Unsqueeze>(transpose, unsqueeze_const);
|
||||
|
||||
auto add = std::make_shared<ov::opset8::Add>(unsqueeze, ov::opset8::Constant::create(ov::element::f32, {}, {scalar_value}));
|
||||
auto add =
|
||||
std::make_shared<ov::opset8::Add>(unsqueeze,
|
||||
ov::opset8::Constant::create(ov::element::f32, {}, {scalar_value}));
|
||||
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{add}, ov::ParameterVector{input});
|
||||
}
|
||||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
||||
auto add = std::make_shared<ov::opset8::Add>(input, ov::opset8::Constant::create(ov::element::f32, {}, {scalar_value}));
|
||||
auto add =
|
||||
std::make_shared<ov::opset8::Add>(input,
|
||||
ov::opset8::Constant::create(ov::element::f32, {}, {scalar_value}));
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(add, transpose_const);
|
||||
|
||||
auto unsqueeze_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {unsqueeze_axis});
|
||||
|
|
@ -195,15 +207,19 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, SingleEltwiseWith5ScalarOnSecondBranch)
|
|||
auto unsqueeze_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {unsqueeze_axis});
|
||||
auto unsqueeze = std::make_shared<ov::opset8::Unsqueeze>(input, unsqueeze_const);
|
||||
|
||||
auto add = std::make_shared<ov::opset8::Add>(unsqueeze, ov::opset8::Constant::create(ov::element::f32, {1, 1, 1, 1, 1}, {scalar_value}));
|
||||
auto add = std::make_shared<ov::opset8::Add>(
|
||||
unsqueeze,
|
||||
ov::opset8::Constant::create(ov::element::f32, {1, 1, 1, 1, 1}, {scalar_value}));
|
||||
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{add}, ov::ParameterVector{input});
|
||||
}
|
||||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
||||
auto add = std::make_shared<ov::opset8::Add>(input, ov::opset8::Constant::create(ov::element::f32, {}, {scalar_value}));
|
||||
auto add =
|
||||
std::make_shared<ov::opset8::Add>(input,
|
||||
ov::opset8::Constant::create(ov::element::f32, {}, {scalar_value}));
|
||||
|
||||
auto unsqueeze_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {unsqueeze_axis});
|
||||
auto unsqueeze = std::make_shared<ov::opset8::Unsqueeze>(add, unsqueeze_const);
|
||||
|
|
@ -229,7 +245,7 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, SingleBinaryEltwiseWithNotScalarOnSecond
|
|||
auto add = std::make_shared<ov::opset8::Add>(unsqueeze, add_scalar);
|
||||
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{add}, ov::ParameterVector{input});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
|
||||
TEST_F(MoveEltwiseUpThroughDataMovTest, SingleUnaryEltwiseDynamicShape) {
|
||||
|
|
@ -244,7 +260,7 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, SingleUnaryEltwiseDynamicShape) {
|
|||
auto sigmoid = std::make_shared<ov::opset8::Sigmoid>(unsqueeze);
|
||||
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{sigmoid}, ov::ParameterVector{input});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -263,13 +279,14 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, SingleUnaryEltwiseDynamicRank) {
|
|||
const std::vector<int64_t> input_order = {3, 2, 1, 0};
|
||||
const int64_t unsqueeze_axis = 2;
|
||||
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape::dynamic(ov::Rank::dynamic()));
|
||||
auto input =
|
||||
std::make_shared<ov::opset8::Parameter>(ov::element::f32, ov::PartialShape::dynamic(ov::Rank::dynamic()));
|
||||
|
||||
auto unsqueeze_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{}, {unsqueeze_axis});
|
||||
auto unsqueeze = std::make_shared<ov::opset8::Unsqueeze>(input, unsqueeze_const);
|
||||
auto sigmoid = std::make_shared<ov::opset8::Sigmoid>(unsqueeze);
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{sigmoid}, ov::ParameterVector{input});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
|
||||
TEST_F(MoveEltwiseUpThroughDataMovTest, TransposeFakeQuantize) {
|
||||
|
|
@ -278,28 +295,32 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, TransposeFakeQuantize) {
|
|||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(input, transpose_const);
|
||||
auto fakequantize = std::make_shared<ov::opset8::FakeQuantize>(transpose,
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-8.5}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {8.5}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-128}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {127}),
|
||||
255);
|
||||
auto fakequantize = std::make_shared<ov::opset8::FakeQuantize>(
|
||||
transpose,
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-8.5}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {8.5}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-128}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {127}),
|
||||
255);
|
||||
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{fakequantize}, ov::ParameterVector{input});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
||||
auto fakequantize = std::make_shared<ov::opset8::FakeQuantize>(input,
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-8.5}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {8.5}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-128}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {127}),
|
||||
255);
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto fakequantize = std::make_shared<ov::opset8::FakeQuantize>(
|
||||
input,
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-8.5}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {8.5}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-128}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {127}),
|
||||
255);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(fakequantize, transpose_const);
|
||||
|
||||
model_ref = std::make_shared<ov::Model>(ov::NodeVector{transpose}, ov::ParameterVector{input});
|
||||
|
|
@ -312,17 +333,19 @@ TEST_F(MoveEltwiseUpThroughDataMovTest, TransposeFakeQuantizePerChannel) {
|
|||
{
|
||||
auto input = std::make_shared<ov::opset8::Parameter>(ov::element::f32, shape);
|
||||
|
||||
auto transpose_const = ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose_const =
|
||||
ov::opset8::Constant::create(ov::element::i64, ov::Shape{input_order.size()}, input_order);
|
||||
auto transpose = std::make_shared<ov::opset8::Transpose>(input, transpose_const);
|
||||
|
||||
auto fakequantize = std::make_shared<ov::opset8::FakeQuantize>(transpose,
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{1, 3, 1, 1}, {-8.5, -7.5, -10.}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{1, 3, 1, 1}, {8.5, 7.5, 10.}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-128}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {127}),
|
||||
255);
|
||||
auto fakequantize = std::make_shared<ov::opset8::FakeQuantize>(
|
||||
transpose,
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{1, 3, 1, 1}, {-8.5, -7.5, -10.}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{1, 3, 1, 1}, {8.5, 7.5, 10.}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {-128}),
|
||||
ov::opset8::Constant::create(ov::element::f32, ov::Shape{}, {127}),
|
||||
255);
|
||||
|
||||
model = std::make_shared<ov::Model>(ov::NodeVector{fakequantize}, ov::ParameterVector{input});
|
||||
manager.register_pass<ov::intel_cpu::MoveEltwiseUpThroughDataMov>();
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
// Copyright (C) 2018-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "move_eltwise_up_data_movement.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
|
||||
#include <openvino/opsets/opset8.hpp>
|
||||
#include "openvino/core/rt_info.hpp"
|
||||
#include "openvino/pass/pattern/op/wrap_type.hpp"
|
||||
#include "transformations/utils/utils.hpp"
|
||||
|
||||
#include "itt.hpp"
|
||||
|
||||
|
||||
namespace {
|
||||
bool is_data_movement_operation(const std::shared_ptr<ov::Node>& node) {
|
||||
return ov::is_type<ov::op::v0::Squeeze>(node) ||
|
||||
ov::is_type<ov::op::v0::Unsqueeze>(node) ||
|
||||
ov::is_type<ov::op::v1::Reshape>(node) ||
|
||||
ov::is_type<ov::op::v1::Transpose>(node) ||
|
||||
ov::is_type<ov::op::v0::ShuffleChannels>(node) ||
|
||||
ov::is_type<ov::op::v7::Roll>(node) ||
|
||||
ov::is_type<ov::op::v0::ReverseSequence>(node) ||
|
||||
ov::is_type<ov::op::v0::DepthToSpace>(node) ||
|
||||
ov::is_type<ov::op::v1::BatchToSpace>(node) ||
|
||||
ov::is_type<ov::op::v1::Broadcast>(node) ||
|
||||
ov::is_type<ov::op::v3::Broadcast>(node) ||
|
||||
ov::is_type<ov::op::v1::Gather>(node) ||
|
||||
ov::is_type<ov::op::v7::Gather>(node) ||
|
||||
ov::is_type<ov::op::v8::Gather>(node);
|
||||
}
|
||||
|
||||
bool is_scalar_like(const std::shared_ptr<ov::Node>& node) {
|
||||
auto constantNode = std::dynamic_pointer_cast<ov::opset8::Constant>(node);
|
||||
return constantNode != nullptr && shape_size(constantNode->get_shape()) == 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ov::intel_cpu::MoveEltwiseUpThroughDataMov::MoveEltwiseUpThroughDataMov() {
|
||||
MATCHER_SCOPE(MoveEltwiseUpThroughDataMov);
|
||||
auto eltwise_pattern = ov::pass::pattern::wrap_type<ov::op::util::UnaryElementwiseArithmetic,
|
||||
ov::op::util::BinaryElementwiseArithmetic,
|
||||
ov::op::v0::FakeQuantize>(ov::pass::pattern::has_static_rank());
|
||||
|
||||
ov::matcher_pass_callback callback = [OV_CAPTURE_CPY_AND_THIS](ov::pass::pattern::Matcher& m) {
|
||||
const auto& pattern_map = m.get_pattern_value_map();
|
||||
|
||||
auto eltwise = pattern_map.at(eltwise_pattern).get_node_shared_ptr();
|
||||
if (transformation_callback(eltwise)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eltwise->get_output_target_inputs(0).size() != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < eltwise->get_input_size(); ++i) {
|
||||
if (!is_scalar_like(eltwise->get_input_node_shared_ptr(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ov::is_type<ov::op::v0::FakeQuantize>(eltwise) && eltwise->get_output_element_type(0) != eltwise->get_input_element_type(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto current = eltwise->get_input_node_shared_ptr(0);
|
||||
auto child = eltwise;
|
||||
|
||||
while (is_data_movement_operation(current)) {
|
||||
if (current->get_output_size() != 1 ||
|
||||
current->get_output_target_inputs(0).size() != 1 ||
|
||||
current->get_output_element_type(0) != current->get_input_element_type(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
child = current;
|
||||
current = current->get_input_node_shared_ptr(0);
|
||||
}
|
||||
|
||||
// now current is the first not data movement op
|
||||
if (child == eltwise) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eltwise constant shape should match new input shape
|
||||
for (size_t i = 1; i < eltwise->get_input_size(); i++) {
|
||||
if (current->get_output_partial_shape(0).size() != eltwise->get_input_partial_shape(i).size()) {
|
||||
auto old_eltwise_const = ov::as_type_ptr<ov::opset8::Constant>(eltwise->get_input_node_shared_ptr(i));
|
||||
if (old_eltwise_const->get_shape().size() != 0) {
|
||||
auto new_constant = std::make_shared<ov::opset8::Constant>(*old_eltwise_const.get(), ov::Shape{});
|
||||
ov::replace_node_update_name(old_eltwise_const, new_constant);
|
||||
}
|
||||
}
|
||||
}
|
||||
ov::replace_output_update_name(eltwise->output(0), eltwise->input_value(0));
|
||||
|
||||
ov::OutputVector eltwiseInputs = eltwise->input_values();
|
||||
eltwiseInputs[0] = child->input_value(0);
|
||||
auto newEltwise = eltwise->clone_with_new_inputs(eltwiseInputs);
|
||||
// WA: it's necessary to set empty friendly name here
|
||||
// to avoid name duplication in TypeRelaxed cases
|
||||
newEltwise->set_friendly_name("");
|
||||
ov::copy_runtime_info(eltwise, newEltwise);
|
||||
|
||||
ov::OutputVector childInputs = child->input_values();
|
||||
childInputs[0] = newEltwise;
|
||||
auto newChild = child->clone_with_new_inputs(childInputs);
|
||||
ov::copy_runtime_info(child, newChild);
|
||||
newChild->set_friendly_name(child->get_friendly_name());
|
||||
|
||||
ov::replace_node(child, newChild);
|
||||
return true;
|
||||
};
|
||||
|
||||
auto m = std::make_shared<ov::pass::pattern::Matcher>(eltwise_pattern, matcher_name);
|
||||
register_matcher(m, callback);
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (C) 2018-2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvino/pass/graph_rewrite.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace intel_cpu {
|
||||
|
||||
class MoveEltwiseUpThroughDataMov : public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("MoveEltwiseUpThroughDataMov", "0");
|
||||
MoveEltwiseUpThroughDataMov();
|
||||
};
|
||||
|
||||
} // namespace intel_cpu
|
||||
} // namespace ov
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
#include "transformations/common_optimizations/wrap_interpolate_into_transposes.hpp"
|
||||
#include "transformations/common_optimizations/matmul_const_transposes_extraction.hpp"
|
||||
#include "transformations/common_optimizations/fuse_rotary_positional_embeddings.hpp"
|
||||
#include "transformations/common_optimizations/move_eltwise_up_data_movement.hpp"
|
||||
#include "transformations/control_flow/unroll_tensor_iterator.hpp"
|
||||
#include "transformations/fp16_compression/mark_decompression_convert_constant_folding.hpp"
|
||||
#include "transformations/op_conversions/convert_batch_to_space.hpp"
|
||||
|
|
@ -116,7 +117,6 @@
|
|||
#include "transformations/cpu_opset/common/pass/decompose_integer_divide.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/convert_fq_rnn_to_quantized_rnn.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/insert_convert_after_extension.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/move_eltwise_up_data_movement.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/ngram_fusion.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/permute_slice_n_interpolation.hpp"
|
||||
#include "transformations/cpu_opset/common/pass/swap_convert_transpose.hpp"
|
||||
|
|
@ -744,9 +744,11 @@ void Transformations::PostLpt() {
|
|||
return node->get_rt_info().count("UNROLL_TI") == 0;
|
||||
},
|
||||
ov::pass::UnrollTensorIterator);
|
||||
CPU_REGISTER_PASS_COMMON(postLPTPassManager, MoveEltwiseUpThroughDataMov);
|
||||
CPU_REGISTER_PASS_COMMON(postLPTPassManager, ov::pass::MoveEltwiseUpThroughDataMov);
|
||||
CPU_SET_CALLBACK_COMMON(postLPTPassManager,
|
||||
[](const std::shared_ptr<const ov::Node>& node) -> bool {
|
||||
if (!ov::is_type<const ov::op::v0::FakeQuantize>(node) && node->get_output_element_type(0) != node->get_input_element_type(0))
|
||||
return true;
|
||||
if (node->get_input_size() >= 2) {
|
||||
return node->get_input_element_type(1) == ov::element::i8 ||
|
||||
node->get_input_element_type(1) == ov::element::u8 ||
|
||||
|
|
@ -754,7 +756,7 @@ void Transformations::PostLpt() {
|
|||
}
|
||||
return false;
|
||||
},
|
||||
MoveEltwiseUpThroughDataMov);
|
||||
ov::pass::MoveEltwiseUpThroughDataMov);
|
||||
CPU_REGISTER_PASS_COMMON(postLPTPassManager, ov::pass::Validate);
|
||||
|
||||
CPU_REGISTER_PASS_COMMON(postLPTPassManager, ov::pass::ConstantFolding);
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ private:
|
|||
void conv_eltwise_read_write_opt(program& p, program_node* node);
|
||||
};
|
||||
|
||||
// TODO: Remove this pass once no unexpected reshapes/reorders are added during ov::Model -> cldnn::topology conversion
|
||||
class prepare_primitive_fusing_through : public base_pass {
|
||||
public:
|
||||
prepare_primitive_fusing_through() : base_pass("prepare_primitive_fusing_through") {}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,11 @@
|
|||
#include "openvino/op/reduce_max.hpp"
|
||||
#include "openvino/op/reduce_mean.hpp"
|
||||
#include "openvino/op/reduce_sum.hpp"
|
||||
#include "openvino/op/reshape.hpp"
|
||||
#include "openvino/op/rnn_cell.hpp"
|
||||
#include "openvino/op/rnn_sequence.hpp"
|
||||
#include "openvino/op/squeeze.hpp"
|
||||
#include "openvino/op/unsqueeze.hpp"
|
||||
#include "openvino/op/util/sub_graph_base.hpp"
|
||||
#include "openvino/pass/constant_folding.hpp"
|
||||
#include "openvino/pass/manager.hpp"
|
||||
|
|
@ -67,6 +70,7 @@
|
|||
#include "transformations/common_optimizations/convert_quantize_dequantize.hpp"
|
||||
#include "transformations/common_optimizations/lin_op_sequence_fusion.hpp"
|
||||
#include "transformations/common_optimizations/lstm_cell_fusion.hpp"
|
||||
#include "transformations/common_optimizations/move_eltwise_up_data_movement.hpp"
|
||||
#include "transformations/common_optimizations/mvn_fusion.hpp"
|
||||
#include "transformations/common_optimizations/softmax_fusion.hpp"
|
||||
#include "transformations/common_optimizations/transpose_sinking.hpp"
|
||||
|
|
@ -705,6 +709,20 @@ void TransformationsPipeline::apply(std::shared_ptr<ov::Model> func) {
|
|||
|
||||
{
|
||||
ov::pass::Manager manager;
|
||||
|
||||
// Other ops support eltwise fusions
|
||||
const std::vector<DiscreteTypeInfo> allowed_data_movement_ops = {
|
||||
ov::op::v1::Reshape::get_type_info_static(),
|
||||
ov::op::v0::Squeeze::get_type_info_static(),
|
||||
ov::op::v0::Unsqueeze::get_type_info_static(),
|
||||
ov::op::v0::ShuffleChannels::get_type_info_static(),
|
||||
ov::op::v7::Roll::get_type_info_static(),
|
||||
ov::op::v0::ReverseSequence::get_type_info_static(),
|
||||
ov::op::v1::Broadcast::get_type_info_static(),
|
||||
ov::op::v3::Broadcast::get_type_info_static(),
|
||||
};
|
||||
manager.register_pass<ov::pass::MoveEltwiseUpThroughDataMov>(allowed_data_movement_ops);
|
||||
|
||||
manager.register_pass<ov::intel_gpu::ClampFP16Output>();
|
||||
manager.register_pass<ov::intel_gpu::ConvertMatMulToFullyConnected>();
|
||||
manager.register_pass<ov::intel_gpu::MoveFCReshapeToWeights>();
|
||||
|
|
|
|||
Loading…
Reference in New Issue