Skip to content

Commit 0577feb

Browse files
committed
code style, test refator, comments
1 parent cb96ca6 commit 0577feb

6 files changed

Lines changed: 136 additions & 84 deletions

File tree

src/common/transformations/include/transformations/common_optimizations/strided_slice_reshape_concat_fusion.hpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,39 @@ class TRANSFORMATIONS_API StridedSliceReshapeConcatFusion;
1717

1818
/**
1919
* @ingroup ov_transformation_common_api
20-
* @brief StridedSliceReshapeConcatFusion matches framing-like pattern built from
21-
* Slice/StridedSlice + Reshape + Concat and replaces it with a single Gather.
20+
* @brief Detects and fuses framing-like subgraphs made of Slice/StridedSlice + Reshape + Concat.
21+
*
22+
* This matcher pass identifies branches that extract windows from the same 2D input tensor,
23+
* reshapes each window from [B, W] to [B, 1, W], and concatenates them along axis=1.
24+
* The pass replaces the whole pattern with a single Gather.
25+
*
26+
* ## Before (for illustration purpose)
27+
*
28+
* Input [B, N]
29+
* |
30+
* +---------------+---------------+
31+
* | | |
32+
* StridedSlice/Slice StridedSlice/Slice ...
33+
* [B, W] [B, W]
34+
* | |
35+
* Reshape [B,1,W] Reshape [B,1,W]
36+
* | |
37+
* +------- Concat(axis=1) -------+
38+
* |
39+
* Output [B, K, W]
40+
*
41+
* ## After
42+
*
43+
* Input [B, N]
44+
* |
45+
* Gather(axis=1, indices[K,W])
46+
* |
47+
* Output [B, K, W]
48+
*
49+
* ## Notes
50+
* - All branches must read from the same source tensor.
51+
* - Branch window sizes must be equal (same W).
52+
* - This pass covers both Slice and StridedSlice forms under supported constraints.
2253
*/
2354
class ov::pass::StridedSliceReshapeConcatFusion : public ov::pass::MatcherPass {
2455
public:

src/common/transformations/src/transformations/common_optimizations/strided_slice_reshape_concat_fusion.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ namespace {
2525

2626
bool get_scalar_i64(const Output<Node>& value, int64_t& out) {
2727
const auto constant = ov::as_type_ptr<op::v0::Constant>(value.get_node_shared_ptr());
28-
if (!constant || ov::shape_size(constant->get_shape()) != 1 || !constant->get_element_type().is_integral_number()) {
28+
if (!constant || ov::shape_size(constant->get_shape()) != 1 ||
29+
!constant->get_element_type().is_integral_number()) {
2930
return false;
3031
}
3132

@@ -58,14 +59,16 @@ bool parse_slice_window(const std::shared_ptr<ov::Node>& node, int64_t& start, i
5859
if (const auto strided_slice = ov::as_type_ptr<op::v1::StridedSlice>(node)) {
5960
data = strided_slice->input_value(0);
6061
const auto pshape = data.get_partial_shape();
61-
if (pshape.rank().is_dynamic() || pshape.rank().get_length() != 2 || pshape[0].is_dynamic() || pshape[1].is_dynamic()) {
62+
if (pshape.rank().is_dynamic() || pshape.rank().get_length() != 2 || pshape[0].is_dynamic() ||
63+
pshape[1].is_dynamic()) {
6264
return false;
6365
}
6466

6567
std::vector<int64_t> begin;
6668
std::vector<int64_t> end;
6769
std::vector<int64_t> strides;
68-
if (!get_vector_i64(strided_slice->input_value(1), begin) || !get_vector_i64(strided_slice->input_value(2), end) ||
70+
if (!get_vector_i64(strided_slice->input_value(1), begin) ||
71+
!get_vector_i64(strided_slice->input_value(2), end) ||
6972
!get_vector_i64(strided_slice->input_value(3), strides)) {
7073
return false;
7174
}
@@ -150,8 +153,10 @@ bool parse_slice_window(const std::shared_ptr<ov::Node>& node, int64_t& start, i
150153

151154
int64_t axis = 0;
152155
int64_t step = 0;
153-
if (!get_scalar_i64(slice->input_value(1), start) || !get_scalar_i64(slice->input_value(2), stop) ||
154-
!get_scalar_i64(slice->input_value(3), step) || !get_scalar_i64(slice->input_value(4), axis)) {
156+
if (!get_scalar_i64(slice->input_value(1), start) ||
157+
!get_scalar_i64(slice->input_value(2), stop) ||
158+
!get_scalar_i64(slice->input_value(3), step) ||
159+
!get_scalar_i64(slice->input_value(4), axis)) {
155160
return false;
156161
}
157162

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ TEST_F(StridedSliceReshapeConcatFusionTest, NegativeUnequalSliceLength) {
5959
auto input = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, ov::Shape{1, 16});
6060
ov::OutputVector concat_inputs;
6161

62-
for (const auto& range : std::vector<std::pair<int64_t, int64_t>>{{0, 4}, {4, 9}, {9, 13}}) {
62+
for (const auto& range : std::vector<std::pair<int64_t, int64_t>>{{0, 4},
63+
{4, 9},
64+
{9, 13}}) {
6365
const auto start = range.first;
6466
const auto stop = range.second;
6567
auto begin = ov::op::v0::Constant::create<int64_t>(ov::element::i64, ov::Shape{2}, {0, start});

src/plugins/intel_cpu/src/transformations/transformation_pipeline.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@
204204
# include "snippets/lowered/pass/mha_parallel_wa_optimizer.hpp"
205205
# include "snippets/pass/common_optimizations.hpp"
206206
# include "transformations/common_optimizations/rms_fusion.hpp"
207+
# include "transformations/common_optimizations/strided_slice_reshape_concat_fusion.hpp"
207208
# include "transformations/cpu_opset/common/op/sdpa.hpp"
208209
# include "transformations/cpu_opset/common/pass/causal_mask_preprocess_fusion.hpp"
209210
# include "transformations/cpu_opset/common/pass/convert_fq_rnn_to_quantized_rnn.hpp"

src/plugins/intel_cpu/tests/functional/custom/subgraph_tests/src/common/strided_slice_reshape_concat_fusion.cpp

Lines changed: 0 additions & 76 deletions
This file was deleted.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (C) 2018-2026 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
5+
#include "utils/cpu_test_utils.hpp"
6+
#include "shared_test_classes/base/ov_subgraph.hpp"
7+
#include "functional_test_utils/skip_tests_config.hpp"
8+
9+
#include "openvino/op/concat.hpp"
10+
#include "openvino/op/reshape.hpp"
11+
#include "openvino/op/strided_slice.hpp"
12+
13+
using namespace CPUTestUtils;
14+
15+
namespace ov {
16+
namespace test {
17+
18+
using StridedSliceReshapeConcatFusionParams = std::tuple<InputShape>;
19+
20+
class StridedSliceReshapeConcatFusionCPUTest
21+
: public testing::WithParamInterface<StridedSliceReshapeConcatFusionParams>,
22+
virtual public SubgraphBaseTest,
23+
public CPUTestsBase {
24+
public:
25+
static std::string getTestCaseName(const testing::TestParamInfo<StridedSliceReshapeConcatFusionParams>& obj) {
26+
const auto& [inputShape] = obj.param;
27+
std::ostringstream result;
28+
result << "IS=" << ov::test::utils::partialShape2str({inputShape.first}) << "_TS=";
29+
for (const auto& shape : inputShape.second) {
30+
result << ov::test::utils::vec2str(shape);
31+
}
32+
return result.str();
33+
}
34+
35+
void SetUp() override {
36+
targetDevice = ov::test::utils::DEVICE_CPU;
37+
38+
const auto& [inputShape] = GetParam();
39+
init_input_shapes({inputShape});
40+
41+
auto input = std::make_shared<ov::op::v0::Parameter>(ov::element::f32, inputDynamicShapes[0]);
42+
ov::OutputVector concat_inputs;
43+
44+
for (const int64_t start : {0, 2, 4}) {
45+
auto begin = ov::op::v0::Constant::create<int64_t>(ov::element::i64, ov::Shape{2}, {0, start});
46+
auto end = ov::op::v0::Constant::create<int64_t>(ov::element::i64, ov::Shape{2}, {1, start + 4});
47+
auto strides = ov::op::v0::Constant::create<int64_t>(ov::element::i64, ov::Shape{2}, {1, 1});
48+
auto strided_slice =
49+
std::make_shared<ov::op::v1::StridedSlice>(input,
50+
begin,
51+
end,
52+
strides,
53+
std::vector<int64_t>{0, 0},
54+
std::vector<int64_t>{0, 0});
55+
auto shape = ov::op::v0::Constant::create<int64_t>(ov::element::i64, ov::Shape{3}, {1, 1, 4});
56+
auto reshape = std::make_shared<ov::op::v1::Reshape>(strided_slice, shape, false);
57+
concat_inputs.push_back(reshape);
58+
}
59+
60+
auto concat = std::make_shared<ov::op::v0::Concat>(concat_inputs, 1);
61+
function = std::make_shared<ov::Model>(ov::OutputVector{concat}, ov::ParameterVector{input});
62+
}
63+
64+
void check_results() {
65+
CheckNumberOfNodesWithType(compiledModel, "Gather", 1);
66+
}
67+
};
68+
69+
TEST_P(StridedSliceReshapeConcatFusionCPUTest, CompareWithRefs) {
70+
SKIP_IF_CURRENT_TEST_IS_DISABLED();
71+
run();
72+
check_results();
73+
}
74+
75+
namespace {
76+
77+
const std::vector<InputShape> input_shapes = {
78+
{{1, 16}, {{1, 16}}},
79+
};
80+
81+
INSTANTIATE_TEST_SUITE_P(smoke_StridedSliceReshapeConcatFusion,
82+
StridedSliceReshapeConcatFusionCPUTest,
83+
::testing::Combine(::testing::ValuesIn(input_shapes)),
84+
StridedSliceReshapeConcatFusionCPUTest::getTestCaseName);
85+
86+
} // namespace
87+
88+
} // namespace test
89+
} // namespace ov

0 commit comments

Comments
 (0)