mirror of
https://github.com/opencv/opencv.git
synced 2026-09-12 05:11:04 -05:00
dnn: vectorize fast_norm for scalable-vector (RVV) targets - #29598 ### Problem The normalization CPU kernels in `fast_norm.cpp` run **scalar** on RISC-V RVV scalable-vector builds. Their vector code is gated by `#if CV_SIMD`, and on scalable targets `intrin.hpp` sets `CV_SIMD 0` / `CV_SIMD_SCALABLE 1`, so the blocks are dropped by the preprocessor. LayerNorm / RMSNorm / InstanceNorm / GroupNorm / MVN are therefore scalar there. Independently, the `#if CV_SIMD` blocks only covered the block-layout path — the NCHW paths these layers actually use had no explicit SIMD, and the compiler does not auto-vectorize them (the per-element `j/step` channel-index division in the GroupNorm apply and the float→double widening reduction defeat it). These kernels are the last scalar piece of the new-engine (`ENGINE_NEW`) transformer norm path; they are shared by the classic engine as well. ### Changes - Guards → `#if (CV_SIMD || CV_SIMD_SCALABLE)` (6 sites; f64 → `#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F`), no fixed-width `::nlanes` — same idiom already used across `modules/dnn/src`. - Vectorized the NCHW mean/variance reduction (new `normAccumSumSqSum` / `normAccumSumSqSum64f`, float/double accumulators matching the scalar reference) and the affine-apply loops. - `fastNormGroup` apply hoists the per-channel scale/bias out of the inner loop so the `j/step` division no longer blocks vectorization. ### Testing — SpacemiT K1 (rv64gcv, VLEN=256, 8×1.6 GHz, governor=performance), 5.x Built `-DCPU_BASELINE=RVV -DRISCV_RVV_SCALABLE=ON`. **Correctness — zero new failures.** Default `ENGINE_AUTO`: | Filter | baseline | patch | |---|---|---| | `*LayerNorm*:*InstanceNorm*:*MVN*:*Norm*:*GroupNorm*` | 27/27 pass | 27/27 pass | | `*Test_ONNX_layers*` | 263 pass / 1 fail (`Tile`, pre-existing) | 263 pass / 1 fail (`Tile`) | Re-run with `OPENCV_FORCE_DNN_ENGINE=2` : the supported subset (LayerNorm/InstanceNorm/GroupNorm) passes on both baseline and patch (MVN is not implemented in the new engine and falls back to classic under AUTO). **Performance** — `opencv_perf_dnn`, geomean of 3 rounds: | Test | shape | base ms (1thread / 8threads) | patch ms (1t / 8t) | speedup (1t / 8t) | |---|---|---|---|---| | GroupNorm::Layer | {2,64,180,240}, g=16 | 104.6 / 14.36 | 12.93 / 10.75 | **8.1×** / 1.34× | | InstanceNorm::Layer | {2,64,180,240} | 52.96 / 11.76 | 13.63 / 10.72 | **3.9×** / 1.10× | | LayerNorm::Layer | {1,50,768} | 0.215 / 0.072 | 0.104 / 0.068 | **2.1×** / ~1.0× | New engine confirmed directly via `readNetFromONNX(layernorm.onnx, ENGINE_NEW)`: 1×512×768 single-thread 2.74 → 1.62 ms (**1.69×**); a base-vs-patch delta under `ENGINE_NEW` proves the new engine executes the changed kernel. ### 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 - [ ] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake