首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >关于手臂霓虹灯优化的建议

关于手臂霓虹灯优化的建议
EN

Stack Overflow用户
提问于 2013-07-05 14:08:17
回答 2查看 1.3K关注 0票数 1

为了学术目的,我想尝试写一个ARM霓虹灯优化的以下算法,甚至只测试是否有可能获得任何性能的改善。我认为这不是一个很好的SIMD优化的候选,因为结果被合并到一起,失去了并行化增益。

这就是算法:

代码语言:javascript
复制
const uchar* center = ...;

int t0, t1, val;
t0 = center[0]; t1 = center[1];
val = t0 < t1;
t0 = center[2]; t1 = center[3];
val |= (t0 < t1) << 1;
t0 = center[4]; t1 = center[5];
val |= (t0 < t1) << 2;
t0 = center[6]; t1 = center[7];
val |= (t0 < t1) << 3;
t0 = center[8]; t1 = center[9];
val |= (t0 < t1) << 4;
t0 = center[10]; t1 = center[11];
val |= (t0 < t1) << 5;
t0 = center[12]; t1 = center[13];
val |= (t0 < t1) << 6;
t0 = center[14]; t1 = center[15];
val |= (t0 < t1) << 7;

d[i] = (uchar)val;

这就是我在手臂装配中所想的:

代码语言:javascript
复制
VLD2.8 {d0, d1} ["center" addr]

假设8位字符,这第一次操作应该加载所有的t0和t1值交替在2个寄存器。

代码语言:javascript
复制
VCLT.U8 d2, d0, d1

用于所有比较的“小于”的单个操作。注意:我读到VCLT只有在#0常量作为第二个操作数时才有可能,所以这必须在>=中倒置。阅读ARM文档我认为每8位值的结果将是true (11111111)的"all 1“或false的"all 0”(00000000)。

代码语言:javascript
复制
VSHR.U8 d4, d2, #7

这种右移将删除8位“单元格”中的8个值中的7个(主要是删除7个)。我使用d4是因为下一步将是在q2中映射的第一个d寄存器。

现在问题开始了:转移和ORs。

代码语言:javascript
复制
VSHLL.U8 q2[1], d4[1], 1
VSHLL.U8 q2[2], d4[2], 2
...
VSHLL.U8 q2[7], d4[7], 7

我只能想象这种方式(如果有可能的话)对左班的影响。应该根据文档指定Q2而不是d4。

代码语言:javascript
复制
VORR(.U8) d4[0], d4[1], d4[0]
VORR(.U8) d4[0], d4[2], d4[0]
...
VORR(.U8) d4[0], d4[7], d4[0]

最后一步应该给出结果。

代码语言:javascript
复制
VST1.8 d4[0], [d[i] addr]

结果的简单存储。

这是我第一次使用手臂霓虹灯,所以很多假设可能是不正确的。帮助我理解可能的错误,并提出一个更好的解决方案,如果可能的话。

编辑:--这是建议解决方案之后的最终工作代码:

代码语言:javascript
复制
__asm__ __volatile ("VLD2.8 {d0, d1}, [%[ordered_center]] \n\t"
"VCGT.U8 d2, d1, d0 \n\t"
"MOV r1, 0x01 \n\t"
"MOV r2, 0x0200 \n\t"
"ORR r2, r2, r1 \n\t"
"MOV r1, 0x10 \n\t"
"MOV r3, 0x2000 \n\t"
"ORR r3, r3, r1 \n\t"
"MOVT r2, 0x0804 \n\t"
"MOVT r3, 0x8040 \n\t"
"VMOV.32 d3[0], r2 \n\t"
"VMOV.32 d3[1], r3 \n\t"
"VAND d0, d2, d3 \n\t"
"VPADDL.U8 d0, d0 \n\t"
"VPADDL.U16 d0, d0 \n\t"
"VPADDL.U32 d0, d0 \n\t"
"VST1.8 d0[0], [%[desc]] \n\t"
:
: [ordered_center] "r" (ordered_center), [desc] "r" (&desc[i])
: "d0", "d1", "d2", "d3", "r1", "r2", "r3");
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-07-05 14:24:38

比较之后,您有一个由0xff0x00表示的8个布尔值数组。SIMD比较(在任何架构上)产生这些值的原因是为了使它们对于位掩码操作(和/或NEON的情况下的位选择)有用,这样您就可以快速地将结果转化为任意值,而不需要乘。

因此,与其将它们简化为10并将它们移动,您会发现用常量0x8040201008040201掩盖它们会更容易。然后,每个车道包含与其在最终结果中的位置相对应的位。您可以将常量预加载到另一个寄存器中(我将使用d3)。

代码语言:javascript
复制
VAND d0, d2, d3

然后,为了组合结果,可以使用VPADD (而不是OR),它将相邻的车道对、d0[0] = d0[0] + d0[1]d0[1] = d0[2] + d0[3]等.由于位模式不重叠,所以没有像或一样好的进位和添加工作。另外,由于输出是输入量的一半,所以我们必须用垃圾填充下半部分。为此,我使用了第二份d0

您需要进行三次添加才能使所有列合并。

代码语言:javascript
复制
VPADD.u8 d0, d0, d0
VPADD.u8 d0, d0, d0
VPADD.u8 d0, d0, d0

现在结果将出现在d0[0]上。

正如您所看到的,d0还有七个结果的空间;VPADD操作的一些车道一直在处理垃圾数据。如果您能够一次获取更多的数据,并在执行过程中将这些额外的工作提供给您,这样就不会浪费所有的算术,这样会更好。

编辑

假设循环被展开了四次;使用d4d5d6d7中的结果;前面提到的常量应该加载到例如d30d31中,然后可以使用一些q寄存器算法:

代码语言:javascript
复制
VAND q0, q2, q15
VAND q1, q3, q15

VPADD.u8 d0, d0, d1
VPADD.u8 d2, d2, d3
VPADD.u8 d0, d0, d2
VPADD.u8 d0, d0, d0 

最后的结果是d00.3,或者简单地说是d0中的32位值。

似乎有很多寄存器可以自由地展开,但我不知道在其他计算中会用到多少寄存器。

票数 1
EN

Stack Overflow用户

发布于 2013-07-25 11:36:25

从显式地表示并行性开始:

代码语言:javascript
复制
int /* bool, whatever ... */ val[8] = {
    center[0] < center[1],
    center[2] < center[3],
    center[4] < center[5],
    center[6] < center[7],
    center[8] < center[9],
    center[10] < center[11],
    center[12] < center[13],
    center[14] < center[15]
};
d[i] = extract_mask(val);

这些变化相当于一个“掩码移动”,因为您希望每个比较都产生一个比特。

对上述16个值的比较可以通过首先执行结构加载(vld2.8)将相邻字节拆分为两个uint8x8_t,然后进行并行比较。其结果是一个以字节表示0xff0x000x00。您希望在各自的位位置中各有一位。

这是一个“面具摘录”;在英特尔的SSE2上,那应该是MASKMOV,但在Neon上,并不存在直接的等价物;上面显示的三种vpadd (或更多信息见epi8 equivalent method for ARM NEON )是一个合适的替代品。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17490870

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档