如何进一步优化此循环?

时间:2011-10-15 08:55:34

标签: c++ for-loop

嗯,这篇文章是Why is memcpy() and memmove() faster than pointer increments?

的原因

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

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)替换三个内部赋值似乎没什么帮助。有什么建议?

3 个答案:

答案 0 :(得分:3)

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

由于复制int通常不比在大多数机器上复制char慢,我怀疑这会使你的速度增加大约3倍。

答案 1 :(得分:3)

在使用SIMD intrinsic进入“汇编程序”模式之前,您可以尝试告诉编译器源和目标指针不是别名,例如使用带有gcc或{的__restrict__关键字{1}}使用visual studio

答案 2 :(得分:1)

扩展Doc Brown的答案,展开循环,这样你就可以为每四次读取编写三个uint32_t个整数(uint64_t将会获得更大的吸引力,但会更难处理)。

如果pimg->m_datacvmColorImg.data未在uint32_t边界上开始,则读取和写入整数可能会有问题。您可以通过在这些int数据成员之前添加unsigned intchar*数据成员来强制执行此操作。或者你可以使用类似于Duff设备的东西。个人偏好:我只想强制对齐。它使复制代码更清晰,更简单,也许更快。填充的一些浪费字节是一个很小的成本。

通常情况下,展开的循环,你必须做一些特殊的事情来处理结束。 (Duff的设备在开始时处理奇怪的事情,所以不要在这里使用Duff的设备。)如果填充输出缓冲区的末尾使其占用4 * N字节,循环处理结束的许多问题将消失