苹果的PowerPC memcpy为何如此快速?

18

我为了寻找一种好的PowerPC内存策略,编写了几个复制函数。使用Altivec或带有缓存提示(dcb*)的fp寄存器,对于大数据,与简单的字节复制循环相比,性能提高了一倍。最初对此感到满意,于是我加入了一个常规的memcpy来进行比较……它比我最好的快了10倍!我没有重写memcpy的意图,但我希望从中学习,并加速一些简单的图像滤镜,这些滤镜大部分时间都在将像素从内存中移动进出。

Shark分析显示,它们的内部循环使用dcbt预取,每次有4个矢量读取和4个矢量写入。在将我的最佳函数调整为每次传输64字节后,memcpy的性能优势仍然令人尴尬。我正在使用dcbz来释放带宽,而Apple则什么也不用,但两个代码都倾向于在存储上犹豫。

预取
  dcbt future
  dcbt distant future
加载东西
  lvx图像
  lvx图像+16
  lvx图像+32
  lvx图像+48
  图像+=64
准备存储
  dcbz过滤
  dcbz过滤+32
存储东西
  stvxl过滤
  stvxl过滤+16
  stvxl过滤+32
  stvxl过滤+48
  过滤+=64
重复

有没有人对非常相似的代码为什么存在如此巨大的性能差距有一些想法?我希望用memcpy使用的任何秘密酱汁来改进真正的图像滤镜!

附加信息:所有数据都是矢量对齐的。我正在制作图像的一个过滤副本,而不是替换原始图像。该代码在PowerPC G4、G5和Cell PPU上运行。Cell SPU版本已经快得惊人了。

5个回答

7
鲨鱼分析表明,它们的内部循环使用dcbt进行预取,每次迭代有4个向量读取和4个向量写入。在调整我的最佳函数以每次迭代传输64字节后,我可能在陈述显而易见的事情,但由于你在问题中没有提到以下内容,因此指出可能是值得的:我敢打赌,苹果选择4个向量读取,然后是4个向量写入,这与G5的管道及其对“分派组”中乱序指令执行的管理一样重要,也可能与一个神奇的64字节完美行大小一样重要。你是否注意到Nick Bastin链接的bcopy.s中的跳行?这意味着开发人员考虑了G5如何消耗指令流。如果你想复制相同的性能,仅仅每次读取64字节的数据是不够的,你必须确保你的指令组填充良好(基本上,我记得指令可以被分为五个独立的指令组,前四个是非跳转指令,第五个只允许是跳转。细节更加复杂)。

编辑:您可能还对同一页上以下段落感兴趣:

dcbz指令仍然像G4和G3一样将内存的32字节段清零。然而,在G5上,这不是一个完整的缓存线,因此它不会带来您可能期望的性能优势。为了清零一个完整的128字节缓存线,G5新引入了dcbzl指令。


我之前没有考虑过调度组。G5的整个“指令汤”总是让我感到困惑,而我更喜欢使用Cell,因为它的执行模型更符合我的思维方式。至于编辑,代码已经针对更大的缓存行进行了修改。 - user242091

2

我不确定你在做什么,因为我看不到你的代码,但苹果的秘密武器在这里


我可以在Shark中看到反汇编,所以知道它们在复制循环中做了什么。只是想知道在那个似乎将其推入超速状态的循环之前有什么东西。那段代码应该有所帮助,所以感谢提供链接! - user242091
@Invisible Cow:是的,我只是希望它能提供更多有见地的上下文和评论。 - Nick Bastin
为G4和其32字节缓存行添加了一些代码到问题中。 - user242091
1
实际上,这是一个内核中的 bcopy。用户模式下 OP 看到的可能是 http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/osfmk/ppc/commpage/bcopy_970.s 上的那个。 - ohmantics

0

可能是因为CPU缓存的原因。尝试运行CacheGrind

Cachegrind是一个缓存分析器。它可以对您CPU中的I1、D1和L2缓存进行详细的模拟,因此可以准确地确定代码中缺失缓存的来源。它识别每行源代码的缓存缺失次数、内存引用次数和指令执行次数,并提供每个函数、每个模块和整个程序的摘要。它适用于任何语言编写的程序。Cachegrind运行程序的速度比正常速度慢20-100倍。


@Nick,你确定吗?http://en.wikipedia.org/wiki/Valgrind "截至3.4.0版本,Valgrind支持x86、x86-64和PowerPC上的Linux操作系统" - Andreas Bonini
1
@Andreas:它适用于Linux,但绝对不适用于Darwin。唯一支持的(而且仅仅是勉强支持)Darwin是x86。 - Nick Bastin
此外,我想这应该是一个相当低的优先级,因为在 PPC 上使用 Shark 应该会给你相同的见解。 - Nick Bastin
1
如果没有Shark,我会迷失不知所措,但它不能提供详细的缓存数据。它只显示表面上的涟漪(这个指令被挫败了),而无法看到底层的问题(在哪里?)。 - user242091

0

虽然这还不是一个答案,但你确定memcpy在实际移动数据吗?也许只是重新映射了写时复制。如果第一个和最后一个页面确实被复制,你仍将在Shark中看到内部memcpy循环。


0
如另一个答案中所提到的,“dcbz”在G5上的定义仅适用于32字节,因此在具有128字节缓存行的G5上使用此指令将会降低性能。您需要使用“dcbzl”来防止目标缓存行从内存中获取(并有效地将您有用的读取内存带宽减半)。

1
不要忘记 - 每128字节行只能使用1个“dcbzl”。看起来你的代码每32字节就执行一次“dcbz”。 - JanePhanie

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