快速的ARM NEON memcpy

5

我想在ARMv7核心上复制一张图片。朴素的实现方式是每行调用memcpy。

for(i = 0; i < h; i++) {
  memcpy(d, s, w);
  s += sp;
  d += dp;
}

我知道下面的内容:

d, dp, s, sp, w

所有的32字节都对齐,因此我的下一个(仍然相当幼稚)实现是沿着这个思路进行的。

for (int i = 0; i < h; i++) {
  uint8_t* dst = d;
  const uint8_t* src = s;
  int remaining = w;
  asm volatile (
    "1:                                               \n"
    "subs     %[rem], %[rem], #32                     \n"
    "vld1.u8  {d0, d1, d2, d3}, [%[src],:256]!        \n"
    "vst1.u8  {d0, d1, d2, d3}, [%[dst],:256]!        \n"
    "bgt      1b                                      \n"
    : [dst]"+r"(dst), [src]"+r"(src), [rem]"+r"(remaining)
    :
    : "d0", "d1", "d2", "d3", "cc", "memory"
  );
  d += dp;
  s += sp;
}

在多次迭代中,与memcpy相比,它的速度快了约150%(针对不同图像,因此不能利用缓存)。我觉得这应该远远不是最佳状态,因为我还没有使用预加载,但当我这样做时,性能似乎只会大幅恶化。这里有人有任何见解吗?


1
尝试将循环展开至少2倍。由于流水线和内存速度,NEON加载不是瞬间完成的。如果您执行2个加载后跟着2个存储,应该会看到好处。缓存预加载肯定可以加快速度,但读取前置距离需要调整到目标平台。 - BitBank
2
预取通常很难做到正确,并且很少有帮助。对于memcpy,你几乎没有任何计算周期可言,因此可能无法从预取中获得任何好处。 - Paul R
1
你有没有考虑使用DMA?我不知道复制速度会快多少或慢多少,但你可以同时进行其他处理,这样整个应用程序的速度可能会提高。 - Josh Petitt
如果检测到w=dp=sp的情况并进行单个memcpy,可以在常见情况下获得巨大的加速。 (或者,使用自定义行复制算法,只需运行一次而不是每行运行)。 - Dan Hulme
@JoshPetitt 这段代码必须在iOS设备上运行,我不认为我可以访问DMA? - robbie_c
显示剩余3条评论
1个回答

5
ARM在这方面有一篇很棒的技术笔记。

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka13544.html

你的性能肯定会因微架构而异,ARM的说明是关于A8的,但我认为它会给你一个不错的想法,底部的总结是对超出常规数字范畴的各种优缺点的很好讨论,比如哪些方法导致寄存器使用最少等等。

另外,正如其他评论者提到的,预取非常难以做到正确,并且会因不同的微架构而有所不同,具体取决于缓存的大小、每行大小以及缓存设计的一堆其他细节。如果不小心,你可能会卡住需要的缓存行。我建议在可移植代码中避免使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接