gpt4 book ai didi

c - GCC SSE代码优化

转载 作者:太空狗 更新时间:2023-10-29 16:35:49 26 4
gpt4 key购买 nike

这篇文章与我发布的另一篇文章密切相关 some days ago .这一次,我写了一个简单的代码,只添加一对元素数组,将结果乘以另一个数组中的值并将其存储在第四个数组中,所有变量都是浮点 double 类型。

我制作了该代码的两个版本:一个带有 SSE 指令,使用调用,另一个没有它们,然后我用 gcc 和 -O0 优化级别编译它们。我把它们写在下面:

// SSE VERSION

#define N 10000
#define NTIMES 100000
#include <time.h>
#include <stdio.h>
#include <xmmintrin.h>
#include <pmmintrin.h>

double a[N] __attribute__((aligned(16)));
double b[N] __attribute__((aligned(16)));
double c[N] __attribute__((aligned(16)));
double r[N] __attribute__((aligned(16)));

int main(void){
int i, times;
for( times = 0; times < NTIMES; times++ ){
for( i = 0; i <N; i+= 2){
__m128d mm_a = _mm_load_pd( &a[i] );
_mm_prefetch( &a[i+4], _MM_HINT_T0 );
__m128d mm_b = _mm_load_pd( &b[i] );
_mm_prefetch( &b[i+4] , _MM_HINT_T0 );
__m128d mm_c = _mm_load_pd( &c[i] );
_mm_prefetch( &c[i+4] , _MM_HINT_T0 );
__m128d mm_r;
mm_r = _mm_add_pd( mm_a, mm_b );
mm_a = _mm_mul_pd( mm_r , mm_c );
_mm_store_pd( &r[i], mm_a );
}
}
}

//NO SSE VERSION
//same definitions as before
int main(void){
int i, times;
for( times = 0; times < NTIMES; times++ ){
for( i = 0; i < N; i++ ){
r[i] = (a[i]+b[i])*c[i];
}
}
}

当使用 -O0 编译它们时,gcc 使用 XMM/MMX 寄存器和 SSE 指令,如果没有特别给出 -mno-sse(和其他)选项。我检查了为第二个代码生成的汇编代码,我注意到它使用了 movsdaddsdmulsd 指令。所以它使用了 SSE 指令,但只使用那些使用寄存器最低部分的指令,如果我没记错的话。正如预期的那样,为第一个 C 代码生成的汇编代码使用了 addpmulpd 指令,尽管生成了相当大的汇编代码。

无论如何,据我所知,第一个代码应该从 SIMD 范例中获得更好的 yield ,因为每次迭代都会计算两个结果值。尽管如此,第二个代码的执行速度比第一个代码快 25%。我还用单精度值进行了测试,得到了类似的结果。这是什么原因?

最佳答案

GCC 中的矢量化在 -O3 处启用。这就是为什么在 -O0 处,您只能看到普通的标量 SSE2 指令(movsdaddsd 等)。使用 GCC 4.6.1 和你的第二个例子:

#define N 10000
#define NTIMES 100000

double a[N] __attribute__ ((aligned (16)));
double b[N] __attribute__ ((aligned (16)));
double c[N] __attribute__ ((aligned (16)));
double r[N] __attribute__ ((aligned (16)));

int
main (void)
{
int i, times;
for (times = 0; times < NTIMES; times++)
{
for (i = 0; i < N; ++i)
r[i] = (a[i] + b[i]) * c[i];
}

return 0;
}

并使用 gcc -S -O3 -msse2 sse.c 编译为内部循环生成以下指令,这非常好:

.L3:
movapd a(%eax), %xmm0
addpd b(%eax), %xmm0
mulpd c(%eax), %xmm0
movapd %xmm0, r(%eax)
addl $16, %eax
cmpl $80000, %eax
jne .L3

如您所见,启用矢量化后,GCC 发出代码以并行执行两个 循环迭代。它可以改进,虽然 - 此代码使用 SSE 寄存器的低 128 位,但它可以使用完整的 256 位 YMM 寄存器,通过启用 SSE 指令的 AVX 编码(如果在机器上可用)。因此,使用 gcc -S -O3 -msse2 -mavx sse.c 编译相同的程序给出了内部循环:

.L3:
vmovapd a(%eax), %ymm0
vaddpd b(%eax), %ymm0, %ymm0
vmulpd c(%eax), %ymm0, %ymm0
vmovapd %ymm0, r(%eax)
addl $32, %eax
cmpl $80000, %eax
jne .L3

请注意,每条指令前面的 v 和指令使用 256 位 YMM 寄存器,原始循环的 4 迭代是并行执行的。

关于c - GCC SSE代码优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7919304/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com