diff --git a/src/plugins/intel_cpu/src/graph_optimizer.cpp b/src/plugins/intel_cpu/src/graph_optimizer.cpp index 417728fdda1d9a..edc5f8c74f560a 100644 --- a/src/plugins/intel_cpu/src/graph_optimizer.cpp +++ b/src/plugins/intel_cpu/src/graph_optimizer.cpp @@ -2523,32 +2523,28 @@ void GraphOptimizer::mergeTransposeReshapeReorder(Graph& graph, } OPENVINO_ASSERT(edge, "Parent node '", parentNode->getName(), "' has invalid edges."); - // transposeNode support blocked input & non-blocked output, in the case, the Reorder after Transpose cannot be optimized std::vector srcPerm; - if (!reverseOrder) { - // case 1. transposeNode support blocked input & non-blocked output, in the case, the reorder - // cannot be optimized - // case 2. Transpose and Reorder do opposite permutation to each other as expected, but isOptimized is already - // set false due to some preliminarily checks. We need to reinterpret layout Transpose input without physical - // change of the memory. - auto* castedTranspose = dynamic_cast(transposeNode.get()); - OPENVINO_ASSERT(castedTranspose, - "[CPU] parent node of type:", - transposeNode->getTypeStr(), - " with name: ", - transposeNode->getName(), - " is not a transpose node"); - - auto inOrder = transposeNode->getSelectedPrimitiveDescriptor()->getConfig().inConfs[0].getMemDesc()->as()->getOrder(); - auto outOrder = reorderOutDesc->as()->getOrder(); - if (!isOptimized || inOrder.size() > outOrder.size()) { - isOptimized = false; - // inDesc should be permuted before calling reorder - auto& ord = castedTranspose->getOrder(); - srcPerm = std::vector(ord.size()); - for (size_t i = 0; i < ord.size(); i++) { - srcPerm[ord[i]] = i; - } + auto* castedTranspose = dynamic_cast(transposeNode.get()); + OPENVINO_ASSERT(castedTranspose, + "[CPU] parent node of type:", + transposeNode->getTypeStr(), + " with name: ", + transposeNode->getName(), + " is not a transpose node"); + + auto inOrder = transposeNode->getSelectedPrimitiveDescriptor()->getConfig().inConfs[0].getMemDesc()->as()->getOrder(); + auto outOrder = reorderOutDesc->as()->getOrder(); + // Permutation must be set and reorder mustn't be optimized in 2 cases: + // 1. Transpose has blocked input & non-blocked output + // 2. Transpose and Reorder do opposite permutation to each other as expected, + // but isOptimized is already set to false due to some preliminarily checks. + if (!isOptimized || inOrder.size() > outOrder.size()) { + isOptimized = false; + // inDesc should be permuted before calling reorder + auto& ord = castedTranspose->getOrder(); + srcPerm = std::vector(ord.size()); + for (size_t i = 0; i < ord.size(); i++) { + srcPerm[ord[i]] = i; } } diff --git a/src/plugins/intel_cpu/tests/unit/graph/merge_transpose_reorder_test.cpp b/src/plugins/intel_cpu/tests/unit/graph/merge_transpose_reorder_test.cpp index 5ec1ecfce18d21..08b285a7d17dff 100644 --- a/src/plugins/intel_cpu/tests/unit/graph/merge_transpose_reorder_test.cpp +++ b/src/plugins/intel_cpu/tests/unit/graph/merge_transpose_reorder_test.cpp @@ -3,62 +3,95 @@ // #include +#include + #include "dummy_node.hpp" -#include "nodes/reorder.h" #include "nodes/input.h" +#include "nodes/reorder.h" #include "nodes/transpose.h" - #include "ov_models/builders.hpp" -#include "ie_ngraph_utils.hpp" using namespace ov::intel_cpu; +using LOOK = Edge::LOOK; +struct MergeTransposeReorderTestParams { + enum Result {IS_OPTIMIZED, NOT_OPTIMIZED}; + MergeTransposeReorderTestParams(const ov::Shape& testShape, + ov::element::Type_t testPrec, + LayoutType firstNodeLayout, + LOOK firstNodeInplaceDirection, + LayoutType secondNodeLayout, + LOOK secondNodeInplaceDirection, + Result test_result) + : testShape(testShape), + testPrec(testPrec), + firstNodeLayout(firstNodeLayout), + firstNodeInplaceDirection(firstNodeInplaceDirection), + secondNodeLayout(secondNodeLayout), + secondNodeInplaceDirection(secondNodeInplaceDirection), + test_result(test_result) {} + + ov::Shape testShape; + ov::element::Type_t testPrec; + LayoutType firstNodeLayout; + LOOK firstNodeInplaceDirection; + LayoutType secondNodeLayout; + LOOK secondNodeInplaceDirection; + Result test_result; +}; +using Result = MergeTransposeReorderTestParams::Result; /* * MergeTransposeReorderIsOptimizedCPUTest to test the CPU plugin-in MergeTransposeReorder graph optimizer * under the circumstance that the upstream node or downstream node is inPlaced thereby the inserted Reorder * cannot be optimized. */ -class MergeTransposeReorderIsOptimizedCPUTest : public ::testing::Test { +class MergeTransposeReorderIsOptimizedCPUTest : public testing::WithParamInterface, + public ov::test::TestsCommon { public: void Validate() const { CheckTransposeCount(0); - CheckReorderOptimized(std::string("_fake"), false); // the fused node is of name "reshape_abcd_acdb_fake" + const bool is_optimized = GetParam().test_result == MergeTransposeReorderTestParams::Result::IS_OPTIMIZED; + CheckReorderOptimized(std::string("_fake"), is_optimized); // the fused reorder has always postfix "fake" } +protected: void SetUp() override { - CreateGraph(); + MergeTransposeReorderTestParams params = GetParam(); + CreateGraph(params.testShape, + params.testPrec, + params.firstNodeLayout, + params.firstNodeInplaceDirection, + params.secondNodeLayout, + params.secondNodeInplaceDirection); } -protected: - /* graph typology - --------- - |Input | - --------- - | - ---------- - | Dummy | <*NOTE: fake node with laytout NCSP, and inplace from upstream*> - ---------- - | - |---------------| - | ---------- | - | |Transpose| | - | --------- | - | | | - | --------- | - | |Reorder | | <*NOTE: Reorder is inheristically inserted since Multiply is asking NSPC input.*> - | --------- | - |---------------| - | - ----------- - | Dummy | <*NOTE: fake node with laytout NSPC, and inplace from downstream*> - ----------- - | - --------- - |Output | - --------- + /* graph topology + ┌───────┐ + │ Input │ + └───┬───┘ + │ + ┌───┴───┐ + │ Dummy │ <*NOTE: fake node with firstNodeLayout, and firstNodeInplaceDirection*> + └───┬───┘ + │ + ┌────┴────┐ + │Transpose│ <*NOTE: Reorder is inserted before/after Transpose depending on first/second node layouts.*> + └────┬────┘ + │ + ┌───┴───┐ + │ Dummy │ <*NOTE: fake node with secondNodeLayout, and secondNodeInplaceDirection*> + └───┬───┘ + │ + ┌────┴───┐ + │ Output │ + └────────┘ */ - void CreateGraph() { - // + void CreateGraph(const ov::Shape& testShape, + const ov::element::Type_t& testPrec, + LayoutType firstNodeLayout, + LOOK firstNodeInplaceDirection, + LayoutType secondNodeLayout, + LOOK secondNodeInplaceDirection) { Config conf; conf.rtCacheCapacity = 100; auto context = std::make_shared(conf, nullptr, nullptr, false); @@ -66,12 +99,13 @@ class MergeTransposeReorderIsOptimizedCPUTest : public ::testing::Test { m_graph = std::unique_ptr(new Graph()); + OPENVINO_ASSERT(testShape.size() == 4 || testShape.size() == 3, "Only 4D and 3D shapes are supported"); // ov::Model with only a transpose node - ov::ParameterVector params{std::make_shared(testPrec, ov::Shape(testShape))}; - auto order = std::vector{0, 3, 1, 2}; - auto constOrder = ngraph::builder::makeConstant(ngraph::element::i32, {order.size()}, order); - auto transpose = std::make_shared(params[0], constOrder); - ov::ResultVector results{std::make_shared(transpose)}; + ov::ParameterVector params{std::make_shared(testPrec, testShape)}; + auto order = testShape.size() == 4 ? std::vector{0, 3, 1, 2} : std::vector{0, 2, 1}; + auto constOrder = ngraph::builder::makeConstant(ov::element::i32, {order.size()}, order); + auto transpose = std::make_shared(params[0], constOrder); + ov::ResultVector results{std::make_shared(transpose)}; // Replicate auto replicate = [&](std::vector &nodes, std::vector &edges) -> void { @@ -87,18 +121,16 @@ class MergeTransposeReorderIsOptimizedCPUTest : public ::testing::Test { auto inputNode = std::make_shared(params[0], context); - // dummy ncsp + inPlace LOOK_UP auto dummyNode1 = std::make_shared( - testShape, testPrec, "reshape", "DummyNode", context, LayoutType::ncsp, Edge::LOOK::LOOK_UP); + testShape, testPrec, "reshape", "DummyNode", context, firstNodeLayout, firstNodeInplaceDirection); auto orderNode = std::make_shared(constOrder, context); // const order auto transposeNode = std::make_shared(transpose, context); transposeNode->filterSupportedPrimitiveDescriptors(); - // dummy nspc + inPlace LOOK_DOWN - const ov::Shape shape_tranpose{testShape[0], testShape[3], testShape[1], testShape[2]}; // shape after transpose + const auto& transpose_shape = transpose->get_output_shape(0); auto dummyNode2 = std::make_shared( - shape_tranpose, testPrec, "multiply", "DummyNode", context, LayoutType::nspc, Edge::LOOK::LOOK_DOWN); + transpose_shape, testPrec, "multiply", "DummyNode", context, secondNodeLayout, secondNodeInplaceDirection); auto outputNode = std::make_shared(results[0], context); @@ -147,12 +179,20 @@ class MergeTransposeReorderIsOptimizedCPUTest : public ::testing::Test { } private: - const ov::element::Type_t testPrec = ov::element::Type_t::f32; - const ov::Shape testShape{1, 3, 8, 16}; - std::unique_ptr m_graph; -}; // class MergeTransposeReorderIsOptimizedCPUTest +}; // class MergeTransposeReorderIsOptimizedCPUTest -TEST_F(MergeTransposeReorderIsOptimizedCPUTest, smoke_Run_MergeTransposeReorder_isOptimized) { +TEST_P(MergeTransposeReorderIsOptimizedCPUTest, smoke_Run_MergeTransposeReorder_isOptimized) { Validate(); -} \ No newline at end of file +} + +const std::vector params = { + {{1, 3, 8, 16}, ov::element::f32, LayoutType::ncsp, LOOK::LOOK_UP, LayoutType::nspc, LOOK::LOOK_DOWN, Result::NOT_OPTIMIZED}, + {{1, 3, 8, 16}, ov::element::f32, LayoutType::ncsp, LOOK::LOOK_DOWN, LayoutType::nspc, LOOK::LOOK_UP, Result::IS_OPTIMIZED}, + {{3, 8, 16}, ov::element::f32, LayoutType::nspc, LOOK::LOOK_UP, LayoutType::ncsp, LOOK::LOOK_DOWN, Result::NOT_OPTIMIZED}, + {{3, 8, 16}, ov::element::f32, LayoutType::nspc, LOOK::LOOK_DOWN, LayoutType::ncsp, LOOK::LOOK_UP, Result::IS_OPTIMIZED}, +}; + +INSTANTIATE_TEST_SUITE_P(smoke_Run_MergeTransposeReorder_isOptimized, + MergeTransposeReorderIsOptimizedCPUTest, + ::testing::ValuesIn(params)); \ No newline at end of file