C++ 在16位整数数组中放入8位整数的最快方法是什么

C++ 在16位整数数组中放入8位整数的最快方法是什么,c++,c++11,optimization,C++,C++11,Optimization,我正在做一个处理图像的程序。如果我能将RGBA值存储在16位整数中,我就可以通过使用SSE来提高性能(没有溢出的风险)。然而,从8位整数到16位整数的转换是瓶颈。将有符号8位整数放入16位整数数组的最快方法是什么?16位整数数组是 int8_t a[128]; int16_t b[128]; for (int i=0;i<128;i++) b[i]=a[i]; int8_t a[128]; int16_t b[128]; 对于(inti=0;iClang)代码,将使用-O

我正在做一个处理图像的程序。如果我能将RGBA值存储在16位整数中,我就可以通过使用SSE来提高性能(没有溢出的风险)。然而,从8位整数到16位整数的转换是瓶颈。将有符号8位整数放入16位整数数组的最快方法是什么?16位整数数组是

int8_t a[128];
int16_t b[128];

for (int i=0;i<128;i++)
       b[i]=a[i];
int8_t a[128];
int16_t b[128];

对于(inti=0;iClang)代码,将使用-O2对该代码进行矢量化

#include <cstdlib>
#include <cstdint>
#include <cstdio>

const int size = 128;
uint8_t a[size];
int16_t b[size];


static __inline__ unsigned long long rdtsc(void)
{
    unsigned hi, lo;
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}


void convert(uint8_t* src, int16_t* dest)
{
    for (int i=0;i<size;i++)
    dest[i]=src[i];
}

int main()
{
    int sum1 = 0;
    int sum2 = 0;
    for(int i = 0; i < size; i++)
    {
        a[i] = rand();
        sum1 += a[i];
    }
    auto t = rdtsc();
    convert(a, b);
    t = rdtsc() - t;
    for(int i = 0; i < size; i++)
    {
        sum2 += b[i];
    }

    printf("%d = %d\n", sum1, sum2);
    printf("t=%llu\n", t);
}
对于较大的大小,这将需要更多的时间,因为编译器不会内联到无限大小

gcc只对-O3进行矢量化,没有进一步的选项,但它会生成类似的代码


但是如果你使用
-ftree vectorize
,gcc也会在-O2中生成SSE指令。

我做了一些测量,在我的(相当嘈杂的)桌面上,它有一个3.1Ghz的AMD CPU。我对AMD的缓存策略不太熟悉,但对于这一点来说应该没什么大不了的

代码如下: 我用-O2和GCC4.92编译了它

结果是:

original: 0.0905usec
aligned64: 0.1191usec
unrolled_8s: 0.0625usec
unrolled_64s: 0.0497usec
  • 原始-您的原始代码
  • aligned64-我认为对齐可能是一个问题,所以我强制它使用*64位对齐。这不是问题所在
  • 展开\u 8s-将128个循环展开为八个一组
  • 展开\u 64s-将128个循环展开为64个组
我的CPU运行在3.1Ghz的CPU上,所以我们假设它大约是每秒30亿个周期,也就是每纳秒3个周期

  • 原件:90毫微秒~270个周期。因此(270/128)=每个副本2.11个周期
  • 对齐64:119 nsec~357个周期。因此(357/128)=每个拷贝2.79个周期
  • 展开的8s:62个nsec~186个周期。因此(186/128)=1.45个周期/份
  • 展开的_64s:50 nsec~150个周期。因此(267/128)=1.17个周期/份
请不要盲目地认为展开循环会更好!我在这里滥用了两件事,严重作弊:

  • 所有数据都保留在缓存中
  • 所有指令(代码)都保留在缓存中

如果您的所有数据都从CPU的缓存中失效,那么从主内存中一路重新获取数据可能要付出巨大的代价。在最坏的情况下,执行复制的线程可能会被从CPU中抛出(“上下文切换”)在每个副本之间。最重要的是,数据可能会从缓存中失效。这意味着每次上下文切换要花费数百微秒,每次内存访问要花费数百个周期。

你有没有研究过循环向量化?这几乎是一个教科书上的例子。@MichaelBers不是真的。编译器不应该吗自动orize?也许,也许不是。GCC将在-O3,但否则你必须告诉它。“从8位整数到16位整数的转换是瓶颈”:真的吗???如果您正在对图像进行任何非平凡的处理,我会非常惊讶,这将是一个瓶颈,即使转换效率非常低。您是否有分析数据支持这一点?您确定要使用int8\u t而不是uint8\u t吗?
original: 0.0905usec
aligned64: 0.1191usec
unrolled_8s: 0.0625usec
unrolled_64s: 0.0497usec