标签:
废话不多说. 用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 实现(二次牛顿插值...).
_mm_movemask_ps 的 arm-neon 实现.
标签:
原文地址:http://www.cnblogs.com/lethep/p/5440986.html