SSE /优化 - 将数组复制到更大的数组中

时间:2014-10-16 06:57:01

标签: c++ arrays image-processing optimization sse

我正在尝试优化以下功能: (基本上它需要一行32位Ints,并将每个int复制到一个更大的目标arrray中,然后复制每一行

for(int i = 0; i < numLines; i++)
{
    pStartOfLine  = pDest;
    for(int j = 0; j < intsPerLineSrc; j++)
    {
        *pDest = *pSrc;                     // copy pixel A to FullSizeBuffer A
        pDest++;                            // Move dest Ptr to next Pixel
        *pDest = *pSrc;                     // Copy pixel A to FullsizeBuffer AGAIN  

        pDest++;                            // Move Src and Dst Pointrs to next pixels
       pSrc++;
    }

   memcpy(pDest, pStartOfLine, (8*intsPerLineSrc) );            // Duplicate the Line written to pDest, to next line of pDest.
   pDest = pDest + (2*intsPerLineSrc);                          // move pDest to Start of Next Line
}

有效地将图像缩放到两个尺寸的原始尺寸2 *。 现在这让我觉得应该从SIMD大量受益,但是我似乎找不到能够在这种特定情况下帮助我的正确的内在指令。

有人关心帮我吗? 或者我会在这么简单的操作中限制内存,重新分解SIMD是一种浪费吗?

是的,这部分代码最终在多个线程中运行,因此它已经是多线程的,但我认为SIMD优化可能更有帮助。

干杯,任何帮助/建议,

詹姆斯

1 个答案:

答案 0 :(得分:1)

您当前的操作是内存带宽限制。

如果您可以找到一种不处理整个图像的方法,而是处理块(例如16x16像素块到32x32像素块)并对每个块执行其他计算,那么您可以使您的操作减少内存带宽限制。< / p>

但是如果你必须处理整个图像,你应该考虑一些事情来实现最大内存带宽:

  1. 对于内存带宽限制操作,它们不会随核心数量而扩展,但它们会scale with the number of sockets。因此,如果你有一个双插槽系统,内存带宽是单个套接字系统的两倍(假设两个套接字使用他们通常做的相同的处理器)。但是,实现带宽的两倍can be tricky
  2. memcpy功能通常不会针对复制大尺寸进行优化。其中一个主要原因是它的许多实现都不使用非临时存储。非时间存储的经验法则是当大小大于最慢缓存大小的两倍时使用它们。我们假设您的处理器有12 MB的L3缓存。然后,如果目标图像的大小大于6MB,则应考虑使用非临时存储。这几乎可以肯定是你的情况,因为你的代码写的是32 MB。
  3. 以下是如何使用SSE2和非临时存储的示例

    int main() {
        int n = 16;
        int *src = (int*)_mm_malloc(n*sizeof(int),   16); //16 byte aligned 
        int *dst = (int*)_mm_malloc(2*n*sizeof(int), 16); //16 byte aligned
        for(int i=0; i<n; i++) src[i] = rand();
        for(int i=0; i<n; i+=4) {
            __m128i  x = _mm_load_si128((__m128i*)&src[i]);
            __m128i lo = _mm_shuffle_epi32(x, 0x50); // 0x50 = 1100 in base 4  
            __m128i hi = _mm_shuffle_epi32(x, 0xfa); // 0xfa = 3322 in base 4
            _mm_stream_si128((__m128i*)&dst[2*i+0], lo); //non-temporal store
            _mm_stream_si128((__m128i*)&dst[2*i+4], hi); //non-temporal store
            //_mm_store_si128((__m128i*)&dst[2*i+0], lo);
            //_mm_store_si128((__m128i*)&dst[2*i+4], hi);
        }
        //for(int i=0; i<n; i++) printf("%x ", src[i]); printf("\n");
        //for(int i=0; i<(2*n); i++) printf("%x ", dst[i]); printf("\n");
    }
    

    在您的情况下,将n替换为像素数。如果n不是四的倍数,那么你必须做一点清理,我没有在这里做。时间存储必须是16字节对齐才能执行此操作,这就是我对齐dst的原因。但是,src不一定是16字节对齐,因此您可以使用_mm_loadu_si128而不是src

    一旦您为单个线程获得最大带宽并假设您拥有多插槽系统,您应该尝试从两个套接字获得最大带宽。我没有足够的经验来帮助我,但我认为可以使用numactl来实现。有关示例,请参阅why-doesnt-this-code-scale-linearly

相关问题