问题:
- 您正在使用一种非常低效的算法进行循环内的计算
- 您的例程遭受了繁重的流水线互锁,逐条指令
void isNonNatural(int32_t * pDst, int32_t *pSrc, int n)
{
int32x4_t vec;
const int32x4_t one = vdupq_n_s32(1);
int32_t a;
unsigned int i;
if (n >= 4)
{
n -= 4;
while (1) {
do {
n -= 4;
vec = vld1q_s32(pSrc++);
vec = vqsubq_s32(vec, one);
vec = (int32x4_t) vshrq_n_u32((uint32x4_t) vec, 31);
vst1q_s32(pDst++, vec);
} while (n >= 0);
if (n <= -4) return;
// dealing with residuals
pSrc += n; // rewind pointers
pDst += n;
} // iterate for one last time
}
for (i = 0; i < n; ++i) {
a = *pSrc++;
if (a > 0) a = 0; else a = 1;
*pDst++ = a;
}
}
上面的这个函数应该比你的实现要快一些。
- 执行饱和减法 1,使 0 变为 -1,而 0x80000000 仍为 0x80000000
- 元素被移动了 31 位,因此只保留了符号位。
- 我可以使用 0xffffffff 而不是 1,您可以省略类型转换并改用
vshrq_n_s32。不过也不会更快。
- 注意余量管理。
对 NEON 进行编程就像驾驶一辆大卡车。你不应该像开紧凑型车一样驾驶它。
虽然 NEON 可以一次计算多个数据,主要是在一个周期内,但它具有更高的指令延迟,通常为 3~4 个周期。换句话说,在上面的实现中,每条指令都必须等待上一条指令返回结果。
实际上,避免这种情况的唯一方法是展开,深入的。
void isNonNatural_unroll(int32_t * pDst, int32_t *pSrc, int n)
{
int32x4_t vec1, vec2, vec3, vec4;
const int32x4_t one = vdupq_n_s32(1);
int32_t a;
unsigned int i;
if (n >= 16)
{
n -= 16;
while (1) {
do {
n -= 16;
vec1 = vld1q_s32(pSrc++);
vec2 = vld1q_s32(pSrc++);
vec3 = vld1q_s32(pSrc++);
vec4 = vld1q_s32(pSrc++);
vec1 = vqsubq_s32(vec1, one);
vec2 = vqsubq_s32(vec2, one);
vec3 = vqsubq_s32(vec3, one);
vec4 = vqsubq_s32(vec4, one);
vec1 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec1, 31);
vec2 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec2, 31);
vec3 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec3, 31);
vec4 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec4, 31);
vst1q_s32(pDst++, vec1);
vst1q_s32(pDst++, vec2);
vst1q_s32(pDst++, vec3);
vst1q_s32(pDst++, vec4);
} while (n >= 0);
if (n <= -16) return;
// dealing with residuals
pSrc += n; // rewind pointers
pDst += n;
} // iterate for one last time
}
if (n & 8)
{
vec1 = vld1q_s32(pSrc++);
vec2 = vld1q_s32(pSrc++);
vec1 = vqsubq_s32(vec1, one);
vec2 = vqsubq_s32(vec2, one);
vec1 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec1, 31);
vec2 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec2, 31);
vst1q_s32(pDst++, vec1);
vst1q_s32(pDst++, vec2);
}
if (n & 4)
{
vec1 = vld1q_s32(pSrc++);
vec1 = vqsubq_s32(vec1, one);
vec1 = (int32x4_t) vshrq_n_u32((uint32x4_t) vec1, 31);
vst1q_s32(pDst++, vec1);
}
n &= 3;
for (i = 0; i < n; ++i) {
a = *pSrc++;
if (a > 0) a = 0; else a = 1;
*pDst++ = a;
}
}
现在这个应该比以前的快很多,因为几乎所有的延迟都被隐藏了(快四倍多),前提是可怜的编译器不会把它搞砸。