From 371b80210390ea3e0e29f3feadfae6f621656e40 Mon Sep 17 00:00:00 2001 From: Hanbin Bae Date: Fri, 30 Jan 2026 21:27:30 +0900 Subject: [PATCH] Merge pull request #28442 from Anemptyship:optimize/resize-parallel optimize(dnn): parallelize Resize layer implementation #28442 ### optimize(dnn): parallelize Resize layer implementation This PR addresses the `TODO` in `modules/dnn/src/layers/resize_layer.cpp` regarding the slow implementation of the `Resize` layer when using `opencv_linear` or `nearest` interpolation with specific configurations. ### Changes - Replaced the serial nested loop (batch x channels) with `cv::parallel_for_`. - This allows OpenCV to utilize multi-threading for the resizing operation, which was previously single-threaded for these specific paths. ### Performance Results Benchmark run on 20-thread CPU (AVX2): | Test Case | Resolution Change | Before (ms) | After (ms) | Speedup | | :--- | :--- | :--- | :--- | :--- | | **Upsample Linear** | 64x64 -> 128x128 | 1.47 ms | **0.18 ms** | **~8.1x** | | **Downsample Nearest** | 128x128 -> 64x64 | 1.55 ms | **0.45 ms** | **~3.4x** | **Test Configuration:** - **Upsample**: `[4, 64, 64, 64]` -> `[4, 64, 128, 128]` (Factor: 2.0, Linear) - **Downsample**: `[4, 128, 128, 128]` -> `[4, 128, 64, 64]` (Factor: 0.5, Nearest) ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable - Added `modules/dnn/perf/perf_resize.cpp` to verify performance gains. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/dnn/perf/perf_resize.cpp | 65 +++++++++++++++++++++++++ modules/dnn/src/layers/resize_layer.cpp | 15 ++++-- 2 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 modules/dnn/perf/perf_resize.cpp diff --git a/modules/dnn/perf/perf_resize.cpp b/modules/dnn/perf/perf_resize.cpp new file mode 100644 index 0000000000..f43e01a70c --- /dev/null +++ b/modules/dnn/perf/perf_resize.cpp @@ -0,0 +1,65 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +#include "perf_precomp.hpp" + +namespace opencv_test { + +struct Layer_Resize : public TestBaseWithParam> +{ + void test_layer(const std::vector& inpShape, int outH, int outW, const String& interp) + { + int backendId = get<0>(GetParam()); + int targetId = get<1>(GetParam()); + + Mat input(inpShape, CV_32FC1); + randu(input, 0.f, 1.f); + + Net net; + LayerParams lp; + lp.type = "Resize"; + lp.name = "testLayer"; + lp.set("interpolation", interp); + lp.set("width", outW); + lp.set("height", outH); + + + + int id = net.addLayerToPrev(lp.name, lp.type, lp); + net.connect(0, 0, id, 0); + + // warmup + { + net.setInputsNames({"data"}); + net.setInput(input, "data"); + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); + Mat out = net.forward(); + } + + TEST_CYCLE() + { + Mat res = net.forward(); + } + + SANITY_CHECK_NOTHING(); + } +}; + +PERF_TEST_P_(Layer_Resize, Resize_Upsample_Linear) +{ + // N=4, C=64, H=64, W=64 -> 128x128 (x2 upsample) + // Common in segmentation/detection heads + test_layer({4, 64, 64, 64}, 128, 128, "opencv_linear"); +} + +PERF_TEST_P_(Layer_Resize, Resize_Downsample_Nearest) +{ + // N=4, C=128, H=128, W=128 -> 64x64 (x0.5 downsample) + test_layer({4, 128, 128, 128}, 64, 64, "nearest"); +} + +INSTANTIATE_TEST_CASE_P(/**/, Layer_Resize, dnnBackendsAndTargets()); + +} // namespace opencv_test diff --git a/modules/dnn/src/layers/resize_layer.cpp b/modules/dnn/src/layers/resize_layer.cpp index 219e282aa4..4cc9723d85 100644 --- a/modules/dnn/src/layers/resize_layer.cpp +++ b/modules/dnn/src/layers/resize_layer.cpp @@ -244,15 +244,20 @@ public: { // INTER_LINEAR Resize mode does not support INT8 inputs InterpolationFlags mode = interpolation == "nearest" ? INTER_NEAREST : INTER_LINEAR; - // [TODO] this is a really slow approach; need to rewrite it completely. - for (size_t n = 0; n < inputs[0].size[0]; ++n) - { - for (size_t ch = 0; ch < inputs[0].size[1]; ++ch) + + size_t nbatch = inputs[0].size[0]; + size_t nch = inputs[0].size[1]; + size_t total_planes = nbatch * nch; + + parallel_for_(Range(0, (int)total_planes), [&](const Range& range){ + for (int i = range.start; i < range.end; ++i) { + int n = i / nch; + int ch = i % nch; resize(getPlane(inp, n, ch), getPlane(out, n, ch), Size(outWidth, outHeight), 0, 0, mode); } - } + }); } else if (interpolation == "nearest") {