optimization
2016-06-13 18:18
239 查看
——在进行YUV_NV21相关的时候:
@编程习惯:
1. 因为一个简单的计算,要转换成一堆SIMD指令。所以在编写的时候,每一步分别加上注释,会使写的过程不那么一头雾水,容易凌乱;
2. 注意signed和unsigned,在NEON中会报错,但是SSE得自己留心;
3. NEON,找不到合适的指令的时候,可以参考下网页:http://write.blog.csdn.net/mdeditor#!postId=51659737。比如,zip操作就是在这里看到的。毕竟中文内容,看着好找点儿。
4. 多花点时间在代码构思上,最好还要提前自己打一下草稿,确定一下每一步的具体指令,编写代码的时候再详细写,并确认指令是否正确。这样能写出比较高质量的代码,即bug少的代码。
5. 某个细节地方如果觉得不对,在写代码的过程就直接对此加以测试,也确认其正确性,避免最后攒了一堆问题,然后必须全局一点儿一点儿调。
6.
@优化point:
1. _mm_lddqu_si128性能会比_mm_load好些貌似;
2. 可以多用shuffle,shuffle挺快的;
3. _mm_packus_epi16 和 vqmovun_s16 自带saturate的功能;
4. 在本来计算量就少的cvtColor中应该避免调用自己写的函数,在那个代码里,就调用了个saturate_cast性能就能慢一半
5.下面两份代码,前者会远快于后者,不造为啥,但是下次遇到这样的代码,可以都试试。
6 . 最好不要按指针访问寄存器变量(操作可以通过shuffle和blend来实现),而且下列代码会报warning,而且结果也不对(但是如果用printf输出一下这部分访问,结果可能又正确了)。因为存在大端小端存储的问题。
7 . (RRR)(GGG)(BBB) -> (RGB)(RGB)(RGB)的过程,对于neon很好实现,但是在SSE中,可以采用shuffle和blend的操作来完成。
8 . 像cvtColor这样的操作,本身计算量就不多,所以要尽可能的压缩冗余计算,以提升性能。
@dug:
1. 遇到bug时,先多看看输出结果,错误数据以及位置,分析可能的错误的原因,便于准确定位。
2. 遇到bug,要多思考,去定位错误位置,而不是急于printf。
——在进行RGB2GBR的转换的时候:
在197(Intel E5-2670 v3 * 24)服务器上,对通过shfful和blend实现赋值(如下文所示),会远快于_mm128i的指针
在199(Intel E5-2660 v2 * 20)服务器上,二者差不多。
在197上,相同的函数相对199能获得约为40%的性能提升
——在进行RGBA2RGB的转换的时候:
需要做如下赋值:
![](https://img-blog.csdn.net/20160614145519282)
最开始采用直接强制指针转换,再赋值的方法。
即如下所示:
报warning(此处通过-Werror选项,将warning也设置为error):
如果不管这个错误仍旧让他执行,执行结果出错。
但是如果给这部分计算前后加上按指针访问的相关数据的属于,即:
程序就能正确执行,是不是比较奇葩……(为什么呢???)
如果直接按ucha指针来访问就能正确执行:
但是觉得这个性能不好,所以最后找到指令blend来实现这个功能:
性能会比用指针访问的效果好
——在进行RGB2GBR的转换的时候:
基本的实现(-O3)的代码反而比这样的代码好:
@编程习惯:
1. 因为一个简单的计算,要转换成一堆SIMD指令。所以在编写的时候,每一步分别加上注释,会使写的过程不那么一头雾水,容易凌乱;
2. 注意signed和unsigned,在NEON中会报错,但是SSE得自己留心;
3. NEON,找不到合适的指令的时候,可以参考下网页:http://write.blog.csdn.net/mdeditor#!postId=51659737。比如,zip操作就是在这里看到的。毕竟中文内容,看着好找点儿。
4. 多花点时间在代码构思上,最好还要提前自己打一下草稿,确定一下每一步的具体指令,编写代码的时候再详细写,并确认指令是否正确。这样能写出比较高质量的代码,即bug少的代码。
5. 某个细节地方如果觉得不对,在写代码的过程就直接对此加以测试,也确认其正确性,避免最后攒了一堆问题,然后必须全局一点儿一点儿调。
6.
@优化point:
1. _mm_lddqu_si128性能会比_mm_load好些貌似;
2. 可以多用shuffle,shuffle挺快的;
3. _mm_packus_epi16 和 vqmovun_s16 自带saturate的功能;
4. 在本来计算量就少的cvtColor中应该避免调用自己写的函数,在那个代码里,就调用了个saturate_cast性能就能慢一半
5.下面两份代码,前者会远快于后者,不造为啥,但是下次遇到这样的代码,可以都试试。
R = (R > 255) ? 255 : ((R < 0) ? 0 : R); G = (G > 255) ? 255 : ((G < 0) ? 0 : G); B = (B > 255) ? 255 : ((B < 0) ? 0 : B);
R = (R <= 255 && R >= 0) ? R : ((R < 0) ? 0 : 255); G = (G <= 255 && G >= 0) ? G : ((G < 0) ? 0 : 255); B = (B <= 255 && B >= 0) ? B : ((B < 0) ? 0 : 255);
6 . 最好不要按指针访问寄存器变量(操作可以通过shuffle和blend来实现),而且下列代码会报warning,而且结果也不对(但是如果用printf输出一下这部分访问,结果可能又正确了)。因为存在大端小端存储的问题。
__mm128i x,y; ... ((int *)(&x))[3] = ((int *)(&y))[2]; //可以按照uchar *来访问,但是int *就不对了。 ...
7 . (RRR)(GGG)(BBB) -> (RGB)(RGB)(RGB)的过程,对于neon很好实现,但是在SSE中,可以采用shuffle和blend的操作来完成。
__m128i _shuff_1 = _mm_set_epi8(10,0,9,8,0,7,6,0,5,4,0,3,2,0,1,0); __m128i _shuff_3 = _mm_set_epi8(0,0,0,0,0,0,0,0,0,15,14,0,13,12,0,11); __m128i _shuff_4 = _mm_set_epi8(5,4,0,3,2,0,1,0,0,0,0,0,0,0,0,0); __m128i _shuff_6 = _mm_set_epi8(0,15,14,0,13,12,0,11,10,0,9,8,0,7,6,0); __m128i _shuff_2 = _mm_set_epi8(0,4,0,0,3,0,0,2,0,0,1,0,0,0,0,0); __m128i _shuff_5 = _mm_set_epi8(0,0,9,0,0,8,0,0,7,0,0,6,0,0,5,0); __m128i _shuff_7 = _mm_set_epi8(15,0,0,14,0,0,13,0,0,12,0,0,11,0,0,10); __m128i _blend_12 = _mm_set_epi8(0,128,0,0,128,0,0,128,0,0,128,0,0,128,0,0); __m128i _blend_34 = _mm_set_epi8(128,128,128,128,128,128,128,128,0,0,0,0,0,0,0,0); __m128i _blend_345 = _mm_set_epi8(0,0,128,0,0,128,0,0,128,0,0,128,0,0,128,0); __m128i _blend_67 = _mm_set_epi8(128,0,0,128,0,0,128,0,0,128,0,0,128,0,0,128); __m128i R; __m128i R; __m128i R; __m128i R; __m128i RG0, RG1; __m128i RGB_temp0, RGB_temp1; __m128i RGB0, RGB1, RGB2; //get R,G,B in uchar datatype ... //blen R with G to RG RG0 = _mm_unpacklo_epi8(R, G); RG1 = _mm_unpackhi_epi8(R, G); // blend RG with B to RGB RGB_temp0 = _mm_shuffle_epi8(RG0, _shuff_1); RGB_temp1 = _mm_shuffle_epi8(B, _shuff_2); RGB0 = _mm_blendv_epi8(RGB_temp0, RGB_temp1, _blend_12); RGB_temp0 = _mm_shuffle_epi8(RG0, _shuff_3); RGB_temp1 = _mm_shuffle_epi8(RG1, _shuff_4); RGB_temp0 = _mm_blendv_epi8(RGB_temp0, RGB_temp1, _blend_34); RGB_temp1 = _mm_shuffle_epi8(B, _shuff_5); RGB1 = _mm_blendv_epi8(RGB_temp0, RGB_temp1, _blend_345); RGB_temp0 = _mm_shuffle_epi8(RG1, _shuff_6); RGB_temp1 = _mm_shuffle_epi8(B, _shuff_7); RGB2 = _mm_blendv_epi8(RGB_temp0, RGB_temp1, _blend_67);
8 . 像cvtColor这样的操作,本身计算量就不多,所以要尽可能的压缩冗余计算,以提升性能。
@dug:
1. 遇到bug时,先多看看输出结果,错误数据以及位置,分析可能的错误的原因,便于准确定位。
2. 遇到bug,要多思考,去定位错误位置,而不是急于printf。
——在进行RGB2GBR的转换的时候:
在197(Intel E5-2670 v3 * 24)服务器上,对通过shfful和blend实现赋值(如下文所示),会远快于_mm128i的指针
在199(Intel E5-2660 v2 * 20)服务器上,二者差不多。
在197上,相同的函数相对199能获得约为40%的性能提升
——在进行RGBA2RGB的转换的时候:
需要做如下赋值:
最开始采用直接强制指针转换,再赋值的方法。
即如下所示:
((unsigned int *)&x)[3] = ((unsigned int *)&y)[0];
报warning(此处通过-Werror选项,将warning也设置为error):
error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
如果不管这个错误仍旧让他执行,执行结果出错。
但是如果给这部分计算前后加上按指针访问的相关数据的属于,即:
for(int i=0; i<4; i++) printf("%x ", ((int *)&y)[i]); printf("\n"); ((unsigned int *)&x)[3] = ((unsigned int *)&y)[0];
程序就能正确执行,是不是比较奇葩……(为什么呢???)
如果直接按ucha指针来访问就能正确执行:
((uchar *)&x)[12] = ((uchar *)&y)[0]; ((uchar *)&x)[13] = ((uchar *)&y)[1]; ((uchar *)&x)[14] = ((uchar *)&y)[2]; ((uchar *)&x)[15] = ((uchar *)&y)[3];
但是觉得这个性能不好,所以最后找到指令blend来实现这个功能:
y = _mm_shuffle_epi32(y, 0); x = _mm_blend_epi16(x, y, 192);
性能会比用指针访问的效果好
——在进行RGB2GBR的转换的时候:
基本的实现(-O3)的代码反而比这样的代码好:
for (; psrc <= pend-48; psrc += 48) { dst_data = _mm_set_epi8( psrc[17], psrc[12], psrc[13], psrc[14], psrc[9], psrc[10], psrc[11], psrc[6], psrc[7], psrc[8], psrc[3], psrc[4], psrc[5], psrc[0], psrc[1], psrc[2]); _mm_storeu_si128((__m128i *)(pdst), dst_data); pdst += 16; dst_data = _mm_set_epi8( psrc[31], psrc[32], psrc[27], psrc[28], psrc[29], psrc[24], psrc[25], psrc[26], psrc[21], psrc[22], psrc[23], psrc[18], psrc[19], psrc[20], psrc[15], psrc[16]); _mm_storeu_si128((__m128i *)(pdst), dst_data); pdst += 16; dst_data = _mm_set_epi8( psrc[45], psrc[46], psrc[47], psrc[42], psrc[43], psrc[44], psrc[39], psrc[40], psrc[41], psrc[36], psrc[37], psrc[38], psrc[33], psrc[34], psrc[35], psrc[30]); _mm_storeu_si128((__m128i *)(pdst), dst_data); pdst += 16; }
相关文章推荐
- 架构设计:负载均衡层设计方案(2)——Nginx安装
- 架构设计:负载均衡层设计方案(1)——负载场景和解决方式
- Shell语法学习篇
- Apache配置反向代理、负载均衡和集群(mod_proxy方式) 经典
- 标准Web系统的架构分层
- 搭建nginx反向代理用做内网域名转发
- SSH Secure Shell Client中文乱码的解决方法
- Linux文件系统以及目录结构简介
- 在centos6.5上面安装pyqt4的运行环境
- TOMCAT-异常
- shell脚本知识
- opencv的Filestorage,待学习
- 基于java web开发的一个购物网站
- Docker 清理命令集锦
- docker 命令
- kubernets 架构设计 第1章 pod
- Linux文件管理之路
- #98 – How Attached Properties Work in WPF(附加属性在WPF中如何工作)
- linux打开navicat没反应
- Zookeeper3.4.6的安装