mirror of
https://github.com/opencv/opencv.git
synced 2026-09-11 04:43:22 -05:00
Merge pull request #29831 from pratham-mcw:nchw_nchw_opt
dnn: fix eltwise layer memory traversal pattern causing Windows-ARM64 slowdown - #29831 ## Problem - `EltwiseInvoker::operator()` (backs the `Eltwise` layer's SUM/PROD/DIV ops) ran slower on Windows-ARM64 than x64. ## Root cause - The loop tiled the output into fixed-size blocks and looped over the **channel axis inside each block**. - In NCHW layout, consecutive channels of the same sample sit far apart in memory (`planeSize * 4` bytes apart). - So each block jumped between ~256 memory locations per tensor — roughly **768 concurrent strided streams** across the three tensors involved. - Hardware prefetchers can only track a small, fixed number of streams and Windows-ARM64's prefetcher falls over on this pattern. ## Fix - Walk the output buffer contiguously and track the current (sample, channel) plane from the offset and clamp each block so it never crosses into the next channel. - This removes the inner channel loop entirely: one channel finishes before the next starts, so every tensor is read/written sequentially instead of jumping around. - No behavior change, only the traversal order is different. ## Performance Benchmarks: <img width="1479" height="208" alt="image" src="https://github.com/user-attachments/assets/0f878757-d988-4564-b166-96d80834c77e" />
This commit is contained in:
@@ -411,7 +411,7 @@ public:
|
||||
void operator()(const Range& r) const CV_OVERRIDE
|
||||
{
|
||||
const EltwiseOp op = self.op;
|
||||
size_t total = dst->size[0]*planeSize;
|
||||
size_t total = dst->total();
|
||||
size_t stripeSize = (total + nstripes - 1)/nstripes;
|
||||
size_t stripeStart = r.start*stripeSize;
|
||||
size_t stripeEnd = std::min(r.end*stripeSize, total);
|
||||
@@ -419,154 +419,94 @@ public:
|
||||
float* dstptr0 = dst->ptr<float>();
|
||||
int blockSize0 = 1 << 12;
|
||||
|
||||
size_t planeIdx = stripeStart / planeSize;
|
||||
int delta = (int)(stripeStart - planeIdx * planeSize);
|
||||
int sampleIdx = (int)(planeIdx / channels);
|
||||
int c = (int)(planeIdx % channels);
|
||||
|
||||
for (size_t ofs = stripeStart; ofs < stripeEnd; )
|
||||
{
|
||||
int sampleIdx = (int)(ofs / planeSize);
|
||||
int delta = (int)ofs - sampleIdx * planeSize;
|
||||
int blockSize = std::min(blockSize0, std::min((int)(stripeEnd - ofs), (int)planeSize - delta));
|
||||
if( blockSize <= 0 )
|
||||
break;
|
||||
|
||||
size_t dstIdx = ofs;
|
||||
float* dstptr = dstptr0 + dstIdx;
|
||||
ofs += blockSize;
|
||||
|
||||
for (int c = 0; c < channels; c++)
|
||||
{
|
||||
size_t dstIdx = delta + (sampleIdx*channels + c)*planeSize;
|
||||
float* dstptr = dstptr0 + dstIdx;
|
||||
const float* srcptr0 = srcs[0]->ptr<float>() + dstIdx;
|
||||
|
||||
// process first two inputs
|
||||
const int inputIdx = 1;
|
||||
int src1_channels = srcNumChannels[inputIdx];
|
||||
if (c >= src1_channels)
|
||||
{
|
||||
const float* srcptr0 = srcs[0]->ptr<float>() + dstIdx;
|
||||
|
||||
const int inputIdx = 1;
|
||||
int src1_channels = srcNumChannels[inputIdx];
|
||||
if (c >= src1_channels)
|
||||
// no data from second input
|
||||
if (!coeffsptr || coeffsptr[0] == 1.0f)
|
||||
{
|
||||
// no data from second input
|
||||
if (!coeffsptr || coeffsptr[0] == 1.0f)
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = srcptr0[j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float c0 = coeffsptr[0];
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = c0*srcptr0[j];
|
||||
}
|
||||
dstptr[j] = srcptr0[j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t srcIdx = delta + (sampleIdx * src1_channels + c) * planeSize;
|
||||
const float* srcptrI = srcs[inputIdx]->ptr<float>() + srcIdx;
|
||||
|
||||
if (op == PROD)
|
||||
float c0 = coeffsptr[0];
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = srcptr0[j] * srcptrI[j];
|
||||
}
|
||||
dstptr[j] = c0*srcptr0[j];
|
||||
}
|
||||
else if (op == DIV)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = srcptr0[j] / srcptrI[j];
|
||||
}
|
||||
}
|
||||
else if (op == MAX)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = std::max(srcptr0[j], srcptrI[j]);
|
||||
}
|
||||
}
|
||||
else if (op == MIN)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = std::min(srcptr0[j], srcptrI[j]);
|
||||
}
|
||||
}
|
||||
else if (op == SUM)
|
||||
{
|
||||
if (!coeffsptr || (coeffsptr[0] == 1.0f && coeffsptr[1] == 1.0f))
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = srcptr0[j] + srcptrI[j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float c0 = coeffsptr[0];
|
||||
float c1 = coeffsptr[1];
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = c0*srcptr0[j] + c1*srcptrI[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsInternal, "");
|
||||
}
|
||||
}
|
||||
|
||||
// aggregate other inputs (3+)
|
||||
for (size_t inputIdx = 2; inputIdx < nsrcs; inputIdx++)
|
||||
else
|
||||
{
|
||||
int srcI_channels = srcNumChannels[inputIdx];
|
||||
if (c >= srcI_channels)
|
||||
continue; // no data from second input
|
||||
size_t srcIdx = delta + (sampleIdx * srcI_channels + c) * planeSize;
|
||||
size_t srcIdx = delta + (sampleIdx * src1_channels + c) * planeSize;
|
||||
const float* srcptrI = srcs[inputIdx]->ptr<float>() + srcIdx;
|
||||
|
||||
if (op == PROD)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] *= srcptrI[j];
|
||||
dstptr[j] = srcptr0[j] * srcptrI[j];
|
||||
}
|
||||
}
|
||||
else if (op == DIV)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] /= srcptrI[j];
|
||||
dstptr[j] = srcptr0[j] / srcptrI[j];
|
||||
}
|
||||
}
|
||||
else if (op == MAX)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = std::max(dstptr[j], srcptrI[j]);
|
||||
dstptr[j] = std::max(srcptr0[j], srcptrI[j]);
|
||||
}
|
||||
}
|
||||
else if (op == MIN)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = std::min(dstptr[j], srcptrI[j]);
|
||||
dstptr[j] = std::min(srcptr0[j], srcptrI[j]);
|
||||
}
|
||||
}
|
||||
else if (op == SUM)
|
||||
{
|
||||
if (!coeffsptr || coeffsptr[inputIdx] == 1.0f)
|
||||
if (!coeffsptr || (coeffsptr[0] == 1.0f && coeffsptr[1] == 1.0f))
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] += srcptrI[j];
|
||||
dstptr[j] = srcptr0[j] + srcptrI[j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float cI = coeffsptr[inputIdx];
|
||||
float c0 = coeffsptr[0];
|
||||
float c1 = coeffsptr[1];
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] += cI * srcptrI[j];
|
||||
dstptr[j] = c0*srcptr0[j] + c1*srcptrI[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -575,10 +515,79 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// aggregate other inputs (3+)
|
||||
for (size_t inputIdx = 2; inputIdx < nsrcs; inputIdx++)
|
||||
{
|
||||
int srcI_channels = srcNumChannels[inputIdx];
|
||||
if (c >= srcI_channels)
|
||||
continue; // no data from second input
|
||||
size_t srcIdx = delta + (sampleIdx * srcI_channels + c) * planeSize;
|
||||
const float* srcptrI = srcs[inputIdx]->ptr<float>() + srcIdx;
|
||||
|
||||
if (op == PROD)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] *= srcptrI[j];
|
||||
}
|
||||
}
|
||||
else if (op == DIV)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] /= srcptrI[j];
|
||||
}
|
||||
}
|
||||
else if (op == MAX)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = std::max(dstptr[j], srcptrI[j]);
|
||||
}
|
||||
}
|
||||
else if (op == MIN)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] = std::min(dstptr[j], srcptrI[j]);
|
||||
}
|
||||
}
|
||||
else if (op == SUM)
|
||||
{
|
||||
if (!coeffsptr || coeffsptr[inputIdx] == 1.0f)
|
||||
{
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] += srcptrI[j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float cI = coeffsptr[inputIdx];
|
||||
for (int j = 0; j < blockSize; j++)
|
||||
{
|
||||
dstptr[j] += cI * srcptrI[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsInternal, "");
|
||||
}
|
||||
|
||||
if( activ )
|
||||
{
|
||||
float* ptr = dstptr0 + delta + sampleIdx*channels*planeSize;
|
||||
activ->forwardSlice(ptr, ptr, blockSize, planeSize, 0, channels);
|
||||
activ->forwardSlice(dstptr, dstptr, blockSize, planeSize, c, c + 1);
|
||||
}
|
||||
|
||||
delta += blockSize;
|
||||
if (delta == (int)planeSize)
|
||||
{
|
||||
delta = 0;
|
||||
if (++c == channels)
|
||||
{
|
||||
c = 0;
|
||||
sampleIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user