如何进一步优化这个循环

how do I optimize this loop further?

本文关键字:循环 优化 进一步      更新时间:2023-10-16

嗯,这篇文章是为什么memcpy()和memmove()比指针增量更快的原因?

我有一个带有4个通道的自定义图像结构,我喜欢将其中3个(RGB)提取到具有3个通道的openCV cv::MAt结构中。

cv::Mat GetColorImgFromFrame(Image* pimg)
{
    int iHeight = pimg->m_imageInfo.m_height;
    int iWidth = pimg->m_imageInfo.m_width;
    cv::Mat cvmColorImg = cv::Mat::zeros(iHeight, iWidth, CV_8UC3);
    int iDestStep = cvmColorImg.channels(); // 3
    uchar* pucSrc = pimg->m_data;    
    uchar* pucDest = cvmColorImg.data;
    uchar* pucDestLimit = cvmColorImg.data + iHeight*iWidth*iDestStep;
    for (;pucDest < pucDestLimit;)
    {       
        *(pucDest++) = *(pucSrc++);
        *(pucDest++) = *(pucSrc++);
        *(pucDest++) = *(pucSrc++);
        pucSrc++;
    }
    return cvmColorImg;
}

用memcpy(pucDest, pucSrc, 3)代替三个内部赋值似乎没有多大帮助。有什么建议吗?

在使用SIMD intrinsic进入"assembler like"模式之前,您可以尝试告诉编译器源指针和目标指针不别名,例如使用gcc的__restrict__关键字或visual studio的__restrict关键字

在32位机器上,您可以尝试使用unsigned int *从源到目标每个循环一次复制4字节。诀窍是保持pucDestuchar指针增加3。您必须将最后一个单词作为特殊情况处理,以免最后一个字节违反目标数组的数组边界。

由于在大多数机器上复制int型通常不会比复制char型慢,因此我认为这将使您的速度提高大约3倍。

扩展Doc Brown的答案,展开循环,这样你每读四次就写三个uint32_t整数(uint64_t将需要更大的大口,但会更难处理)。

如果您的pimg->m_datacvmColorImg.data不在uint32_t边界上开始,则读写int可能会出现问题。您可以通过在这些char*数据成员之前放置intunsigned int数据成员来强制实现这一点。或者你可以使用类似达夫的设备。个人偏好:我只会强制对齐。它使复制代码更干净、更容易,也许还更快。为填充而浪费的几个字节是一个很小的成本。

与展开循环的典型情况一样,您必须做一些特殊的事情来处理结束。(达夫的设备一开始就能处理奇怪的事情,所以这里不要用达夫的设备。)如果您填充输出缓冲区的末尾,使其占用4*N字节,那么循环结束处理的许多问题将会消失。