_mm_movemask_ps 的 arm-neon 实现.

废话不多说. 用SIMD指令集优化Vector 运算的时候一定会大量使用 _mm_movemask_ps 来获取比较结果.

但是arm-neon 并没有提供这个指令.

参考: http://stackoverflow.com/questions/11870910/sse-mm-movemask-epi8-equivalent-method-for-arm-neon

我没对这个帖子中的结果进行测试, 但是从实现上看, 不是最好的. 其性能,健壮性都不如DirectXMath中的

下面这个是DirectXMath 库的实现:

uint8x8x2_t vTemp = vzip_u8(vget_low_u8(vResult), vget_high_u8(vResult));
            uint16x4x2_t vTemp1 = vzip_u16((uint16x4_t)vTemp.val[0], (uint16x4_t)vTemp.val[1]);
            return (vget_lane_u32(vTemp1.val[1], 1) == 0xFFFFFFFFU);

这里用到2个zip 来混排4个分量, 并最终将每个分量的高位(most sign)组成一个uint32_t 的值,来检查判断结果.

比如 0xFFFFFFFFU 标识全部通过. 0xFF0000FF 则表示 Vector [x,y,z,w]中的y,z 分量未通过检测.

这本身没有问题, 问题是zip 指令开销有点大.

 

下面是我的实现. 其中Mask cElementIndex 等是因为写博客零时添加的. 实际实现中, 这两个变量都是constexpr 的.

最终在lumia 950 xl 上测试 性能要比DirectXMath 提升约 23% 的性能. (用一个 位与 , 位或 , PADD 代替了两个 zip ).

constexpr   const uint32_t  cElementIndex[4]{1,2,4,8};

static inline uint32_t vmaskq_u32(uint32x4_t& CR)
{
    static const uint32x4_t Mask = vld1q_u32(cElementIndex);
        // extract element index bitmask from compare result.
    uint32x4_t vTemp = vandq_u32(CR, Mask);
    uint32x2_t vL = vget_low_u32(vTemp);    // get low 2 uint32 
    uint32x2_t vH = vget_high_u32(vTemp);  // get high 2 uint32
    vL = vorr_u32(vL, vH);  
    vL = vpadd_u32(vL, vL);
    return vget_lane_u32(vL, 0); 
}

我的实现中, 添加了一个多余的东西,就是cElementIndex {1,2,4,8} 其实可以用{1,1,1,1}或其它非0值都可以.

但是用{1,2,4,8}的目的是便于对最终结果更容易理解, 更符合 movemask 的规范.

比如 if(vmaskq_u32(IntersectionResult) &(0x6)) // 检查IntersectionResult[x,y,z,w]中的  Y,Z 分量是否通过.

 

 

 

 

后面在把我另外一个也是坑的东西给贴出来吧. 那就是 _mm_div_ps 的neon 实现(二次牛顿插值...).

 

 

 

 

   

posted @ 2016-04-27 23:52  访问异常  阅读(1214)  评论(0编辑  收藏  举报