optimization

——在进行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;
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值