【问题标题】:ARM neon performance issueARM neon 性能问题
【发布时间】:2013-06-06 08:08:31
【问题描述】:

考虑以下两段代码,第一段是C版本:

void __attribute__((no_inline)) proj(uint8_t * line, uint16_t length)
{
    uint16_t i;
    int16_t tmp;
    for(i=HPHD_MARGIN; i<length-HPHD_MARGIN; i++) {
        tmp = line[i-3] - 4*line[i-2] + 5*line[i-1] - 5*line[i+1] + 4*line[i+2] - line[i+3];
        hphd_temp[i]=ABS(tmp);
    }
}

第二个是使用霓虹内在函数的相同功能(边框除外)

void  __attribute__((no_inline)) proj_neon(uint8_t * line, uint16_t length)
{
    int i;
    uint8x8_t b0b7, b8b15, p1p8,p2p9,p4p11,p5p12,p6p13, m4, m5;
    uint16x8_t result;

    m4 = vdup_n_u8(4);
    m5 = vdup_n_u8(5);
    b0b7 = vld1_u8(line);
    for(i = 0; i < length - 16; i+=8) {
        b8b15 = vld1_u8(line + i + 8);
        p1p8  = vext_u8(b0b7,b8b15, 1);
        p2p9  = vext_u8(b0b7,b8b15, 2);
        p4p11 = vext_u8(b0b7,b8b15, 4);
        p5p12 = vext_u8(b0b7,b8b15, 5);
        p6p13 = vext_u8(b0b7,b8b15, 6);

        result = vsubl_u8(b0b7, p6p13); //p[-3]
        result = vmlal_u8(result, p2p9, m5); // +5 * p[-1];
        result = vmlal_u8(result, p5p12, m4);// +4 * p[1];
        result = vmlsl_u8(result, p1p8, m4); //-4 * p[-2];
        result = vmlsl_u8(result, p4p11, m5);// -5 * p[1];
        vst1q_s16(hphd_temp + i + 3, vabsq_s16(vreinterpretq_s16_u16(result)));
        b0b7 = b8b15;
    }
    /* todo : remaining pixel */

}

我对性能提升感到失望:大约 10 - 15 %。如果我查看生成的程序集:

  • C 版本转换为 108 指令循环
  • Neon 版本转换为 72 条指令循环。

但是,neon 代码中的一个循环计算的数据量是通过 C 循环的一个迭代的 8 倍,因此应该会看到显着的改进。

你对这两个版本之间的细微差别有什么解释吗?

其他细节: 测试数据为 10 Mpix 图片,C 版本的计算时间约为 2 秒。

CPU : ARM 皮质 a8

【问题讨论】:

  • 当您谈论指令数与性能时间时,看到性能提升低于预期的情况并不少见。特别是在处理大量数据时,例如图像。很可能,尽管您的 36 条指令存在差异,但大部分时间您都在等待内存中的事物转移,即使指令存在如此差异,您的大部分性能提升都来自于处理内存的 neon 代码比执行的指令数更好(分支预测、每个循环更大的块、更少的指令等)。
  • 您使用的是什么编译器、编译器版本和编译器命令行参数?你能包括内在版本的反汇编吗?
  • @ChrisCM :这不是 108 对 72,而是 108*8 对 72。即使考虑到双重问题,我仍然可以期待 6 倍的改进。
  • 我有点惊讶你没有像tmp = 1*(line[i-3] - line[i+3]) + 4*(line[i+2] - line[i-2]) + 5*(line[i-1] - line[i+1]) 那样重新排列你的术语,只是计算三个差异加上最后的点积(这将是一个不同的收集负载/向量子/并行乘法序列)。但这只有在您还没有内存带宽限制的情况下才有帮助。值得在循环前发出__pld(*(line + 8)),在循环内发出__pld(*(line + i + 16))
  • 你能发布你的代码的反汇编吗?众所周知,GNU 编译器有时会对 NEON 内在函数做一些疯狂的事情。 (我在这里闻到了老鼠的味道)

标签: c arm neon


【解决方案1】:

我要大胆猜测一下,缓存(数据)是您看不到预期性能大幅提升的原因。虽然我不知道您的芯片组是否支持缓存或在什么级别,但数据是否跨越缓存行、对齐不佳或在 CPU 同时执行其他操作(中断、线程等)的环境中运行.),那么这也会使您的结果变得混乱。

【讨论】:

  • 确实,当删除这些函数外部但在每次调用之间发生的操作时,我看到了 4 倍的增长。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-18
  • 2016-08-18
  • 1970-01-01
  • 2012-06-25
相关资源
最近更新 更多