From ad0ef566657f9862095f4f8134b09fa4570377d6 Mon Sep 17 00:00:00 2001 From: Maksim Kutakov Date: Mon, 12 Dec 2022 20:08:15 +0100 Subject: [PATCH] [CPU] Fix for the convolution node dummy shapes generation (#14521) --- .../src/memory_desc/cpu_memory_desc_utils.cpp | 13 +++ .../src/memory_desc/cpu_memory_desc_utils.h | 10 +- src/plugins/intel_cpu/src/nodes/conv.cpp | 45 +++++++-- src/plugins/intel_cpu/src/nodes/conv.h | 3 +- .../single_layer_tests/convolution.cpp | 97 ++++++++++++++----- .../functional/test_utils/cpu_test_utils.cpp | 2 +- 6 files changed, 138 insertions(+), 32 deletions(-) diff --git a/src/plugins/intel_cpu/src/memory_desc/cpu_memory_desc_utils.cpp b/src/plugins/intel_cpu/src/memory_desc/cpu_memory_desc_utils.cpp index 71d42cfb439..afe0c6ed8bb 100644 --- a/src/plugins/intel_cpu/src/memory_desc/cpu_memory_desc_utils.cpp +++ b/src/plugins/intel_cpu/src/memory_desc/cpu_memory_desc_utils.cpp @@ -157,5 +157,18 @@ Shape MemoryDescUtils::makeDummyShape(const Shape &shape, Dim dummyVal) { return Shape(dummyDims); } +Shape MemoryDescUtils::makeDummyShape(const Shape &shape, const VectorDims& dummyVals) { + if (shape.getRank() != dummyVals.size()) { + IE_THROW() << "makeDummyShape(): dummyVals vector size and shape ranks mismatch"; + } + const auto& minDims = shape.getMinDims(); + const auto& maxDims = shape.getMaxDims(); + const auto& dims = shape.getDims(); + VectorDims dummyDims(dims.size()); + for (size_t i = 0; i < dims.size(); ++i) { + dummyDims[i] = dims[i] == Shape::UNDEFINED_DIM ? std::min(maxDims[i], std::max(minDims[i], dummyVals[i])) : dims[i]; + } + return Shape(dummyDims); +} } // namespace intel_cpu } // namespace ov diff --git a/src/plugins/intel_cpu/src/memory_desc/cpu_memory_desc_utils.h b/src/plugins/intel_cpu/src/memory_desc/cpu_memory_desc_utils.h index 308de1fb1bf..58fd1b29067 100644 --- a/src/plugins/intel_cpu/src/memory_desc/cpu_memory_desc_utils.h +++ b/src/plugins/intel_cpu/src/memory_desc/cpu_memory_desc_utils.h @@ -93,12 +93,20 @@ public: /** * @brief Makes a static dummy shape where all undefined values are replaced with the smallest value between the parameter and the upper bound dim - * @param shape a shape from which the new static shape is generated + * @param shape a Shape object from which the new static shape is generated * @param dummyVal Dim value to replace undefined dimensions * @return a new Shape with dummy values instead of undefined dims */ static Shape makeDummyShape(const Shape& shape, Dim dummyVal = DEFAULT_DUMMY_VAL); + /** + * @brief Makes a static dummy shape where all undefined values are replaced with the smallest value between the parameter and the upper bound dim + * @param shape a Shape object from which the new static shape is generated + * @param dummyVals vector of values to replace undefined dimensions + * @return a new Shape with dummy values instead of undefined dims + */ + static Shape makeDummyShape(const Shape& shape, const VectorDims& dummyVals); + /** * @brief Converts dim to string, undefined dim represented as ? * @param dim Dim to be converted diff --git a/src/plugins/intel_cpu/src/nodes/conv.cpp b/src/plugins/intel_cpu/src/nodes/conv.cpp index 5b42df7df62..6da41b63676 100644 --- a/src/plugins/intel_cpu/src/nodes/conv.cpp +++ b/src/plugins/intel_cpu/src/nodes/conv.cpp @@ -826,8 +826,7 @@ void Convolution::createDescriptor(const std::vector& inputDesc, if (inputDesc[0]->isDefined()) { inpDesc = inputDesc[0]; } else { - auto dummyInDims = MemoryDescUtils::makeDummyShape(inputDesc[0]->getShape()).getStaticDims(); - dummyInDims[1] = IC; + auto dummyInDims = makeInputDummyShape(inputDesc[0]->getShape()); inpDesc = inputDesc[0]->cloneWithNewDims(dummyInDims); } DnnlMemoryDescPtr definedInpMemDesc = MemoryDescUtils::convertToDnnlMemoryDesc(inpDesc); @@ -917,7 +916,8 @@ void Convolution::addLegacyZeroPoints(dnnl::primitive_attr& attr) { void Convolution::SetPostOpsAndZeroPoints(std::vector &attrs) { // attr[0] - Legacy post ops + Legacy zero points. attrs.resize(1); - setPostOps(attrs[0], MemoryDescUtils::makeDummyShape(getOutputShapeAtPort(0)).getStaticDims(), true); + auto outputShape = outputStaticShape(); + setPostOps(attrs[0], outputShape, true); addLegacyZeroPoints(attrs[0]); //dw-conv would be fused into conv only on AVX2 platform. no need attr[1]. Avoid extra useless attribute. if (attrs[0].get_post_ops().get()->find(dnnl::impl::primitive_kind::convolution) != -1) { @@ -939,9 +939,9 @@ void Convolution::SetPostOpsAndZeroPoints(std::vector &att //WR to ONEDNN limitation. attr[1] - legacy post ops + stock zero point. //@todo:Unify to use binary postops+stock zero point when limitation is fixed. //For now, have to adapt to JIT_AMX kernel for performance. - setPostOps(attrs[1], MemoryDescUtils::makeDummyShape(getOutputShapeAtPort(0)).getStaticDims(), true); + setPostOps(attrs[1], outputShape, true); else - setPostOps(attrs[1], MemoryDescUtils::makeDummyShape(getOutputShapeAtPort(0)).getStaticDims(), false); + setPostOps(attrs[1], outputShape, false); addZeroPoints(attrs[1]); } @@ -1613,7 +1613,7 @@ void Convolution::initTryBrgconvFlag() { // should remove after binary postops performance issue resolved // heuristics: if it's avx512 ISA model && it doesn't have binary post ops or per channel zero point. dnnl::primitive_attr attr; - setPostOps(attr, MemoryDescUtils::makeDummyShape(getOutputShapeAtPort(0)).getStaticDims(), false); + setPostOps(attr, outputStaticShape(), false); const auto& ops = attr.get_post_ops(); if (ops.get()->find(dnnl::impl::primitive_kind::binary) != -1) { shouldTryBrgconv = false; @@ -1641,6 +1641,39 @@ void Convolution::initializeInputZeroPoints(const uint8_t* inputZpData, const si inputZeroPoints.push_back(static_cast(inputZpData[0])); } +VectorDims Convolution::makeInputDummyShape(const Shape& inpShape) const { + // There are a bunch of heuristics mostly aimed to guess the most appropriate oneDNN implementation, to reduce the + // amount of the implementation mismatch and the internal reordering as a consequence. + constexpr Dim dummyInputDim = 64; + + const size_t spatialRank = stride.size(); + const size_t filterStartIndx = weightDims.size() - spatialRank; + + VectorDims dummyInputShapeVals(inpShape.getRank(), dummyInputDim); + dummyInputShapeVals[1] = IC; //channels + + for (size_t i = 0; i < spatialRank; i++) { + if (weightDims[filterStartIndx + i] > dummyInputShapeVals[2 + i]) { + constexpr Dim dummyOutputDim = 16; + dummyInputShapeVals[2 + i] = (dummyOutputDim - 1) * stride[i] - + (paddingL[i] + paddingR[i]) + + weightDims[filterStartIndx + i] + + (weightDims[filterStartIndx + i]- 1) * (dilation[i]); + } + } + return MemoryDescUtils::makeDummyShape(inpShape, dummyInputShapeVals).getStaticDims(); +} + +VectorDims Convolution::outputStaticShape() const { + auto& outputShape = getOutputShapeAtPort(0); + if (outputShape.isDynamic()) { + auto inpDummyShape = makeInputDummyShape(getInputShapeAtPort(0)); + auto outputDims = shapeInferGeneric({ Shape(inpDummyShape), Shape(weightDims) }); + return Shape(outputDims.front()).getStaticDims(); + } + return outputShape.getStaticDims(); +} + } // namespace node } // namespace intel_cpu } // namespace ov diff --git a/src/plugins/intel_cpu/src/nodes/conv.h b/src/plugins/intel_cpu/src/nodes/conv.h index 4f79474ed74..63cea4ed6b8 100644 --- a/src/plugins/intel_cpu/src/nodes/conv.h +++ b/src/plugins/intel_cpu/src/nodes/conv.h @@ -112,7 +112,8 @@ private: void updatePadding(); MemoryDescPtr getSumMemDesc(dnnl::primitive_desc_iterator &primitive_desc_it); MemoryPtr getOutputMemory() const; - + VectorDims makeInputDummyShape(const Shape& inpShape) const; + VectorDims outputStaticShape() const; void appendLegacyZeroPointsArgs(); void appendZeroPointsArgs(); void initTryBrgconvFlag(); diff --git a/src/plugins/intel_cpu/tests/functional/single_layer_tests/convolution.cpp b/src/plugins/intel_cpu/tests/functional/single_layer_tests/convolution.cpp index f7b1e975baf..9303511ada2 100755 --- a/src/plugins/intel_cpu/tests/functional/single_layer_tests/convolution.cpp +++ b/src/plugins/intel_cpu/tests/functional/single_layer_tests/convolution.cpp @@ -214,31 +214,33 @@ TEST_P(ConvolutionLayerCPUTest, CompareWithRefs) { } } - // Skip tests for brgconv convolution where kernel size = 1x1 - if (priority[0] == "brgconv_avx512" || priority[0] == "brgconv_avx512_amx") { - bool is_1x1 = true; - for (const auto &i : kernel) { - if (i != 1) { - is_1x1 = false; - break; - } + if (!priority.empty()) { + // Skip tests for brgconv convolution where kernel size = 1x1 + if (priority[0] == "brgconv_avx512" || priority[0] == "brgconv_avx512_amx") { + bool is_1x1 = true; + for (const auto &i : kernel) { + if (i != 1) { + is_1x1 = false; + break; + } + } + if (is_1x1) { + GTEST_SKIP() << "Disabled test due to the brgconv does not support 1x1 convolution kernel." << std::endl; + } } - if (is_1x1) { - GTEST_SKIP() << "Disabled test due to the brgconv does not support 1x1 convolution kernel." << std::endl; - } - } - // Skip tests for brgconv_amx convolution where dilation is not 1 - if (priority[0].find("amx") != std::string::npos) { - bool dilation_is_1x1 = true; - for (const auto &i : dilation) { - if (i != 1) { - dilation_is_1x1 = false; - break; - } - } - if (!dilation_is_1x1) { - GTEST_SKIP() << "Disabled test due to the brgconv amx does not support non 1 dilation convolution kernel." << std::endl; + // Skip tests for brgconv_amx convolution where dilation is not 1 + if (priority[0].find("amx") != std::string::npos) { + bool dilation_is_1x1 = true; + for (const auto &i : dilation) { + if (i != 1) { + dilation_is_1x1 = false; + break; + } + } + if (!dilation_is_1x1) { + GTEST_SKIP() << "Disabled test due to the brgconv amx does not support non 1 dilation convolution kernel." << std::endl; + } } } @@ -1683,4 +1685,53 @@ INSTANTIATE_TEST_SUITE_P(smoke_Conv_winograd, ConvolutionLayerCPUTest, } // namespace winograd +/* ============= Large Filter Test ============= */ +namespace { + +const size_t outChannels = 80; + +const SizeVector kernel = { 251 }; +const SizeVector stride = { 10 }; +const std::vector padBegins = { 0 }; +const std::vector padEnds = { 0 }; +const SizeVector dilations = { 1 }; + +const auto convParams_1D = ::testing::Combine( + ::testing::Values(kernel), + ::testing::Values(stride), + ::testing::Values(padBegins), + ::testing::Values(padEnds), + ::testing::Values(dilations), + ::testing::Values(outChannels), + ::testing::Values(ngraph::op::PadType::EXPLICIT) +); + +std::vector inShapes = { + {{}, {{ 1, 1, 600 }}}, + { + //dynamic shape + { -1, 1, -1 }, + { //target static shapes + { 1, 1, 600 }, + { 10, 1, 700 }, + { 1, 1, 600 } + } + } +}; + +INSTANTIATE_TEST_SUITE_P(smoke_Conv_Large_Filter, ConvolutionLayerCPUTest, + ::testing::Combine( + ::testing::Combine( + convParams_1D, + ::testing::Values(ElementType::f32), + ::testing::Values(ElementType::f32), + ::testing::Values(ElementType::undefined), + ::testing::ValuesIn(inShapes), + ::testing::Values(CommonTestUtils::DEVICE_CPU)), + ::testing::Values(CPUSpecificParams{{}, {}, {}, CPUTestsBase::any_type}), + ::testing::Values(emptyFusingSpec), + ::testing::Values(cpuEmptyPluginConfig)), + ConvolutionLayerCPUTest::getTestCaseName); + +} // namespace } // namespace CPULayerTestsDefinitions diff --git a/src/plugins/intel_cpu/tests/functional/test_utils/cpu_test_utils.cpp b/src/plugins/intel_cpu/tests/functional/test_utils/cpu_test_utils.cpp index 3eb463ea2af..f2dc81a4f35 100644 --- a/src/plugins/intel_cpu/tests/functional/test_utils/cpu_test_utils.cpp +++ b/src/plugins/intel_cpu/tests/functional/test_utils/cpu_test_utils.cpp @@ -222,7 +222,7 @@ void CPUTestsBase::CheckPluginRelatedResultsImpl(const std::shared_ptr