Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CPU]remove useless convert pair(Convert[a->b] -> Convert[b->a]) #27868

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/bindings/python/tests/test_graph/test_constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ def test_float_to_f8e8m0_constant_matrix(ov_type, numpy_dtype, opset):

compressed_const = opset.constant(data, dtype=ov_type, name="fx_constant")
convert_to_fp8 = opset.convert(compressed_const, Type.f8e8m0)
convert_to_fp8.get_rt_info()["DisableEliminateUselessConvert"] = "1"
convert_back = opset.convert(convert_to_fp8, ov_type)
parameter = opset.parameter(ov.PartialShape([-1, -1]), ov_type)
add_op = opset.add(parameter, convert_back)
Expand Down Expand Up @@ -481,6 +482,7 @@ def test_float_to_f8e5m2_convert(ov_type, numpy_dtype, opset):

compressed_const = opset.constant(data, dtype=ov_type, name="fx_constant")
convert_to_fp8 = opset.convert(compressed_const, Type.f8e5m2)
convert_to_fp8.get_rt_info()["DisableEliminateUselessConvert"] = "1"
convert_back = opset.convert(convert_to_fp8, ov_type)
parameter = opset.parameter(ov.PartialShape([-1]), ov_type)
add_op = opset.add(parameter, convert_back)
Expand Down Expand Up @@ -514,6 +516,7 @@ def test_float_to_f8e4m3_convert(ov_type, numpy_dtype, opset):

compressed_const = opset.constant(data, dtype=ov_type, name="fx_constant")
convert_to_fp8 = opset.convert(compressed_const, Type.f8e4m3)
convert_to_fp8.get_rt_info()["DisableEliminateUselessConvert"] = "1"
convert_back = opset.convert(convert_to_fp8, ov_type)
parameter = opset.parameter(ov.PartialShape([-1]), ov_type)
add_op = opset.add(parameter, convert_back)
Expand Down Expand Up @@ -549,6 +552,7 @@ def test_float_to_f8e8m0_convert(ov_type, numpy_dtype, opset):

