1
  image width  = 4000
  image height = 2000
  number of iterations = width * height / 64 = 125 000  

  asm volatile(
  "1:                                                    \n\t"
  "prfm     pldl1keep, [%[src], #128]                    \n\t"

  "LD4   {v0.16B, v1.16B, v2.16B,v3.16B},  [%[src]], #64 \n\t" //5 cycles

  "MOVI v10.16B, #12                                     \n\t" //1 cycle

  "AND v4.16B, v0.16B, v10.16B                           \n\t" //1 cycle
  "AND v5.16B, v1.16B, v10.16B                           \n\t" //1 cycle
  "AND v6.16B, v2.16B, v10.16B                           \n\t" //1 cycle
  "AND v7.16B, v3.16B, v10.16B                           \n\t" //1 cycle

  "MOVI v11.16B, #20                                     \n\t" //1 cycle

  "SUB v8.16B, v4.16B, v11.16B                           \n\t" //1 cycle
  "SUB v9.16B, v5.16B, v11.16B                           \n\t" //1 cycle
  "SUB v10.16B, v6.16B, v11.16B                          \n\t" //1 cycle
  "SUB v11.16B, v7.16B, v11.16B                          \n\t" //1 cycle

  "ST4   {v8.16B, v9.16B, v10.16B,v11.16B}, [%[dst]], #64 \n\t" //5 cycles

  "subs  %[simd_it], %[simd_it], #1                      \n\t" //1 cycle
  "bne   1b                                              \n\t" //4 cycles

每次迭代大约 25 个时钟周期 每个图像 125 000 * 25 = 3 125 000 个周期

我在 ARM NEON 内联汇编中实现了示例代码(代码没有意义)。有(大约,我使用了来自 ARMv7 的信息,这是 ARMv8,但我不希望这个数字会更高)每个整个图像有 3,1M 个时钟周期。

我在频率为 1Ghz 的处理器上运行。如果处理器每秒执行 1G 个时钟周期,它应该在 4ms 内完成 3,1M 个周期。但我测量的时间约为 14 毫秒。

为什么不匹配?没有其他进程在同一核心上运行。

有条件分支,对于每次迭代(bne),它们将导致需要重新填充管道。如果条件跳过将被删除,而不是它,将编写数千行长的代码,它会快 3-4 倍吗?谢谢

4

1 回答 1

1
  • 没有理由使用ld4这个st4算法。
  • 您应该在循环之外进行v10初始化。v11
  • 您不应该subsb.ne.
  • 您应该避免使用v8tov15因为它们必须根据aapcs.
  • 分支预测器将使条件分支消耗 0 个周期。

    movi    v4.16b, #12
    movi    v5.16b, #20

.balign 64
1:
    ld1     {v0.16b-v3.16b}, [src], #64
    subs    simd_it, simd_it, #1

    and     v0.16b, v0.16b, v4.16b
    and     v1.16b, v1.16b, v4.16b
    and     v2.16b, v2.16b, v4.16b
    and     v3.16b, v3.16b, v4.16b

    sub     v0.16b, v0.16b, v5.16b
    sub     v1.16b, v1.16b, v5.16b
    sub     v2.16b, v2.16b, v5.16b
    sub     v3.16b, v3.16b, v5.16b

    st1     {v0.16b-v3.16b}, [dst], #64
    b.gt    1b

上面的代码每次迭代消耗 16 个周期,理论上

125,000 * 16 = 2,000,000 = 2ms @ 1Ghz

我想你测量的时间大约是 12 毫秒,因为我觉得你要么没有启用缓存,要么是在非缓存区域读/写。

对于四个寄存器的高速缓存未命中惩罚,每次迭代大约 80 个周期的损失看起来非常合理。

于 2020-05-11T02:36:54.453 回答