Intrinsics中Neon的校验和代码实现

时间:2012-08-22 05:46:20

标签: embedded arm simd neon cortex-a8

我正在尝试使用内在函数为NEON实现校验和计算代码(2的补码加法)。当前的校验和计算正在ARM上进行。

我的实现从存储器一次取128位到NEON寄存器并进行SIMD(加法),结果从128位数字折叠成16位数。

一切看起来都很好,但是我的NEON实现消耗的时间比ARM版本多。

ARM版本需要: 0.860000 s NEON版本需要: 1.260000 s

注意:

  1. 使用“time.h”中的实用程序进行概述
  2. 校验和函数从示例应用程序调用10,000次,并在完成所有函数运行后计算时间
  3. 其他详情:

    1. 使用GNU工具链(arm-none-linux-gnueabi-gcc)编译内在代码而不是arm工具链。
    2. Linux平台。
    3. C-内在代码。
    4. 问题:

      1. 为什么NEON版本比ARM版本花费更多时间? (虽然我已经注意到使用批次中具有最小循环的固有物质)

      2. 如何实现我想要达到的目标? (与NEON的效率)

      3. 有人可以指点我或分享一些使用ARM-NEON的互操作的示例实现(伪代码/算法/代码,而不是理论实现论文或会谈)吗?

        < / LI>

        非常感谢任何帮助。

        这是我的代码:

        uint16_t do_csum(const unsigned char * buff, int len)
        {
        int odd, count, i;
        
        uint32x4_t result = veorq_u32( result, result), sum = veorq_u32( sum, sum); 
        uint16x4_t data, data_hi, data_low, data8;
        uint16x8_t dataq;
        uint16_t result16, disp[20] = {0,0,0,0,0,0,0,0,0,0};
        
        if (len <= 0)
            goto out;
        odd = 1 & (unsigned long) buff;
        if (odd) {
            uint8x8_t data1 = veor_u8( data1, data1); 
            data1 = (uint16x4_t)vld1_lane_u8((uint8_t *)buff, data1, 0); //result = *buff << 8;
            data1 = (uint16x4_t)vshl_n_u16( data1, 8);
        
            len--;
            buff++;
            result = vaddw_u16(result, data1);
        }
        count = len >> 1;       /* nr of 16-bit words.. */
        if (count) {
            if (2 & (unsigned long) buff) {
                uint16x4_t data2 = veor_u16( data2, data2); 
                data2 = (uint16x4_t) vld1_lane_u16((uint16_t *)buff, data2, 0); //result += *(unsigned short *) buff;
                count--;
                len -= 2;
                buff += 2;
                result = vaddw_u16( result, data2);
            }
            count >>= 1;        /* nr of 32-bit words.. */
            if (count) {
                if (4 & (unsigned long) buff) {
                    uint32x2_t data4 = (uint16x4_t) vld1_lane_u32((uint32_t *) buff, data4, 0);
                    count--;
                    len -= 4;
                    buff += 4;
                    result = vaddw_u16( result, data4);
                }
                count >>= 1;    /* nr of 64-bit words.. */
                if (count) {
                    if (8 & (unsigned long) buff) {
                        uint64x1_t data8 = vld1_u64((uint64_t *) buff); 
                        count--;
                        len -= 8;
                        buff += 8;
                        result = vaddw_u16( result,(uint16x4_t)data8);
                    }
                    count >>= 1;    /* nr of 128-bit words.. */
                    if (count) {
                        do {
                            dataq = (uint16x8_t)vld1q_u64((uint64_t *) buff); // VLD1.64 {d0, d1}, [r0]
                            count--;
                            buff += 16;
        
                            sum = vpaddlq_u16(dataq);   
                            vst1q_u16( disp, dataq); // VST1.16 {d0, d1}, [r0]
        
                            result = vaddq_u32( sum, result);
                        } while (count);
                    }
                    if (len & 8) {
                        uint64x1_t data8 =  vld1_u64((uint64_t *) buff); 
                        buff += 8;
                        result = vaddw_u16( result, (uint16x4_t)data8);
                    }
                }
                if (len & 4) {
                    uint32x2_t data4 = veor_u32( data4, data4); 
        
                    data4 = (uint16x4_t)vld1_lane_u32((uint32_t *) buff, data4, 0);//result += *(unsigned int *) buff;
                    buff += 4;
                    result = vaddw_u16( result,(uint16x4_t) data4);
                }
            }
            if (len & 2) {
                uint16x4_t data2 = veor_u16( data2, data2); 
                data2 = (uint16x4_t) vld1_lane_u16((uint16_t *)buff, data2, 0); //result += *(unsigned short *) buff;
                buff += 2;
                result = vaddw_u16( result, data2);
            }
        }
        if (len & 1){
            uint8x8_t data1 = veor_u8( data1, data1); 
            data1 = (uint16x4_t) vld1_lane_u8((uint8_t *)buff, data1, 0); //result = *buff << 8;
            result = vaddw_u8( result, data1);
        }
        
        
        result16 = from128to16(result);
        
        if (odd)
            result16 = ((result16 >> 8) & 0xff) | ((result16 & 0xff) << 8);
        
        out:
            return result16;
        }
        

1 个答案:

答案 0 :(得分:6)

您可以改进的一些事项:

  • 摆脱商店到disp - 这看起来像调试代码留下了?
  • 不要在主循环中进行水平加法 - 只需在循环中进行部分(垂直)求和,然后在循环后进行最后一次水平加法(有关如何执行此操作的示例,请参阅this answer - 它是对于SSE但原则是相同的)
  • 确保使用gcc -O3 ...从编译器优化中获得最大收益
  • 请勿使用goto! (不影响表现,但却是邪恶的。)