2

我在 SSE2 intrinsincs 中有以下代码。它处理来自 Kinect 的输入。

__m128i md = _mm_setr_epi16((r0<<3)  | (r1>>5), ((r1<<6)  | (r2>>2) ), ((r2<<9)  | (r3<<1) | (r4>>7) ), ((r4<<4)  | (r5>>4) ), ((r5<<7)  | (r6>>1) ),((r6<<10) | (r7<<2) | (r8>>6) ), ((r8<<5)  | (r9>>3) ), ((r9<<8)  | (r10)   ));
md = _mm_and_si128(md, mmask);
__m128i mz = _mm_load_si128((__m128i *) &depth_ref_z[i]);
__m128i mZ = _mm_load_si128((__m128i *) &depth_ref_Z[i]);
mz = _mm_cmpgt_epi16(md, mz);
mZ = _mm_cmpgt_epi16(mZ, md);
mz = _mm_and_si128(mz, mZ);
md = _mm_and_si128(mz, md);
_mm_store_si128((__m128i *) frame,md)
if(_mm_movemask_epi8(mz)){ ... }

这基本上将 11 uint8_t (r0-r10​​) 解压缩到 SSE 寄存器中的 8 uint16_t(mmask 是常量并且之前创建的)。然后它从两个用作边界的数组中加载另外两个带有相应元素的寄存器。它检查它们并创建一个寄存器,其中不符合标准的元素被清零。然后它存储它们并进一步处理每个元素。当没有元素通过时,移动掩码可以作为一个很好的优化,在这种情况下可以跳过处理。

这很好用,现在我也想将它移植到 NEON。除了两部分外,大部分内容都很简单。查看 SSE2 代码中的汇编器输出(gcc),我发现它不是在 _mm_setr_epi16 中进行 8 次 uint16_t 移动,而是将它们转换为 uint32_t 并最终进行 4 次移动。这似乎很有效,因为编译器会处理它,所以我没有更改代码。我应该在 NEON 案例中手动应用它吗?而不是 8 个 vsetq_lane_u16 进行移位并执行 4 个 vsetq_lane_u32?我会遇到字节顺序问题吗?这是否值得?

最后一部分是移动蒙版,因为我找不到等价物。任何人都可以提出一些建议吗?

4

1 回答 1

1

我更喜欢从纯 C 代码而不是 SSE2 开始;在这个低级别可能不容易看到优化机会

于 2012-02-22T20:21:23.617 回答