compressed_const = opset.constant(data, dtype=ov_type, name="fx_constant")
convert_to_fp8 = opset.convert(compressed_const, Type.f8e8m0)
convert_to_fp8.get_rt_info()["DisableEliminateUselessConvert"] = "1"
convert_back = opset.convert(convert_to_fp8, ov_type)
parameter = opset.parameter(ov.PartialShape([-1]), ov_type)
add_op = opset.add(parameter, convert_back)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace pass {

class TRANSFORMATIONS_API EliminateConcat;
class TRANSFORMATIONS_API EliminateConvert;
class TRANSFORMATIONS_API DisableEliminateUselessConvert;
class TRANSFORMATIONS_API EliminateConvertNonZero;
class TRANSFORMATIONS_API EliminateEltwise;
class TRANSFORMATIONS_API EliminateScatterUpdate;
Expand Down Expand Up @@ -63,6 +64,17 @@ class ov::pass::EliminateConvert : public ov::pass::MatcherPass {
EliminateConvert();
};

/**
* @ingroup ov_transformation_common_api
* @brief DisableEliminateUselessConvert eliminates convert(a->b) + convert(b->a) that does nothing. Add just for some
* testing.
*/
class ov::pass::DisableEliminateUselessConvert : public ov::pass::MatcherPass {
public:
OPENVINO_RTTI("DisableEliminateUselessConvert", "0");
DisableEliminateUselessConvert();
};

/**
* @ingroup ov_transformation_common_api
* @brief EliminateConvertNonZero eliminates convert before NonZero
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,16 +443,54 @@ pass::EliminateConvert::EliminateConvert() {
if (!convert) {
return false;
}

if (convert->get_input_element_type(0) == convert->get_element_type()) {
return replace_output_update_name(convert->output(0), convert->input_value(0));
}

// Only for some test.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definetelly incorrect way to solve test failures. Test exptected behavior should be updated instead.

if (convert->get_rt_info().count("DisableEliminateUselessConvert") &&
convert->get_rt_info()["DisableEliminateUselessConvert"].as<std::string>() == std::string("1")) {
return false;
}

// Eliminate useless convert pattern, for example:
// Convert[fp16->fp32] -> Convert[fp32->f16]
if (convert && convert->get_output_size() > 0u && convert->get_input_size() > 0u) {
auto convert2 = ov::as_type_ptr<ov::op::v0::Convert>(
convert->get_output_target_inputs(0).begin()->get_node()->shared_from_this());
if (convert2 && convert->get_input_element_type(0) == convert2->get_output_element_type(0) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general such optimization is not safe. Cases like Convert[fp32->fp16] -> Convert[fp16->f32] still needs to be executed explicitly as long as its elemintation will affect model accuracy.

However we still can adjust the pass to the patterns which are safe to remove (then src/dst precisions can be converted to intermediate precision w/o any loss).
@itikhono Do you think it is reasonable extension for the existing EliminateConvert pass?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @dmitry-gorokhov for the clarification.

convert->get_output_partial_shape(0) == convert2->get_output_partial_shape(0)) {
replace_output_update_name(convert2->output(0), convert->input_value(0));
return true;
}
}

return false;
};

auto m = make_shared<pattern::Matcher>(convert_pattern, matcher_name);
this->register_matcher(m, callback);
}

pass::DisableEliminateUselessConvert::DisableEliminateUselessConvert() {
MATCHER_SCOPE(DisableEliminateUselessConvert);
auto convert_pattern = pattern::wrap_type<ov::op::v0::Convert>();

matcher_pass_callback callback = [](pattern::Matcher& m) {
auto convert = dynamic_pointer_cast<ov::op::v0::Convert>(m.get_match_root());
if (!convert) {
return false;
}
convert->get_rt_info()["DisableEliminateUselessConvert"] = "1";

return true;
};

auto m = make_shared<pattern::Matcher>(convert_pattern, matcher_name);
this->register_matcher(m, callback);
}

pass::EliminateConvertNonZero::EliminateConvertNonZero() {
MATCHER_SCOPE(EliminateConvertNonZero);
auto convert_pattern = pattern::wrap_type<ov::op::v0::Convert>(pattern::consumers_count(1));
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/intel_cpu/src/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,11 @@ void Node::filterSupportedPrimitiveDescriptors() {

auto isNotSuitableDesc = [&](const NodeDesc& desc) {
const auto &config = desc.getConfig();

if (this->getTypeStr() == "Parameter") {
return false;
}

if (inputMemoryFormatsFilter.size() > config.inConfs.size() || outputMemoryFormatsFilter.size() > config.outConfs.size())
OPENVINO_THROW("Incorrect number of input or output memory formats");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,26 @@ TEST_P(ConcatSDPTransposeTestSetState, CompareWithRefs) {
for (size_t i = 0; i < actualOutputs.size(); i++) {
ov::test::utils::compare(expectedOutputs[i], actualOutputs[i], abs_threshold, rel_threshold);
}

// Check compileModel doesn't should contain 2 useless Convert.
auto check_node_type = [](std::shared_ptr<ov::Node> node, const std::string& typeName) -> bool {
auto rtInfo = node->get_rt_info();
auto it = rtInfo.find(ov::exec_model_info::LAYER_TYPE);
OPENVINO_ASSERT(rtInfo.end() != it);
return it->second.as<std::string>() == typeName;
};
EXPECT_TRUE(compiledModel.get_runtime_model() != nullptr);
for (const auto& node : compiledModel.get_runtime_model()->get_ops()) {
if (check_node_type(node, "Convert")) {
auto convert1 = node;
auto convert2 = node->get_output_target_inputs(0).begin()->get_node()->shared_from_this();
if (node->get_output_size() > 0u && check_node_type(convert2, "Convert")) {
bool useless_convert = convert1->get_input_element_type(0) == convert2->get_output_element_type(0) &&
convert1->get_output_partial_shape(0) == convert2->get_output_partial_shape(0);
EXPECT_FALSE(useless_convert);
}
}
}
}

namespace {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "openvino/op/add.hpp"
#include "openvino/op/constant.hpp"
#include "openvino/op/slice.hpp"
#include "openvino/pass/manager.hpp"
#include "transformations/common_optimizations/nop_elimination.hpp"

using namespace ov;
using namespace ov::preprocess;
Expand Down Expand Up @@ -139,6 +141,11 @@ static RefPreprocessParams convert_only() {
.convert_element_type(element::u8)
.convert_element_type(element::f32);
p.build();

ov::pass::Manager manager;
manager.register_pass<ov::pass::DisableEliminateUselessConvert>();
manager.run_passes(f);

return f;
};
res.inputs.emplace_back(Shape{1, 1, 2, 2}, element::i16, std::vector<int16_t>{2, 3, 4, 5});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include "common_test_utils/common_utils.hpp"
#include "ov_lpt_models/fake_quantize_and_convolution.hpp"
#include "transformations/common_optimizations/nop_elimination.hpp"
#include "openvino/pass/manager.hpp"

namespace LayerTestsDefinitions {

Expand Down Expand Up @@ -50,6 +52,10 @@ void ConvolutionQDqTransformation::SetUp() {
{});

this->configuration[ov::hint::inference_precision.name()] = "f32";

ov::pass::Manager manager;
manager.register_pass<ov::pass::DisableEliminateUselessConvert>();
manager.run_passes(function);
}

void ConvolutionQDqTransformation::run() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include "transformations/init_node_info.hpp"
#include "ov_lpt_models/fake_quantize_and_convolution.hpp"
#include "transformations/common_optimizations/nop_elimination.hpp"
#include "openvino/pass/manager.hpp"

namespace LayerTestsDefinitions {

Expand Down Expand Up @@ -48,6 +50,10 @@ void FakeQuantizeWithNotOptimalTransformation::SetUp() {
testValues.convertOnWeights,
testValues.dequantizationOnWeights,
testValues.dequantizationAfter);

ov::pass::Manager manager;
manager.register_pass<ov::pass::DisableEliminateUselessConvert>();
manager.run_passes(function);
}

void FakeQuantizeWithNotOptimalTransformation::run() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include "common_test_utils/common_utils.hpp"
#include "ov_lpt_models/fake_quantize_and_convolution.hpp"
#include "transformations/common_optimizations/nop_elimination.hpp"
#include "openvino/pass/manager.hpp"

namespace LayerTestsDefinitions {

Expand Down Expand Up @@ -48,6 +50,10 @@ void GroupConvolutionQDqTransformation::SetUp() {
param.convertOnWeights,
param.dequantizationOnWeights,
{}, {}, {}, param.reshape, {}, "GroupConvolution", param.multiplyAfter);

ov::pass::Manager manager;
manager.register_pass<ov::pass::DisableEliminateUselessConvert>();
manager.run_passes(function);
}

void GroupConvolutionQDqTransformation::run() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <vector>

#include "ov_lpt_models/reduce.hpp"
#include "transformations/common_optimizations/nop_elimination.hpp"
#include "openvino/pass/manager.hpp"

namespace LayerTestsDefinitions {

Expand Down Expand Up @@ -59,6 +61,10 @@ void ReduceMeanTransformation::SetUp() {
param.reduceMean.constantValues,
param.reduceMean.keepDims,
param.dequantizationAfter);

ov::pass::Manager manager;
manager.register_pass<ov::pass::DisableEliminateUselessConvert>();
manager.run_passes(function);
}

void ReduceMeanTransformation::run() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "openvino/core/validation_util.hpp"
#include "openvino/runtime/exec_model_info.hpp"
#include "shared_test_classes/subgraph/quantized_convolution_batch_norm.hpp"
#include "transformations/common_optimizations/nop_elimination.hpp"
#include "openvino/pass/manager.hpp"

namespace ov {
namespace test {
Expand Down Expand Up @@ -169,6 +171,10 @@ void QuantizedConvolutionBatchNorm::SetUp() {
auto var = std::make_shared<ov::op::v0::Constant>(var_tensor);
auto batch_norm = std::make_shared<ov::op::v5::BatchNormInference>(conv, gamma, beta, mean, var, 0.00001);
function = std::make_shared<ov::Model>(batch_norm, ParameterVector{parameter});

ov::pass::Manager manager;
manager.register_pass<ov::pass::DisableEliminateUselessConvert>();
manager.run_passes(function);
}

void QuantizedConvolutionBatchNorm::TearDown() {
Expand Down
Loading