X86架构下SSE系列指令使用
sse指令集中的指令,一条指令可以实现多项数据运算,即SIMD-Single Instruction Multiple Data。
相关资料可参考:
http://blog.chinaunix.net/uid-20385936-id-3902720.html
http://blog.youkuaiyun.com/fengbingchun/article/details/18460199
下面的简短示例代码,函数f1使用sse实现了两个数组的累加:array_c = array_a + array_b;
编译运行效果:
# gcc test.c -msse2 -O2
# ./a.out
4 9 5 5
再看看函数f1的汇编代码,真是相当简单,3条指令就完成了数组的累加
若要使用SSE指令,需要在编译选项中增加-msse2(或-msse3, 或-msse4),当启用了高版本的的选项时,低版本的选项也就同时自动启用了。
当启用了-msse系列的选项时,编译器就可以编译使用了加速指令的嵌入式汇编代码,也可以针对代码中调用的sse系列的bultin函数(例如int __builtin_ia32_comisdgt (v2df, v2df))生成相应的sse指令代码。
如何让源代码同时适应启用与不启用-msse系列选项的情况呢?
当gcc编译命令中带了-msse系列选项时,会有相应的预定义的宏存在。
通过如下命令,就能知道,启用-msse4时,编译器会预定义哪些sse相关的宏,这就告诉了编码者,当前编译启动了何种sse。
从输出也能看出,启用高级别的sse时,低级别的sse也就自动打开了。
# gcc -dM -E -msse4 - <<<'' | grep SSE
#define __SSE4_1__ 1
#define __SSE4_2__ 1
#define __SSE2_MATH__ 1
#define __SSE_MATH__ 1
#define __SSE2__ 1
#define __SSSE3__ 1
#define __SSE__ 1
#define __SSE3__ 1
如何在代码中使用sse相关指令呢?
要么自己写嵌入式汇编代码,
要么调用相关的builtin函数(不同级别的sse有不同的builtin函数集,这些函数在编译时自动生成sse指令代码),
要么#include <emmintrin.h>并使用其中声明的接口。
通过查看/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/emmintrin.h的内容,可以发现,这个头文件,实际上是将sse2系列的相关builtin函数进行了包装。这样的话,如果直接使用emmintrin.h中的接口,相当于只能使用到sse2级别的加速指令。
sse指令集中的指令,一条指令可以实现多项数据运算,即SIMD-Single Instruction Multiple Data。
相关资料可参考:
http://blog.chinaunix.net/uid-20385936-id-3902720.html
http://blog.youkuaiyun.com/fengbingchun/article/details/18460199
下面的简短示例代码,函数f1使用sse实现了两个数组的累加:array_c = array_a + array_b;
#include <stdio.h>
#include <emmintrin.h>
int array_a[4] __attribute__ ((aligned (128))) = {1,2,3,4};
int array_b[4] __attribute__ ((aligned (128))) = {3,7,2,1};
int array_c[4] __attribute__ ((aligned (128)));
void f1(void *a, void *b, void *c)
{
__m128i big1 = _mm_load_si128((__m128i *)(a));
__m128i big2 = _mm_load_si128((__m128i *)(b));
__m128i big3 = _mm_add_epi16(big1,big2);
_mm_store_si128((__m128i *)c,big3);
}
int main()
{
int i;
f1(array_a, array_b, array_c);
for (i=0;i<4;i++)
{
printf("%d ", array_c[i]);
}
return 0;
}
编译运行效果:
# gcc test.c -msse2 -O2
# ./a.out
4 9 5 5
再看看函数f1的汇编代码,真是相当简单,3条指令就完成了数组的累加
0000000000400570 <f1>:
<span style="color:#ff0000;"> 400570: 66 0f 6f 07 movdqa (%rdi),%xmm0
400574: 66 0f fd 06 paddw (%rsi),%xmm0
400578: 66 0f 7f 02 movdqa %xmm0,(%rdx)</span>
40057c: c3 retq
40057d: 0f 1f 00 nopl (%rax)
若要使用SSE指令,需要在编译选项中增加-msse2(或-msse3, 或-msse4),当启用了高版本的的选项时,低版本的选项也就同时自动启用了。
当启用了-msse系列的选项时,编译器就可以编译使用了加速指令的嵌入式汇编代码,也可以针对代码中调用的sse系列的bultin函数(例如int __builtin_ia32_comisdgt (v2df, v2df))生成相应的sse指令代码。
如何让源代码同时适应启用与不启用-msse系列选项的情况呢?
当gcc编译命令中带了-msse系列选项时,会有相应的预定义的宏存在。
通过如下命令,就能知道,启用-msse4时,编译器会预定义哪些sse相关的宏,这就告诉了编码者,当前编译启动了何种sse。
从输出也能看出,启用高级别的sse时,低级别的sse也就自动打开了。
# gcc -dM -E -msse4 - <<<'' | grep SSE
#define __SSE4_1__ 1
#define __SSE4_2__ 1
#define __SSE2_MATH__ 1
#define __SSE_MATH__ 1
#define __SSE2__ 1
#define __SSSE3__ 1
#define __SSE__ 1
#define __SSE3__ 1
如何在代码中使用sse相关指令呢?
要么自己写嵌入式汇编代码,
要么调用相关的builtin函数(不同级别的sse有不同的builtin函数集,这些函数在编译时自动生成sse指令代码),
要么#include <emmintrin.h>并使用其中声明的接口。
通过查看/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/emmintrin.h的内容,可以发现,这个头文件,实际上是将sse2系列的相关builtin函数进行了包装。这样的话,如果直接使用emmintrin.h中的接口,相当于只能使用到sse2级别的加速指令。