1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

dnn:rvv: restore the fixed block size C0 == 8

#29304 made the net-wide block size follow the RVV vector width
(defaultC0 = max(8, vlanes())), so the blocked memory layout of every tensor
depended on the host's VLEN and on the LMUL of the universal intrinsics in core.
The same model gets a different layout on a 128-, 256- or 1024-bit machine.

Restore the fixed default of 8, which is the mainstream, well-tested setting on
every platform. C0/K0 become compile-time constants again on RVV as they already
were elsewhere, so the runtime block-size plumbing and the vlanes()-based scratch
sizing are dropped, and getConvFunc_ no longer advertises block sizes the kernels
cannot handle.

The blocked kernels currently require C0 == vlanes(), which this alone does not
satisfy; the next commit removes that requirement.
This commit is contained in:
velonica0
2026-07-13 00:11:55 -07:00
parent bc647fe3ec
commit 11e0a41bf6
4 changed files with 5 additions and 48 deletions
@@ -38,14 +38,7 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
parallel_for_(Range(0, NC1), [&](const Range& range)
{
constexpr int MAX_CONV_DIMS = ConvState::MAX_CONV_DIMS;
#if CV_SIMD_SCALABLE
// RVV: universal intrinsics use LMUL=2, so the float vector width tracks
// the hardware VLEN at runtime. The block size C0 == defaultC0 == vlanes()
// (e.g. 16 at VLEN=256), so read it at runtime instead of fixing it to 8 (#28852).
const int C0 = (int)cs.inpshape.back();
#else
constexpr int C0 = 8;
#endif
CV_Assert(cs.nspatialdims <= MAX_CONV_DIMS && MAX_CONV_DIMS == 3);
CV_Assert(C0 == cs.inpshape.back());
@@ -80,14 +73,7 @@ static void depthwiseConv32f(const void* inp__, const void* residual__,
const float* activParams = cs.activParams.data();
ActivationFunc activation = cs.activation;
float maxval = FLT_MAX, defaultAlpha = 0.f;
#if CV_SIMD_SCALABLE
// C0 is runtime on RVV; size scratch by the compile-time upper bound.
float scalebuf[VTraits<v_float32>::max_nlanes];
float biasbuf[VTraits<v_float32>::max_nlanes];
float alphabuf[VTraits<v_float32>::max_nlanes];
#else
float scalebuf[C0], biasbuf[C0], alphabuf[C0];
#endif
if (fastActivation == FAST_ACTIV_CLIP) {
CV_Assert(cs.activParams.size() == 2u);
maxval = activParams[1];
@@ -2202,17 +2202,11 @@ static void conv32fC8(const void* inp__, const void* residual__, void* out__,
parallel_for_(Range(0, total_tasks_gen), [&](const Range& range) {
constexpr int SPAT_BLOCK_SIZE = 10;
#if CV_SIMD_SCALABLE
// RVV: block size follows the runtime vector width (defaultC0 = vlanes(), LMUL=2).
const int C0 = (int)inpshape.back();
int C0shift = 0; while ((1 << C0shift) < C0) C0shift++;
const int K0 = C0, K0shift = C0shift;
constexpr int C0BUF = VTraits<v_float32>::max_nlanes; // compile-time scratch bound
#else
// The block size is a fixed property of the blocked layout on every platform,
// so C0/K0 are compile-time constants here (#29454).
constexpr int C0shift = 3, K0shift = C0shift;
constexpr int C0 = 1 << C0shift, K0 = C0;
constexpr int C0BUF = K0;
#endif
CV_Assert_N(inpshape.back() == C0, outshape.back() == K0);
@@ -2557,16 +2551,9 @@ static void conv32fC8(const void* inp__, const void* residual__, void* out__,
cv::dnn::ConvFunc getConvFunc_(int depth, int C0)
{
ConvFunc func = nullptr;
#if CV_SIMD_SCALABLE
// RVV: block size follows the runtime vector width; accept the supported pow2 widths.
if (depth == CV_32F && (C0 == 8 || C0 == 16 || C0 == 32 || C0 == 64)) {
func = conv32fC8;
}
#else
if (depth == CV_32F && C0 == 8) {
func = conv32fC8;
}
#endif
return func;
}
+3 -8
View File
@@ -5,7 +5,6 @@
#include "precomp.hpp"
#include "net_impl.hpp"
#include "opencv2/core/hal/intrin.hpp"
#ifdef HAVE_ONNXRUNTIME
#include <onnxruntime_cxx_api.h>
@@ -74,14 +73,10 @@ Net::Impl::Impl()
// onnx_opset = 0;
accuracy = CV_32F;
// The block size is a network-wide memory-layout contract, so it must not depend
// on the SIMD width of the host: C0 == 8 is the mainstream, well-tested setting.
// (Deriving it from vlanes() made the layout hardware-dependent; see #29454.)
defaultC0 = DEFAULT_C0;
#if CV_SIMD_SCALABLE
// RVV: the universal intrinsics use LMUL=2, so the float vector width is
// (VLEN/32)*2 (16 at VLEN=256). The blocked-layout kernels fill a full
// vector per channel block, so the net-wide block size must match that
// width rather than the fixed default of 8 (#28852).
defaultC0 = std::max((int)DEFAULT_C0, VTraits<v_float32>::vlanes());
#endif
enableFP16 = haveFP16 = false;
// FP16 is not ready yet in the new DNN engine
// Ticket: https://github.com/opencv/opencv/issues/26196
-11
View File
@@ -5,7 +5,6 @@
#include "precomp.hpp"
#include "net_impl.hpp"
#include "opencv2/core/hal/intrin.hpp"
#include <limits>
@@ -546,16 +545,6 @@ void Net::Impl::prepareForInference()
#endif
if (!prepared) {
#if CV_SIMD_SCALABLE
// RVV (#28852): keep quantized graphs at C0=8. The int8 kernels are hardwired to
// C0=8 (VNNI/NEON weight packing + per-channel quantization) and run scalar on RVV,
// so a wider block gives no benefit and breaks them. Only fp32 graphs use the wider
// vlanes()-based defaultC0. Signed-int8 (CV_8S) args are the quantization signature
// (uint8 image inputs are CV_8U, so they don't trigger this).
for (const ArgData& a : args) {
if (a.type == CV_8S) { defaultC0 = 8; break; }
}
#endif
fuseQDQ();
constFold();
fuseBN();