我正在开发一个处理图像的程序。如果我可以将RGBA值存储在16位整数中,我可以通过使用SSE来提高性能(没有溢出的风险)。然而,从8位整数到16位整数的转换是瓶颈。将带符号的8位整数放入16位整数数组的最快方法是什么,效率相当于
int8_t a[128];
int16_t b[128];
for (int i=0;i<128;i++)
b[i]=a[i];
我正在使用openmp和指针。
答案 0 :(得分:2)
Clang将使用-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);
}
这是clang ++生成的代码。
; The loop inlined from `convert` as a single pass.
#APP
rdtsc
#NO_APP
movl %eax, %esi
movl %edx, %ecx
movq a(%rip), %xmm1
movq a+8(%rip), %xmm2
pxor %xmm0, %xmm0
punpcklbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqa %xmm1, b(%rip)
movdqa %xmm2, b+16(%rip)
movq a+16(%rip), %xmm1
movq a+24(%rip), %xmm2
punpcklbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqa %xmm1, b+32(%rip)
movdqa %xmm2, b+48(%rip)
movq a+32(%rip), %xmm1
movq a+40(%rip), %xmm2
punpcklbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqa %xmm1, b+64(%rip)
movdqa %xmm2, b+80(%rip)
movq a+48(%rip), %xmm1
movq a+56(%rip), %xmm2
punpcklbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqa %xmm1, b+96(%rip)
movdqa %xmm2, b+112(%rip)
movq a+64(%rip), %xmm1
movq a+72(%rip), %xmm2
punpcklbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqa %xmm1, b+128(%rip)
movdqa %xmm2, b+144(%rip)
movq a+80(%rip), %xmm1
movq a+88(%rip), %xmm2
punpcklbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqa %xmm1, b+160(%rip)
movdqa %xmm2, b+176(%rip)
movq a+96(%rip), %xmm1
movq a+104(%rip), %xmm2
punpcklbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqa %xmm1, b+192(%rip)
movdqa %xmm2, b+208(%rip)
movq a+112(%rip), %xmm1
movq a+120(%rip), %xmm2
punpcklbw %xmm0, %xmm1
punpcklbw %xmm0, %xmm2
movdqa %xmm1, b+224(%rip)
movdqa %xmm2, b+240(%rip)
#APP
rdtsc
#NO_APP
对于较大的尺寸,它需要更多,因为编译器不会内联到无限大小。
gcc只能在没有-O3的其他选项的情况下进行矢量化,但它会生成类似的代码。
但是如果使用-ftree-vectorize
,gcc也会在-O2中生成SSE指令。
答案 1 :(得分:1)
我做了一些测量,并在我的(相当嘈杂的)桌面上,它运行3.1Ghz AMD CPU。我对AMD的缓存策略并不太熟悉,但对此而言,这并不重要。
以下是代码:gist of test.cpp 我用-O2用GCC 4.92
编译它结果:
original: 0.0905usec
aligned64: 0.1191usec
unrolled_8s: 0.0625usec
unrolled_64s: 0.0497usec
我的CPU运行在3.1Ghz CPU,所以我们假设它每秒大约有30亿个周期,所以每纳秒大约需要3个周期。
请不要盲目地假设展开你的循环会更好!我滥用了两件事,在这里作弊很多:
如果所有数据都从CPU的缓存中失效,那么从主内存中重新获取数据可能会付出可怕的代价。在最坏的情况下,执行复制的线程可能会在每个副本之间被CPU(“上下文切换”)抛出。最重要的是,数据可能会从缓存中失效。这意味着每个上下文切换需要支付数百微秒,每个内存访问需要数百个周期。