Skip to content

Commit 161cd2c

Browse files
PiotrKrzemmryzhov
andauthored
[Transformation] Identity with Result removal transformation workaround (#33175)
### Details: - Now correctly removes all Identity op from graph even in Result edge cases where the input node has another Result attached to it - Unlocks #33166 ### Tickets: - 177222 ### Explaination: When trying to convert graph: ``` p0 -> I0 -----\ Multiply ---- Result1 p1 -> I1 -----/ \ I2 ----- Result2 ``` to ``` p0 ----\ Multiply ---- Result1 p1 ----/ \ Result2 ``` the issue arises since Multiply outputs 1 tensor: T1, with a name "t1". Since Identity is a copy operation, then the I2 outputs a new tensor T2 with a name "t2". After removing I2, we need to keep T2, but Multiply has only one output, T1. The possible solutions were: - Keep only Multiply’s name "t1": Result2 now would have to have source tensor renamed to "t1" - Keep only Identity’s name "t2": Result1 Result2 now would have to have source tensor renamed to "t2" - Merge (union) them: both old names still exist, but the tensor’s name set changed vs the original one - And the fourth one, replace I2 with another OP. This would generate a new tensor "t2" from "t1" and if the new OP effectively "does nothing", the names would be kept So for now, there is no satisfactory solution. Therefore a workaround for the NPU plugin is to use another OP: in this PR I decided to replace I2 edge case with Reshape to have the marginal performance loss, if any. It won't be eliminated with optimizations for the same reasons why I2 cannot be eliminated. --------- Co-authored-by: Mikhail Ryzhov <mikhail.ryzhov@intel.com>
1 parent 5604706 commit 161cd2c

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

src/common/transformations/src/transformations/common_optimizations/nop_elimination.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "openvino/op/scatter_elements_update.hpp"
3232
#include "openvino/op/scatter_nd_update.hpp"
3333
#include "openvino/op/scatter_update.hpp"
34+
#include "openvino/op/shape_of.hpp"
3435
#include "openvino/op/slice.hpp"
3536
#include "openvino/op/split.hpp"
3637
#include "openvino/op/squeeze.hpp"
@@ -856,7 +857,37 @@ pass::EliminateIdentity::EliminateIdentity() {
856857
if (!identity) {
857858
return false;
858859
}
859-
return replace_output_update_name(identity->output(0), identity->input_value(0));
860+
861+
auto identity_out = identity->output(0);
862+
auto replacement = identity->input_value(0);
863+
864+
if (!replace_output_update_name(identity_out, replacement)) {
865+
const auto& consumers = replacement.get_target_inputs();
866+
bool replacement_has_result_consumer =
867+
std::any_of(consumers.cbegin(), consumers.cend(), [](const Input<Node>& consumer) {
868+
return ov::is_type<op::v0::Result>(consumer.get_node());
869+
});
870+
// Edge case from ticket 177222
871+
// NPU doesn't support Identity, so until then
872+
// we use a Reshape as a temp no-op to preserve both tensors
873+
if (replacement_has_result_consumer) {
874+
auto shape_of = std::make_shared<ov::op::v3::ShapeOf>(replacement, ov::element::i64);
875+
auto reshape = std::make_shared<ov::op::v1::Reshape>(replacement, shape_of, false);
876+
877+
// Preserve the "Identity" friendly name on the new node, so any
878+
// output still sees the same logical node name
879+
reshape->set_friendly_name(identity->get_friendly_name());
880+
881+
// Preserve Identity's tensor names on the new output, so externally
882+
// visible names for that path remain stable
883+
reshape->output(0).get_tensor().set_names(identity_out.get_tensor().get_names());
884+
885+
ov::copy_runtime_info(identity, {shape_of, reshape});
886+
identity_out.replace(reshape);
887+
}
888+
}
889+
890+
return true;
860891
};
861892

862893
auto m = make_shared<pattern::Matcher>(identity_pattern, matcher_name);

src/common/transformations/tests/common_optimizations/nop_elimination.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,3 +2151,45 @@ TEST_F(TransformationTestsF, ScatterNDUpdates15Elimination) {
21512151
model_ref = std::make_shared<ov::Model>(OutputVector{result}, ParameterVector{data, indices, updates});
21522152
}
21532153
}
2154+
2155+
TEST_F(TransformationTestsF, EliminateIdentity) {
2156+
{
2157+
auto p0 = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1});
2158+
auto p1 = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1});
2159+
2160+
auto id0 = std::make_shared<ov::op::v16::Identity>(p0);
2161+
id0->set_friendly_name("I0");
2162+
2163+
auto id1 = std::make_shared<ov::op::v16::Identity>(p1);
2164+
id1->set_friendly_name("I1");
2165+
2166+
auto mul = std::make_shared<ov::op::v1::Multiply>(id0, id1);
2167+
mul->set_friendly_name("Multiply");
2168+
2169+
auto id2 = std::make_shared<ov::op::v16::Identity>(mul);
2170+
id2->set_friendly_name("I2");
2171+
2172+
auto res1 = std::make_shared<ov::op::v0::Result>(mul);
2173+
auto res2 = std::make_shared<ov::op::v0::Result>(id2);
2174+
2175+
model = std::make_shared<ov::Model>(ov::ResultVector{res1, res2}, ov::ParameterVector{p0, p1});
2176+
2177+
manager.register_pass<ov::pass::EliminateIdentity>();
2178+
}
2179+
{
2180+
auto p0 = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1});
2181+
auto p1 = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1});
2182+
2183+
auto mul = std::make_shared<ov::op::v1::Multiply>(p0, p1);
2184+
mul->set_friendly_name("Multiply");
2185+
2186+
// Tests the NPU workaround from ticket 177222
2187+
auto shape_of = std::make_shared<ov::op::v3::ShapeOf>(mul, ov::element::i64);
2188+
auto reshape = std::make_shared<ov::op::v1::Reshape>(mul, shape_of, false);
2189+
2190+
auto res1 = std::make_shared<ov::op::v0::Result>(mul);
2191+
auto res2 = std::make_shared<ov::op::v0::Result>(reshape);
2192+
2193+
model_ref = std::make_shared<ov::Model>(ov::ResultVector{res1, res2}, ov::ParameterVector{p0, p1});
2194+
}
2195+
}

0 commit comments

Comments
 (0)