#include "counter.h"
#include <intrin.h>
#include <stdlib.h>
#include <math.h>
/*
标题:我的第一个调用Intrinsics函数的程序
所属项目名称:TestSSE
项目类型:Win32控制台项目
依赖:counter.h文件//提供计时功能
描述:以前需要使用汇编对CPU的指令集进行优化,现在可以直接使用Intrinsic函数达到类似效果,
可读性和移植性相对于汇编语言有长足进步。现在极少有x86架构cpu不支持SSE/SSE3指令集
所以是时候了解Intrinsic函数(SSE、SSE2)的使用。
现在让我们通过简单的例子,来了解Intrinsic函数(SSE/SSE2指令)如何使用!
依赖:counter.h
最后更新:kagula 2014-04-22
测试环境:Windows 8.1 64bit、Visual Studio 2013 Update1
测试结果:在Core i5-2500k上,addWithSSE函数相对于add函数大概缩短了一半的运算时间。
如何调用Intrinsics函数还可参考下面资料:
[1]《跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac) 》
http://www.cnblogs.com/zyl910/archive/2012/10/22/simdsumfloat.html
[2]《在C/C++代码中使用SSE等指令集的指令(5)SSE进行加法运算简单的性能测试》
http://blog.csdn.net/gengshenghong/article/details/7011373
[4]《Introduction to SSE Programming》
http://www.codeproject.com/Articles/4522/Introduction-to-SSE-Programming
*/
struct MyData {
_MM_ALIGN16 float *op1;//后面使用到的Intrinsics函数需要128位对齐
_MM_ALIGN16 float *op2;
_MM_ALIGN16 float *result1;
_MM_ALIGN16 float *result2;
MyData(const unsigned int count)
{
op1 = new float[count];
op2 = new float[count];
result1 = new float[count];;
result2 = new float[count];;
}
~MyData()
{
delete op1;
delete op2;
delete result1;
delete result2;
}
};
//初始化样本
void initSample(const unsigned int count, MyData &md)
{
for (unsigned int i = 0; i < count; i++)
{
md.op1[i] = (float)rand() / (float)RAND_MAX;
md.op2[i] = (float)rand() / (float)RAND_MAX;
}
}
void add(const unsigned int count, MyData &md)
{
for (unsigned int i = 0; i < count; i++)
md.result1[i] = md.op1[i] + md.op2[i];
}
void addWithSSE(const unsigned int count, MyData &md)
{
__m128 a;
__m128 b;
__m128 c;
for (unsigned int i = 0; i < count; i = i + 4)
{
//Intrinsic函数的调用分三个步骤,具体如下
//[S1/3]装载数据
a = _mm_load_ps(md.op1 + i);
b = _mm_load_ps(md.op2 + i);
//[S2/3]四元组运算. 要求CPU支持SSE指令集
c = _mm_add_ps(a, b); // c = a + b
//[S3/3]保存数据
_mm_store_ps(md.result2 + i, c);
}
}
/*
Intrinsic函数名的形式为_mm_<intrin_op>_<suffix>
有三部分组成,其中后缀(suffix)又分为两部分.
函数名中的mm,代表操作数是128位,把mm替换为mm256就是256位,需CPU支持AVX。
函数名中的add可替换为sub、mul、div、sqrt、rcp、rsqrt、max、min分别
代表了减法、乘法、除法、平方根、倒数、平方根的倒数、返回较大值、较小值等。
函数名中的后缀有两部分组成
第一部分,有三种,决定运算范围
s 标量(scalar) 只操作元组的第一个因子
p 包(pack) 操作元组中的全部因子
ep 扩展包(extend pack) 相对于p,因子的位宽增加一倍。
比如原来128位容纳4个因子,现在只能2个因子,低位向高位做了符号扩展
第二部分,有11种,决定元组中因子的数据类型(部分要求SSE2支持,比如双精)
s 单精(single-precision floating point)
d 双精(double-precision floating point)
i128 128位整数(signed 128-bit integer)
i64 64位整数(signed 64-bit integer
u64 无符号64位整数(unsigned 64-bit integer)
i32 32位整数(signed 32-bit integer)
u32 无符号32位整数(unsigned 32-bit integer)
i16 16位整数(signed 16-bit integer
u16 无符号16位整数(unsigned 16-bit integer)
i8 8位整数(signed 8-bit integer)
u8 无符号8位整数(unsigned 8-bit integer
所以_mm_add_ps函数名,表示128位,加法,元组,单精度函数
因为一个单精度占32位,操作数为128位,128/32=4,所以元组含四个因子。
例如:可以把_mm_add_ps中的ps替换为ss,就只对四元组中的第一个元素操作了。
有哪些Intrinsic函数可供我们调用,参考下面资料
[1]《VC++ 浮点数学SSE Intrinsics》
http://blog.163.com/chenqneu@126/blog/static/457384842007814114410576/
[2]《Intel Intrinsics Guide》
https://software.intel.com/sites/landingpage/IntrinsicsGuide/
通过Intrinsic形式要调用CPU扩展指令集SSE、SSE2,得考虑到还有少部分老机器不支持,
可使用CPUID指令(在Visual Studio 2010以上版本中有对应的Intrinsic函数)判断。
*/
int main(int argc, wchar_t* argv[])
{
const unsigned int count = 400 * 100000; // 4*32bit=128bit对齐
MyData md(count);
initSample(count, md);
srand((unsigned int)time(NULL));
wprintf(L"Add a vector array:\n");
startTiming();
add(count, md);
stopWithPrintTiming();
printf("\n");
wprintf(L"Add a vector array with SSE instructions:\n");
startTiming();
addWithSSE(count, md);
stopWithPrintTiming();
//system("pause");
return 0;
}
#include <intrin.h>
#include <stdlib.h>
#include <math.h>
/*
标题:我的第一个调用Intrinsics函数的程序
所属项目名称:TestSSE
项目类型:Win32控制台项目
依赖:counter.h文件//提供计时功能
描述:以前需要使用汇编对CPU的指令集进行优化,现在可以直接使用Intrinsic函数达到类似效果,
可读性和移植性相对于汇编语言有长足进步。现在极少有x86架构cpu不支持SSE/SSE3指令集
所以是时候了解Intrinsic函数(SSE、SSE2)的使用。
现在让我们通过简单的例子,来了解Intrinsic函数(SSE/SSE2指令)如何使用!
依赖:counter.h
最后更新:kagula 2014-04-22
测试环境:Windows 8.1 64bit、Visual Studio 2013 Update1
测试结果:在Core i5-2500k上,addWithSSE函数相对于add函数大概缩短了一半的运算时间。
如何调用Intrinsics函数还可参考下面资料:
[1]《跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac) 》
http://www.cnblogs.com/zyl910/archive/2012/10/22/simdsumfloat.html
[2]《在C/C++代码中使用SSE等指令集的指令(5)SSE进行加法运算简单的性能测试》
http://blog.csdn.net/gengshenghong/article/details/7011373
[4]《Introduction to SSE Programming》
http://www.codeproject.com/Articles/4522/Introduction-to-SSE-Programming
*/
struct MyData {
_MM_ALIGN16 float *op1;//后面使用到的Intrinsics函数需要128位对齐
_MM_ALIGN16 float *op2;
_MM_ALIGN16 float *result1;
_MM_ALIGN16 float *result2;
MyData(const unsigned int count)
{
op1 = new float[count];
op2 = new float[count];
result1 = new float[count];;
result2 = new float[count];;
}
~MyData()
{
delete op1;
delete op2;
delete result1;
delete result2;
}
};
//初始化样本
void initSample(const unsigned int count, MyData &md)
{
for (unsigned int i = 0; i < count; i++)
{
md.op1[i] = (float)rand() / (float)RAND_MAX;
md.op2[i] = (float)rand() / (float)RAND_MAX;
}
}
void add(const unsigned int count, MyData &md)
{
for (unsigned int i = 0; i < count; i++)
md.result1[i] = md.op1[i] + md.op2[i];
}
void addWithSSE(const unsigned int count, MyData &md)
{
__m128 a;
__m128 b;
__m128 c;
for (unsigned int i = 0; i < count; i = i + 4)
{
//Intrinsic函数的调用分三个步骤,具体如下
//[S1/3]装载数据
a = _mm_load_ps(md.op1 + i);
b = _mm_load_ps(md.op2 + i);
//[S2/3]四元组运算. 要求CPU支持SSE指令集
c = _mm_add_ps(a, b); // c = a + b
//[S3/3]保存数据
_mm_store_ps(md.result2 + i, c);
}
}
/*
Intrinsic函数名的形式为_mm_<intrin_op>_<suffix>
有三部分组成,其中后缀(suffix)又分为两部分.
函数名中的mm,代表操作数是128位,把mm替换为mm256就是256位,需CPU支持AVX。
函数名中的add可替换为sub、mul、div、sqrt、rcp、rsqrt、max、min分别
代表了减法、乘法、除法、平方根、倒数、平方根的倒数、返回较大值、较小值等。
函数名中的后缀有两部分组成
第一部分,有三种,决定运算范围
s 标量(scalar) 只操作元组的第一个因子
p 包(pack) 操作元组中的全部因子
ep 扩展包(extend pack) 相对于p,因子的位宽增加一倍。
比如原来128位容纳4个因子,现在只能2个因子,低位向高位做了符号扩展
第二部分,有11种,决定元组中因子的数据类型(部分要求SSE2支持,比如双精)
s 单精(single-precision floating point)
d 双精(double-precision floating point)
i128 128位整数(signed 128-bit integer)
i64 64位整数(signed 64-bit integer
u64 无符号64位整数(unsigned 64-bit integer)
i32 32位整数(signed 32-bit integer)
u32 无符号32位整数(unsigned 32-bit integer)
i16 16位整数(signed 16-bit integer
u16 无符号16位整数(unsigned 16-bit integer)
i8 8位整数(signed 8-bit integer)
u8 无符号8位整数(unsigned 8-bit integer
所以_mm_add_ps函数名,表示128位,加法,元组,单精度函数
因为一个单精度占32位,操作数为128位,128/32=4,所以元组含四个因子。
例如:可以把_mm_add_ps中的ps替换为ss,就只对四元组中的第一个元素操作了。
有哪些Intrinsic函数可供我们调用,参考下面资料
[1]《VC++ 浮点数学SSE Intrinsics》
http://blog.163.com/chenqneu@126/blog/static/457384842007814114410576/
[2]《Intel Intrinsics Guide》
https://software.intel.com/sites/landingpage/IntrinsicsGuide/
通过Intrinsic形式要调用CPU扩展指令集SSE、SSE2,得考虑到还有少部分老机器不支持,
可使用CPUID指令(在Visual Studio 2010以上版本中有对应的Intrinsic函数)判断。
*/
int main(int argc, wchar_t* argv[])
{
const unsigned int count = 400 * 100000; // 4*32bit=128bit对齐
MyData md(count);
initSample(count, md);
srand((unsigned int)time(NULL));
wprintf(L"Add a vector array:\n");
startTiming();
add(count, md);
stopWithPrintTiming();
printf("\n");
wprintf(L"Add a vector array with SSE instructions:\n");
startTiming();
addWithSSE(count, md);
stopWithPrintTiming();
//system("pause");
return 0;
}