Pav*_*l P 5 c++ arm intrinsics neon
如何在 8 个字节、8 个短裤或 8 个整数的数组中找到最大元素?我可能只需要 max 元素的位置、max 元素的值,或者两者都需要。
例如:
unsigned FindMax8(const uint32_t src[8]) // returns position of max element
{
unsigned ret = 0;
for (unsigned i=0; i<8; ++i)
{
if (src[i] > src[ret])
ret = i;
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
在-O2clang 展开循环但它不使用霓虹灯,这应该会提供不错的性能提升(因为它消除了许多依赖数据的分支?)
对于 8 个字节和 8 个 shorts 方法应该更简单,因为整个数组可以加载到单个 q 寄存器中。对于 arm64,使用 vmaxv_u16 应该更简单,但是如何使其在 32 位霓虹灯中高效?
正如 Marc 在评论中所指出的,当函数更改为返回最大值时,GCC 自动向量化器会为 neon64生成以下内容:
ldr q0, [x0, 16]
ld1r {v2.4s}, [x0]
ldr q1, [x0]
umax v0.4s, v0.4s, v2.4s
umax v0.4s, v0.4s, v1.4s
umaxv s0, v0.4s
umov w0, v0.s[0]
Run Code Online (Sandbox Code Playgroud)
我有一个函数可以进行相当复杂的数学运算,在计算结束时我最终得到uint32x4_t res结果,我需要的只是获取最大元素的索引。此单件是代码的最慢的部分,通过远低于这个数学重函数的其余部分的其余部分慢。
我尝试了三种不同的方法(根据分析器从最慢到最快):
使用neon 进行完整计算,最终单个32 位结果从neon 传输到arm。
vst1q_u32(src, res) 然后使用常规 C 代码查找最大元素的索引。
使用 vget_lane_u64 两次 vmov 到四个 32 位 arm 寄存器,然后使用一些位移来计算最大元素的索引。
unsigned compute(unsigned short *input)
{
uint32x4_t result = vld1q_u32((uint32_t*)(input));
// some computations...
// ... and at the end I end up with res01 and res23
// and I need to get index of max element from them:
uint32x2_t res01 = vget_low_u32(result);
uint32x2_t res23 = vget_high_u32(result);
// real code below:
uint64_t xres01 = vget_lane_u64(vreinterpret_u64_u32(res01), 0);
uint64_t xres23 = vget_lane_u64(vreinterpret_u64_u32(res23), 0);
unsigned ret = 0;
uint32_t xmax0 = (uint32_t)(xres01 & 0xffffffff);
uint32_t xmax1 = (uint32_t)(xres01 >> 32);
uint32_t xmax2 = (uint32_t)(xres23 & 0xffffffff);
uint32_t xmax3 = (uint32_t)(xres23 >> 32);
if (xmax1 > xmax0)
{
xmax0 = xmax1;
ret = 1;
}
if (xmax2 > xmax0)
{
xmax0 = xmax2;
ret = 2;
}
if (xmax3 > xmax0)
ret = 3;
return ret;
}
Run Code Online (Sandbox Code Playgroud)
使用完整霓虹灯计算的版本这样做:
{1u<<31, 1u<<30, 1u<<29, 1u<<28 }也许其他地方的问题,请参阅我正在尝试优化的实际代码。我的优化版本,只有最后一块需要改进。不知何故,分析器显示 80% 的时间花在我计算最大索引的最后几行。有任何想法吗?将这个简单的 c-loop 更改为成对的 regs 将整个功能提高了 20-30%。请注意,根据分析器,两个 vst1_u32 是函数花费大部分时间的那些。
我可以尝试什么其他方法?
更新: 函数末尾的减速似乎与代码无关。我不知道为什么,但是当我尝试根据我调用它们的顺序运行不同版本的函数时,我得到了 3-4 倍的时间变化。此外,通过不同的测试,如果函数结束时没有停顿,那么完整的霓虹灯版本似乎是最快的,我不确定为什么会发生这种停顿。出于这个原因,我创建了一个新问题来找出原因。
| 归档时间: |
|
| 查看次数: |
934 次 |
| 最近记录: |