NEON 增加运行时间

Posted

技术标签:

【中文标题】NEON 增加运行时间【英文标题】:NEON increasing run time 【发布时间】:2017-03-12 17:19:41 【问题描述】:

我目前正在尝试优化我的一些图像处理代码以使用 NEON 指令。

假设我需要非常大的浮点数组,并且我想将第一个的每个值与第二个的三个连续值相乘。 (第二个是三倍大。)

float*     l_ptrGauss_pf32   = [...];
float*     l_ptrLaplace_pf32 = [...]; // Three times as large 

for (uint64_t k = 0; k < l_numPixels_ui64; ++k)

    float l_weight_f32 = *l_ptrGauss_pf32;

    *l_ptrLaplace_pf32 *= l_weight_f32;
    ++l_ptrLaplace_pf32;
    *l_ptrLaplace_pf32 *= l_weight_f32;
    ++l_ptrLaplace_pf32;
    *l_ptrLaplace_pf32 *= l_weight_f32;
    ++l_ptrLaplace_pf32;

    ++l_ptrGauss_pf32;

所以当我用 NEON 内部函数替换上面的代码时,运行时间大约长了 10%。

float32x4_t l_gaussElem_f32x4;
float32x4_t l_laplElem1_f32x4;
float32x4_t l_laplElem2_f32x4;
float32x4_t l_laplElem3_f32x4;

for( uint64_t k=0; k<(l_lastPixelInBlock_ui64/4); ++k)

    l_gaussElem_f32x4 = vld1q_f32(l_ptrGauss_pf32);
    l_laplElem1_f32x4 = vld1q_f32(l_ptrLaplace_pf32);
    l_laplElem2_f32x4 = vld1q_f32(l_ptrLaplace_pf32+4);
    l_laplElem3_f32x4 = vld1q_f32(l_ptrLaplace_pf32+8);

    l_laplElem1_f32x4 = vmulq_f32(l_gaussElem_f32x4, l_laplElem1_f32x4);
    l_laplElem2_f32x4 = vmulq_f32(l_gaussElem_f32x4, l_laplElem2_f32x4);
    l_laplElem3_f32x4 = vmulq_f32(l_gaussElem_f32x4, l_laplElem3_f32x4);

    vst1q_f32(l_ptrLaplace_pf32,   l_laplElem1_f32x4);
    vst1q_f32(l_ptrLaplace_pf32+4, l_laplElem2_f32x4);
    vst1q_f32(l_ptrLaplace_pf32+8, l_laplElem3_f32x4);

    l_ptrLaplace_pf32 += 12;
    l_ptrGauss_pf32   += 4;

两个版本均使用 Apple LLVM 8.0 使用 -Ofast 编译。即使没有 NEON 内在函数,编译器真的那么擅长优化这段代码吗?

【问题讨论】:

你检查编译器生成的代码了吗? Clang 确实使用了 Neon 指令(假设你“让它”),而你不必求助于内在函数,并且通常比你使用内在函数时做得更好(因为它会更聪明地安排指令和操作,其中内在函数往往被视为“必须完全这样”)。 也可能是因为你的手动展开,循环的展开或其他重新排列变得不同。 这是针对 armv7 还是 arm64 的?另外,小切线,但最好将size_t 用于kl_lastPixelInBlock_ui64(因为在armv7 上是32 位)。此外,指针可以别名吗?如果没有,将它们标记为restrict 应该会有所帮助。 【参考方案1】:

您的代码包含相对较多的向量加载操作和一些乘法操作。所以我建议优化向量的加载。有两个步骤:

在阵列中使用对齐的内存。 使用预取。

为了做到这一点,我建议使用下一个功能:

inline float32x4_t Load(const float * p)

    // use prefetch:
    __builtin_prefetch(p + 256); 
    // tell compiler that address is aligned:
    float * _p = (float *)__builtin_assume_aligned(p, 16);
    return vld1q_f32(_p);

【讨论】:

以上是关于NEON 增加运行时间的主要内容,如果未能解决你的问题,请参考以下文章

当我在 Linux 上运行使用硬浮点选项构建的 Neon 代码时出现分段错误

Eclipse Neon 上的目标运行时未列出 Tomcat 服务器

使用 neon-wallet-db + neon-js + NEO-cli /rpc 搭建轻钱包服务端

mips64el-linux-android-g++:错误:无法识别的命令行选项“-mfpu=neon”

包含 NEON 或 VFP3 指令的 ELF?

将 NEON 添加到 Android.mk 但得到“传递给空闲的无效地址 0xe76a4080:未分配值”