c/c++ 代码中使用sse指令集加速
使用SSE指令,首先要了解这一类用于进行初始化加载数据以及将暂存器的数据保存到内存相关的指令,
我们知道,大多数SSE指令是使用的xmm0到xmm8的暂存器,那么使用之前,就需要将数据从内存加载到这些暂存器。
1. load系列,用于加载数据,从内存到暂存器
- __m128 _mm_load_ss (float *p)
- __m128 _mm_load_ps (float *p)
- __m128 _mm_load1_ps (float *p)
- __m128 _mm_loadh_pi (__m128 a, __m64 *p)
- __m128 _mm_loadl_pi (__m128 a, __m64 *p)
- __m128 _mm_loadr_ps (float *p)
- __m128 _mm_loadu_ps (float *p)
上面是从手册查询到的load系列的函数。其中,
_mm_load_ss用于scalar的加载,所以,加载一个单精度浮点数到暂存器的低字节,其它三个字节清0,(r0 := *p, r1 := r2 := r3 := 0.0)。
_mm_load_ps用于packed的加载(下面的都是用于packed的),要求p的地址是16字节对齐,否则读取的结果会出错,(r0 := p[0], r1 := p[1], r2 := p[2], r3 := p[3])。
_mm_load1_ps表示将p地址的值,加载到暂存器的四个字节,需要多条指令完成,所以,从性能考虑,在内层循环不要使用这类指令。(r0 := r1 := r2 := r3 := *p)。
_mm_loadh_pi和_mm_loadl_pi分别用于从两个参数高底字节等组合加载。具体参考手册。
_mm_loadr_ps表示以_mm_load_ps反向的顺序加载,需要多条指令完成,当然,也要求地址是16字节对齐。(r0 := p[3], r1 := p[2], r2 := p[1], r3 := p[0])。
_mm_loadu_ps和_mm_load_ps一样的加载,但是不要求地址是16字节对齐,对应指令为movups。
2. set系列,用于加载数据,大部分需要多条指令完成,但是可能不需要16字节对齐。
- __m128 _mm_set_ss (float w)
- __m128 _mm_set_ps (float z, float y, float x, float w)
- __m128 _mm_set1_ps (float w)
- __m128 _mm_setr_ps (float z, float y, float x, float w)
- __m128 _mm_setzero_ps ()
这一系列函数主要是类似于load的操作,但是可能会调用多条指令去完成,方便的是可能不需要考虑对齐的问题。
_mm_set_ss对应于_mm_load_ss的功能,不需要字节对齐,需要多条指令。(r0 = w, r1 = r2 = r3 = 0.0)
_mm_set_ps对应于_mm_load_ps的功能,参数是四个单独的单精度浮点数,所以也不需要字节对齐,需要多条指令。(r0=w, r1 = x, r2 = y, r3 = z,注意顺序)
_mm_set1_ps对应于_mm_load1_ps的功能,不需要字节对齐,需要多条指令。(r0 = r1 = r2 = r3 = w)
_mm_setzero_ps是清0操作,只需要一条指令。(r0 = r1 = r2 = r3 = 0.0)
3. store系列,用于将计算结果等SSE暂存器的数据保存到内存中。
- void _mm_store_ss (float *p, __m128 a)
- void _mm_store_ps (float *p, __m128 a)
- void _mm_store1_ps (float *p, __m128 a)
- void _mm_storeh_pi (__m64 *p, __m128 a)
- void _mm_storel_pi (__m64 *p, __m128 a)
- void _mm_storer_ps (float *p, __m128 a)
- void _mm_storeu_ps (float *p, __m128 a)
- void _mm_stream_ps (float *p, __m128 a)
这一系列函数和load系列函数的功能对应,基本上都是一个反向的过程。
_mm_store_ss:一条指令,*p = a0
_mm_store_ps:一条指令,p[i] = a[i]。
_mm_store1_ps:多条指令,p[i] = a0。
_mm_storeh_pi,_mm_storel_pi:值保存其高位或低位。
_mm_storer_ps:反向,多条指令。
_mm_storeu_ps:一条指令,p[i] = a[i],不要求16字节对齐。
_mm_stream_ps:直接写入内存,不改变cache的数据。
(2)算术指令
SSE提供了大量的浮点运算指令,包括加法、减法、乘法、除法、开方、最大值、最小值、近似求倒数、求开方的倒数等等,可见SSE指令的强大之处。那么在了解了上面的数据加载和数据保存的指令之后,使用这些算术指令就很容易了,下面以加法为例。
SSE中浮点加法的指令有:
- __m128 _mm_add_ss (__m128 a, __m128 b)
- __m128 _mm_add_ps (__m128 a, __m128 b)
其中,_mm_add_ss表示scalar执行模式,_mm_add_ps表示packed执行模式。
一般而言,使用SSE指令写代码,步骤为:使用load/set函数将数据从内存加载到SSE暂存器;使用相关SSE指令完成计算等;使用store系列函数将结果从暂存器保存到内存,供后面使用。
下面是一个完成加法的例子:
- #include <intrin.h>
- int main(int argc, char* argv[])
- {
- float op1[] = {1.0, 2.0, 3.0, 4.0};
- float op2[] = {1.0, 2.0, 3.0, 4.0};
- float result[];
- __m128 a;
- __m128 b;
- __m128 c;
- // Load
- a = _mm_loadu_ps(op1);
- b = _mm_loadu_ps(op2);
- // Calculate
- c = _mm_add_ps(a, b); // c = a + b
- // Store
- _mm_storeu_ps(result, c);
- /* // Using the __m128 union to get the result.
- printf("0: %lf\n", c.m128_f32[0]);
- printf("1: %lf\n", c.m128_f32[1]);
- printf("2: %lf\n", c.m128_f32[2]);
- printf("3: %lf\n", c.m128_f32[3]);
- */
- printf("0: %lf\n", result[]);
- printf("1: %lf\n", result[]);
- printf("2: %lf\n", result[]);
- printf("3: %lf\n", result[]);
- return ;
- }
这个例子,前面已经写过类似的加法例子,注释中的printf部分是利用__m128这个数据类型来获取相关的值,
这个类型是一个union类型,具体定义可以参考相关头文件,但是,对于实际使用,有时候这个值是一个中间值,需要后面计算使用,就得使用store了,效率更高。
上面使用的是_mm_loadu_ps和_mm_storeu_ps,不要求字节对齐,如果使用_mm_load_ps和_mm_store_ps,会发现程序会崩溃或得不到正确结果。下面是指定字节对齐后的一种实现方法:
- #include <intrin.h>
- int main(int argc, char* argv[])
- {
- __declspec(align()) float op1[] = {1.0, 2.0, 3.0, 4.0};
- __declspec(align()) float op2[] = {1.0, 2.0, 3.0, 4.0};
- _MM_ALIGN16 float result[]; // A macro, same as "__declspec(align(16))"
- __m128 a;
- __m128 b;
- __m128 c;
- // Load
- a = _mm_load_ps(op1);
- b = _mm_load_ps(op2);
- // Calculate
- c = _mm_add_ps(a, b); // c = a + b
- // Store
- _mm_store_ps(result, c);
- /* // Using the __m128 union to get the result.
- printf("0: %lf\n", c.m128_f32[0]);
- printf("1: %lf\n", c.m128_f32[1]);
- printf("2: %lf\n", c.m128_f32[2]);
- printf("3: %lf\n", c.m128_f32[3]);
- */
- printf("0: %lf\n", result[]);
- printf("1: %lf\n", result[]);
- printf("2: %lf\n", result[]);
- printf("3: %lf\n", result[]);
- return ;
- }
原文地址:http://blog.csdn.net/gengshenghong/article/details/7011373
原本看sse就是为给骨骼动画加速的,但是用起来感觉不快,
下面是矩阵乘以向量的代码,以及sse版
- vec3 operator*(const vec3 &v) const {
- vec3 ret;
- ret[] = mat[] * v[] + mat[] * v[] + mat[] * v[] + mat[];
- ret[] = mat[] * v[] + mat[] * v[] + mat[] * v[] + mat[];
- ret[] = mat[] * v[] + mat[] * v[] + mat[] * v[] + mat[];
- return ret;
- }
- vec3 MultVec3SSE(const vec3& v) const
- {
- vec3 ret;
- __m128 res1;
- __m128 res2;
- __m128 res3;
- __m128 addres;
- /*
- _MM_ALIGN16 float addresf[4] = {mat[12],mat[13],mat[14],1};
- _MM_ALIGN16 float multf1[4] = {mat[0],mat[1],mat[2],1};
- _MM_ALIGN16 float multf2[4] = {mat[4],mat[5],mat[6],1};
- _MM_ALIGN16 float multf3[4] = {mat[8],mat[9],mat[10],1};
- _MM_ALIGN16 float mvecf1[4] = {v[0],v[0],v[0],1};
- _MM_ALIGN16 float mvecf2[4] = {v[1],v[1],v[1],1};
- _MM_ALIGN16 float mvecf3[4] = {v[2],v[2],v[2],1};
- addres = _mm_load_ps(addresf);
- */
- addres = _mm_set_ps(,mat[],mat[],mat[]);
- /*
- res1 = _mm_mul_ps(_mm_load_ps(multf1),_mm_load_ps(mvecf1));
- res2 = _mm_mul_ps(_mm_load_ps(multf2),_mm_load_ps(mvecf2));
- res3 = _mm_mul_ps(_mm_load_ps(multf3),_mm_load_ps(mvecf3)); //_mm_set_ps
- */
- res1 = _mm_mul_ps(_mm_set_ps(,mat[],mat[],mat[]),_mm_set_ps(,v[],v[],v[]));
- res2 = _mm_mul_ps(_mm_set_ps(,mat[],mat[],mat[]),_mm_set_ps(,v[],v[],v[]));
- res3 = _mm_mul_ps(_mm_set_ps(,mat[],mat[],mat[]),_mm_set_ps(,v[],v[],v[]));
- res1 = _mm_add_ps(res1,res2);
- res3 = _mm_add_ps(res1,res3);
- res3 = _mm_add_ps(res3,addres);
- float dest[];
- _mm_storeu_ps(dest,res3);
- ret.x = dest[] ;
- ret.y = dest[] ;
- ret.z = dest[] ;
- return ret;
- }
debug模式下fps只提高了个位数,release下 更是跑不过 非sse的。。。。。情何以堪
c/c++ 代码中使用sse指令集加速的更多相关文章
- SSE指令集加速之 I420转BGR24
void yuv420_to_rgb24_sse3(uint8_t *yp, uint8_t *up, uint8_t *vp, int sy, int suv, int width, int hei ...
- 【转帖】超能课堂(186) CPU中的那些指令集都有什么用?
超能课堂(186)CPU中的那些指令集都有什么用? https://www.expreview.com/68615.html 不明觉厉 开始的地方 第一大类:基础运算类x86.x86-64及EM64T ...
- AVX图像算法优化系列二: 使用AVX2指令集加速查表算法。
查表算法,无疑也是一种非常常用.有效而且快捷的算法,我们在很多算法的加速过程中都能看到他的影子,在图像处理中,尤其常用,比如我们常见的各种基于直方图的增强,可以说,在photoshop中的调整菜单里8 ...
- SSE指令集学习:Compiler Intrinsic
大多数的函数是在库中,Intrinsic Function却内嵌在编译器中(built in to the compiler). 1. Intrinsic Function Intrinsic Fun ...
- SSE指令集优化学习:双线性插值
对SSE的学习总算迈出了第一步,用2天时间对双线性插值的代码进行了优化,现将实现的过程梳理以下,算是对这段学习的一个总结. 1. 什么是SSE 说到SSE,首先要弄清楚的一个概念是SIMD(单指令多数 ...
- 【转】【SSE】基于SSE指令集的程序设计简介
基于SSE指令集的程序设计简介 作者:Alex Farber 出处:http://www.codeproject.com/cpp/sseintro.asp SSE技术简介 Intel公司的单指令多数据 ...
- 【转】【SEE】基于SSE指令集的程序设计简介
SSE技术简介 Intel公司的单指令多数据流式扩展(SSE,Streaming SIMD Extensions)技术能够有效增强CPU浮点运算的能力.Visual Studio .NET 2003提 ...
- 在VS代码中编辑Python
在VS代码中编辑Python Python扩展提供了许多用于在Visual Studio代码中编辑Python源代码的功能: 自动完成和智能感知 在终端中运行选择/行(REPL) 格式化 重构 也见L ...
- DSO 优化代码中的 Schur Complement
接上一篇博客<直接法光度误差导数推导>,DSO 代码中 CoarseInitializer::trackFrame 目的是优化两帧(ref frame 和 new frame)之间的相对状 ...
随机推荐
- debian下创建新用户useradd
1.使用sudo: sudo useradd -m abc -g sudo -s /bin/bash -d /home/abc sudo passwd abc 2.直接在root用户下: groupa ...
- Redis 相关操作
1.安装 下载地址:http://www.redis.cn/ 在使用Redis时,开始就遇到了问题,客户端打不开,原因是需要先开启服务端,这需要先配置—— 1.下载好redis安装包,解压安装之后,复 ...
- RTX——第19章 SVC 中断方式调用用户函数(后期补历程)
以下内容转载自安富莱电子: http://forum.armfly.com/forum.php 本章节为大家讲解如何采用 SVC 中断方式调用用户函数. 当用户将 RTX 任务设置为工作在非特权级模式 ...
- 为python 添加新功能-dump
一直觉得thinkphp提供的dump函数挺好用的,但是python里面没有,就一直想着写个简单的. dir是我比较常用的一个内置函数了,但是显示效果实在有点受不了,每次我都要从大量的字符串里找到我需 ...
- numpy 学习总结
numpy 学习总结 作者:csj更新时间:01.09 email:59888745@qq.com 说明:因内容较多,会不断更新 xxx学习总结: 回主目录:2017 年学习记录和总结 #生成数组/使 ...
- MAC OSX 10.10 下启用自带的Apache的rewrite模块
1.修改Apache配置文件 sudo vim /etc/apache2/httpd.conf LoadModule rewrite_module libexec/apache2/mod_rewrit ...
- 【Android】Android6.0读取通话记录
需求:读取通话记录,然后列表显示,每条记录的数据包括姓名.号码.类型(来电.去电.未接,字体颜色分别为绿.蓝.红),然后长按条目弹出一个列表弹窗,显示[复制号码到拨号盘].[发短信].[打电话]. 先 ...
- Win10下打开chm文档提示无法显示该页的解决方法
一是检查chm文件属性里最下面是否有个“解除锁定”,如有,点击“解除锁定”按钮就可以了. 如果没有上面提到的“解除锁定”,检查chm文件存放的路径.本例中,由于chm文件的存放路径中不能带有特殊字符“ ...
- require.js+knockout.js+.underscore模板引擎的使用
第一种使用方式: HTML: <ul data-bind="template: { name: 'peopleList' }"></ul> <scri ...
- zookeeper快照清理
从3.4版本以后,在配置文件中有2个参数分别设置快照的清理.默认没有打开. autopurge.purgeInterval=1 这个参数指定了清理频率,单位是小时,需要填写一个1或更大的整数,默认是0 ...