——在进行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.下面两份代码,前者会远快于后者,不造为啥,但是下次遇到这样的代码,可以都试试。
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;
}