diff --git a/src/plugins/intel_cpu/src/nodes/subgraph.cpp b/src/plugins/intel_cpu/src/nodes/subgraph.cpp index a7ea7358c6c..0da55f44206 100644 --- a/src/plugins/intel_cpu/src/nodes/subgraph.cpp +++ b/src/plugins/intel_cpu/src/nodes/subgraph.cpp @@ -283,6 +283,75 @@ void Snippet::initSupportedPrimitiveDescriptors() { } void Snippet::selectOptimalPrimitiveDescriptor() { + // Check if memory desc is in SupportedPrimitiveDescriptors. + auto memDescInSupportedPD = [&](const ov::intel_cpu::MemoryDescPtr& parentMemDesc, + const size_t& idx, + size_t& supportedPDIdx) { + for (auto& type : getImplPriority()) { + for (size_t i = 0; i < getSupportedPrimitiveDescriptors().size(); i++) { + const auto& supportedPrimitiveDesc = getSupportedPrimitiveDescriptors()[i]; + const impl_desc_type supportedType = supportedPrimitiveDesc.getImplementationType(); + if (supportedType == type) { + const auto& supportedMemDesc = supportedPrimitiveDesc.getConfig().inConfs[idx].getMemDesc(); + // Static shape + same shape + same precision + same layout + if (parentMemDesc->getShape().isStatic() && supportedMemDesc->getShape().isStatic() && + parentMemDesc->getShape() == supportedMemDesc->getShape() && + parentMemDesc->getPrecision() == supportedMemDesc->getPrecision()) { + if ((parentMemDesc->hasLayoutType(LayoutType::ncsp) && + supportedMemDesc->hasLayoutType(LayoutType::ncsp)) || + (parentMemDesc->hasLayoutType(LayoutType::nspc) && + supportedMemDesc->hasLayoutType(LayoutType::nspc))) { + supportedPDIdx = i; + return true; + } + } + } + } + } + return false; + }; + + if (getParentEdges().size() == 3u) { + auto check_special_shape = [](const ov::intel_cpu::Shape& shape) { + // SShape=[1,?,1,1] + if (shape.isStatic()) { + const auto& dims = shape.getDims(); + if (dims.size() == 4u) { + return dims[0] == 1u && dims[1] > 1u && dims[2] == 1u && dims[3] == 1u; + } + } + return false; + }; + + int num_sshape_ncsp = 0; + int num_nonsshape_nspc = 0; + size_t selectedPDIdx = 0; + for (size_t i = 0; i < getParentEdges().size(); i++) { + auto parentEdge = getParentEdgeAt(i); + auto parentPtr = parentEdge->getParent(); + auto parent_spd = parentPtr->getSelectedPrimitiveDescriptor(); + auto parentDesc = parent_spd->getConfig().outConfs[0].getMemDesc(); + size_t supportedPDIdx = 0; + if (check_special_shape(parentDesc->getShape()) && parentDesc->hasLayoutType(LayoutType::ncsp) && + memDescInSupportedPD(parentDesc, i, supportedPDIdx)) { + num_sshape_ncsp++; + } + + if ((!check_special_shape(parentDesc->getShape())) && parentDesc->hasLayoutType(LayoutType::nspc) && + memDescInSupportedPD(parentDesc, i, supportedPDIdx)) { + num_nonsshape_nspc++; + selectedPDIdx = supportedPDIdx; + } + } + + bool foundOptimalPD = num_sshape_ncsp == 2 && num_nonsshape_nspc == 1; + if (foundOptimalPD) { + selectPrimitiveDescriptorByIndex(selectedPDIdx); + return; + } + } + + // fallback selectPreferPrimitiveDescriptor(getImplPriority(), true); } diff --git a/src/plugins/intel_cpu/tests/functional/subgraph_tests/src/subgraph_select_pd.cpp b/src/plugins/intel_cpu/tests/functional/subgraph_tests/src/subgraph_select_pd.cpp new file mode 100644 index 00000000000..5e21bd9f41c --- /dev/null +++ b/src/plugins/intel_cpu/tests/functional/subgraph_tests/src/subgraph_select_pd.cpp @@ -0,0 +1,124 @@ +// Copyright (C) 2024 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include "openvino/opsets/opset8.hpp" +#include "ov_models/builders.hpp" +#include "shared_test_classes/base/ov_subgraph.hpp" +#include "test_utils/cpu_test_utils.hpp" + +namespace ov { +namespace test { + +/* + input1(f32_abcd_{1,64,32,32}) input2(f32_abcd_{1,128,1,1}) + | | + Reorder(f32_acdb_{1,64,32,32}) const | + | / | + | / | + Convolution(f32_acdb_{1,1,30,30}) Range_1520 VariadicSplit(f32_abcd_{1,64,1,1}, f32_abcd_{1,64,1,1}) + | / \ / + | / \ / + | / \ / + | / \ / + MVN(f32_acdb_{1,1,30,30}) Reorder1(f32_acdb_{1,64,1,1}) Reorder2(f32_acdb_{1,64,1,1}) + \ / / + \ / / + \ / / + \ / / + Subgraph(f32_acdb_{1,64,30,30}) + | + | + Convolution(f32_acdb_{1,1,28,28}) + | + Result + The Subgraph node have 3 inputs: they don't have same layout. + Expected: Reorder is inserted after VariadicSplit[0] and VariadicSplit[1], not inserted after MVN. + Because VariadicSplit's output layout is [1,64,1,1], it' reorder almost have calculation. +*/ + +class SubgraphSelectPD : virtual public SubgraphBaseStaticTest { +protected: + void SetUp() override { + targetDevice = ov::test::utils::DEVICE_CPU; + abs_threshold = 2e-2; + + auto type = element::f32; + constexpr int const1 = 32; + auto input1 = std::make_shared(type, Shape{1, const1 / 2, 8, 8}); + input1->set_friendly_name("input1"); + auto input2 = std::make_shared(type, Shape{1, const1, 1, 1}); + input2->set_friendly_name("input2"); + + auto variadicSplit = std::make_shared( + input2, + ov::opset8::Constant::create(element::i64, Shape{1}, {1}), + ov::opset8::Constant::create(element::i64, Shape{2}, {const1 / 2, const1 / 2})); + variadicSplit->set_friendly_name("variadicSplit"); + + auto add1 = std::make_shared(variadicSplit->output(0), + ov::opset8::Constant::create(type, Shape{1}, {0})); + add1->set_friendly_name("add1"); + auto shapeof = std::make_shared(input1); + auto rankof = std::make_shared(shapeof); + auto squeeze = + std::make_shared(rankof, ov::opset8::Constant::create(element::i64, Shape{1}, {0})); + + auto range = std::make_shared(ov::opset8::Constant::create(element::i64, Shape{}, {2}), + squeeze, + ov::opset8::Constant::create(element::i64, Shape{}, {1}), + ov::element::i64); + auto create_conv = [&](const std::shared_ptr& input_node) { + auto conv = std::make_shared( + input_node, + ngraph::builder::makeConstant(type, + Shape{const1 / 2u, const1 / 2u, 3, 3}, + std::vector{}, + true, + 0.1f, + 0.9f), + Strides{1, 1}, + CoordinateDiff{1, 1}, + CoordinateDiff{1, 1}, + Strides{1, 1}); + conv->get_rt_info() = + CPUTestUtils::CPUTestsBase::makeCPUInfo({CPUTestUtils::nhwc}, {CPUTestUtils::nhwc}, {}); + return conv; + }; + auto create_relu = [&](const std::shared_ptr& input_node) { + return std::make_shared(input_node, + ov::opset8::Constant::create(element::f32, Shape{1}, {1})); + }; + auto conv1 = create_conv(input1); + auto mvn = + std::make_shared(create_relu(conv1), range, false, 0.1, op::MVNEpsMode::INSIDE_SQRT); + auto mul = std::make_shared(create_relu(add1), mvn); + auto add2 = std::make_shared(variadicSplit->output(1), mul); + auto conv2 = create_conv(create_relu(add2)); + conv2->set_friendly_name("conv2"); + + function = std::make_shared(conv2, ParameterVector{input1, input2}); + } + + void TearDown() override { + auto runtime_function = compiledModel.get_runtime_model(); + int nodes_found = 0; + for (const auto& n : runtime_function->get_ordered_ops()) { + auto layer_type = n->get_rt_info().at(ov::exec_model_info::LAYER_TYPE).as(); + if (layer_type == "Subgraph") { + nodes_found++; + auto output_layout = n->get_rt_info().at(ov::exec_model_info::OUTPUT_LAYOUTS).as(); + // The optimal choose should be: 'nhwc'. + ASSERT_EQ(output_layout, "acdb"); + } + } + ASSERT_GT(nodes_found, 0); + } +}; + +TEST_F(SubgraphSelectPD, smoke_CompareWithRefs) { + run(); +} + +} // namespace test +} // namespace ov