如何清除霓虹灯中除第一个非零车道之外的所有车道?
How to clear all but the first non-zero lane in neon?
我在uint32x4_t霓虹灯寄存器中有一个掩码。在这个掩码中,至少设置了 4 个整数中的 1 个(例如 0xffffffff(,但是,我可能有寄存器中设置了多个项目的情况。如何确保只设置一个?
在 C 伪代码中:
uint32x4_t clearmask(uint32x4_t m)
{
if (m[0]) { m[1] = m[2] = m[3] = 0; }
else if (m[1]) { m[2] = m[3] = 0; }
else if (m[2]) { m[3] = 0; }
return m;
}
基本上,我想清除除一条设置车道之外的所有车道。neon 中显而易见的直接实现可能是:
uint32x4_t cleanmask(uint32x4_t m)
{
uint32x4_t mx;
mx = vdupq_lane_u32(vget_low_u32(vmvnq_u32(m)), 0);
mx = vsetq_lane_u32(0xffffffff, mx, 0);
m = vandq_u32(m, mx);
mx = vdupq_lane_u32(vget_low_u32(vmvnq_u32(m)), 1);
mx = vsetq_lane_u32(0xffffffff, mx, 1);
m = vandq_u32(m, mx);
mx = vdupq_lane_u32(vget_high_u32(vmvnq_u32(m)), 0);
mx = vsetq_lane_u32(0xffffffff, mx, 2);
m = vandq_u32(m, mx);
return m;
}
如何在手臂霓虹灯中更有效地完成此操作?
很简单:
vceq.u32 q1, q0, #0
vmov.i8 d7, #0xff
vext.8 q2, q3, q1, #12
vand q0, q0, q2
vand d1, d1, d2
vand d1, d1, d4
总共 6 条指令,如果可以保持 Q3 恒定,则为 5 条。
下面的aarch64
版本必须更容易理解:
cmeq v1.4s, v0.4s, #0
movi v31.16b, #0xff
ext v2.16b, v31.16b, v1.16b, #12
ext v3.16b, v31.16b, v1.16b, #8
ext v4.16b, v31.16b, v1.16b, #4
and v0.16b, v0.16b, v2.16b
and v0.16b, v0.16b, v3.16b
and v0.16b, v0.16b, v4.16b
这是如何工作的
ext
/vext
从两个向量的串联中获取一个窗口,因此我们正在创建掩码
v0 = [ d c b a ]
v2 = [ !c !b !a -1 ]
v3 = [ !b !a -1 -1 ]
v4 = [ !a -1 -1 -1 ]
如果前面的任何元素不为零,则最高元素(d
(为零。
第二高的元素(c
(如果前面的任何元素(a
或b
(不为零,则为零。 等等。
当元素保证为 0 或 -1 时,mvn
也可以代替与零进行比较。
我的想法与您的未注释代码几乎相同:如果设置了该元素,则将倒置元素作为 AND 掩码广播到零个后续元素,否则保持向量不变。
但是,如果您在循环中使用它并且有 3 个备用向量寄存器,则您不能使用异或除一个元素之外的所有元素,而不是 MVN + 设置一个元素。
vdupq_lane_u32(vget_low_u32(m), 1);
似乎可以有效地编译为vdup.32 q9, d16[1]
,并且我的代码的那部分与您的代码相同(但没有 MVN(。
不幸的是,这是一个很长的串行依赖链;我们正在从 AND 结果创建下一个掩码,所以没有 ILP。 我没有看到一种好方法可以在降低延迟的同时仍然获得所需的结果。
uint32x4_t cleanmask_xor(uint32x4_t m)
{
// { a b c d }
uint32x4_t maska = { 0, ~0U, ~0U, ~0U};
uint32x4_t maskb = {~0U, 0, ~0U, ~0U};
uint32x4_t maskc = {~0U, ~0U, 0, ~0U};
uint32x4_t tmp = vdupq_lane_u32(vget_low_u32(m), 0);
uint32x4_t aflip = tmp ^ maska;
m &= aflip; // if a was non-zero, the rest are zero
tmp = vdupq_lane_u32(vget_low_u32(m), 1);
uint32x4_t bflip = tmp ^ maskb;
m &= bflip; // if b was non-zero, the rest are zero
tmp = vdupq_lane_u32(vget_high_u32(m), 0);
uint32x4_t cflip = tmp ^ maskc;
m &= cflip; // if b was non-zero, the rest are zero
return m;
}
(戈博尔特(
/* design notes
[ a b c d ]
[ a ~a ~a ~a ]
&:[ a 0 0 0 ]
or[ 0 b c d ]
= [ e f g h ]
[ ~f f ~f ~f ] // not b, because f can be zero when b isn't
= [ i j k l ]
...
*/
当负载被吊出一个循环时,这只是 9 条指令而不是 12 条指令,因为我们跳过了vmov.32 d1[0], r3
或任何在每个掩码中插入-1
的东西。 (与元素自身一起与 AND 等效于与-1U
一起做。veor
其他元素中的全 1 替换了vmvn
。
Clang在加载多个向量常量方面似乎效率低下:它分别设置每个地址,而不仅仅是将它们存储在可以从一个基本指针到达的位置。 因此,您可能需要考虑创建 3 个常量的替代策略。
#if 1
// clang sets up the address of each constant separately
// { a b c d }
uint32x4_t maska = { 0, ~0U, ~0U, ~0U};
uint32x4_t maskb = {~0U, 0, ~0U, ~0U};
uint32x4_t maskc = {~0U, ~0U, 0, ~0U};
#else
static const uint32_t maskbuf[] =
{ -1U, -1U, 0, -1U, -1U, -1U};
// unaligned loads.
// or load one + shuffle?
#endif
- 当回溯以零开始时,如何调试崩溃
- 在没有太多条件句的情况下,我如何避免被零除
- OpenInventor从9.8升级到10.4.2后,GLSL纹理返回零
- 矩阵向量乘法(cublasDgemv)返回零
- 是否可以对零模板参数进行模板专门化
- 处理除以零会导致<csignal>意外行为
- 清除前检查矢量
- 在C++中向零方向近似的最佳方法
- ifstream文件在从行中读取时被清除
- 打包可变参数模板具有零元素时的递归
- WINAPI 注册应用程序重新启动时不清除打开的套接字
- 如何将零填充的多维数组传递给 C++ 中的函数?
- 禁止显示由于常量为零而比较始终为假的警告
- 如何修复我的最大公约数代码?它适用于除零和零以外的所有数字
- Switch 语句(字符串)一直选择默认值,除非其为零
- 如何在将高位设置为零的同时将__m128i转换为__m256i?
- 如何清除/清空已打开的文件C++
- 如何清除霓虹灯中除第一个非零车道之外的所有车道?
- 是否应始终在析构函数中清除/归零成员数据
- 将零复制到 char 数组中也会清除其他 char 数组