Repository: opencv/opencv
File: modules/imgproc/src/median_blur.simd.hpp (C++)

Code before:
                    WT p0 = row0[j0], p1 = row0[j], p2 = row0[j2];
                    WT p3 = row1[j0], p4 = row1[j], p5 = row1[j2];
                    WT p6 = row2[j0], p7 = row2[j], p8 = row2[j2];

                    op(p1, p2); op(p4, p5); op(p7, p8); op(p0, p1);
                    op(p3, p4); op(p6, p7); op(p1, p2); op(p4, p5);
                    op(p7, p8); op(p0, p3); op(p5, p8); op(p4, p7);
                    op(p3, p6); op(p1, p4); op(p2, p5); op(p4, p7);
                    op(p4, p2); op(p6, p4); op(p4, p2);
                    dst[j] = (T)p4;
                }

                if( limit == size.width )
                    break;

#if (CV_SIMD || CV_SIMD_SCALABLE)
                int nlanes = VTraits<typename VecOp::arg_type>::vlanes();
#else
                int nlanes = 1;
#endif
                if(_dst.cols > 2*nlanes) //minimum size to safely allow extra vectorized store into next row.
                {
                    /* Reducing bottleneck in non-vectorized path beyond (size.width - nlanes - cn)
                    /* In the last iteration of the loop below, even if residue is less than nlanes, we allow it to be completed in vectorized path.
                    /* Though we do additional store in every row, it will be overwritten by correct value in the next row. */
                    for( ; j < size.width - cn ; )
                    {
                        VT p0 = vop.load(row0+j-cn), p1 = vop.load(row0+j), p2 = vop.load(row0+j+cn);
                        VT p3 = vop.load(row1+j-cn), p4 = vop.load(row1+j), p5 = vop.load(row1+j+cn);
                        VT p6 = vop.load(row2+j-cn), p7 = vop.load(row2+j), p8 = vop.load(row2+j+cn);

                        vop(p1, p2); vop(p4, p5); vop(p7, p8); vop(p0, p1);
                        vop(p3, p4); vop(p6, p7); vop(p1, p2); vop(p4, p5);
                        vop(p7, p8); vop(p0, p3); vop(p5, p8); vop(p4, p7);
                        vop(p3, p6); vop(p1, p4); vop(p2, p5); vop(p4, p7);
                        vop(p4, p2); vop(p6, p4); vop(p4, p2);
                        vop.store(dst+j, p4);

                        int offset = nlanes;
                        if( j > (size.width - nlanes - cn) )
                        {
                            offset = size.width - j - cn;
                        }
                        j += offset;

                        /*In the last row(height-1) processing, additional store would overwrite outside output buffer.
                        Its avoided with below check. */
                        if((i == size.height-1) && ( j >= size.width - cn - nlanes))
                        {
                            break;
                        }

Review comment (suggestion):
If you meant to take care of the tail with vectorization, you should take a look at this trick (line 161 to 166):

https://github.com/opencv/opencv/blob/cb87f05e4bbae077e266d15b571ed8b23598d265/modules/core/src/convert.simd.hpp#L157-L171

Code after:
                    WT p0 = row0[j0], p1 = row0[j], p2 = row0[j2];
                    WT p3 = row1[j0], p4 = row1[j], p5 = row1[j2];
                    WT p6 = row2[j0], p7 = row2[j], p8 = row2[j2];

                    op(p1, p2); op(p4, p5); op(p7, p8); op(p0, p1);
                    op(p3, p4); op(p6, p7); op(p1, p2); op(p4, p5);
                    op(p7, p8); op(p0, p3); op(p5, p8); op(p4, p7);
                    op(p3, p6); op(p1, p4); op(p2, p5); op(p4, p7);
                    op(p4, p2); op(p6, p4); op(p4, p2);
                    dst[j] = (T)p4;
                }

                if( limit == size.width )
                    break;

#if (CV_SIMD || CV_SIMD_SCALABLE)
                int nlanes = VTraits<typename VecOp::arg_type>::vlanes();
#else
                int nlanes = 1;
#endif
                for (; j < size.width - cn; j += nlanes)
                {
                    //handling tail in vectorized path itself
                    if ( j > size.width - cn - nlanes ) {
                        if (j == cn || src == dst) {
                            break;
                        }
                        j = size.width - cn - nlanes;
                    }

                    VT p0 = vop.load(row0+j-cn), p1 = vop.load(row0+j), p2 = vop.load(row0+j+cn);
                    VT p3 = vop.load(row1+j-cn), p4 = vop.load(row1+j), p5 = vop.load(row1+j+cn);
                    VT p6 = vop.load(row2+j-cn), p7 = vop.load(row2+j), p8 = vop.load(row2+j+cn);

                    vop(p1, p2); vop(p4, p5); vop(p7, p8); vop(p0, p1);
                    vop(p3, p4); vop(p6, p7); vop(p1, p2); vop(p4, p5);
                    vop(p7, p8); vop(p0, p3); vop(p5, p8); vop(p4, p7);
                    vop(p3, p6); vop(p1, p4); vop(p2, p5); vop(p4, p7);
                    vop(p4, p2); vop(p6, p4); vop(p4, p2);
                    vop.store(dst+j, p4);

                }

                limit = size.width;
            }
        }
    }
    else if( m == 5 )
    {
        if( size.width == 1 || size.height == 1 )
        {