关于循环中的值箝位的优化建议

optimisation advice on value clamping in a loop

本文关键字:优化 于循环 循环      更新时间:2023-10-16

我有一个紧密的循环,就像Chandler Carruth在CPP CON 2017中介绍的一样:https://www.youtube.com/watch?v=2EWejmkKlxs在这段视频的25分钟里,有一个这样的循环:

for (int& i:v)
i = i>255?255:i;

其中CCD_ 1是向量。这与我的程序中使用的代码完全相同,经过分析后,证明需要花费大量时间。

在他的演讲中,Chandler修改了程序集并加快了循环。我的问题是,在实践中,在生产代码中,优化这一点的推荐方法是什么?我们应该在c++代码中使用内联汇编吗?或者像Chandler那样,把C++代码编译成汇编程序,然后优化汇编程序?

假设采用x86体系结构,我们将非常欣赏优化上述for循环的示例。

我的问题是,在实践中,在生产代码中,建议使用什么方法来优化它?我们应该在c++代码中使用内联汇编吗?或者像Chandler那样,把C++代码编译成汇编程序,然后优化汇编程序?

对于生产代码,您需要考虑软件可能在自动构建系统中进行编译和链接。

在这样的系统中,您希望如何将代码更改应用于汇编程序代码?你可能会应用一个diff文件,但如果优化(或其他)设置被更改,如果切换到另一个编译器或。。。

现在剩下两个选项:在汇编文件(.s)中写入整个函数,或者在C++代码中包含内联汇编代码–后者可能具有将相关代码保持在同一翻译单元中的优点。

尽管如此,我还是让编译器生成一次汇编程序代码–具有最高的优化级别。然后,这些代码可以作为手工优化的(已经预先优化的)基础,其结果应该作为内联程序集粘贴回C++源文件或放置到单独的程序集源文件中。

Chandler修改了编译器的asm输出,因为这是一种简单的方法,可以进行一次性实验,以确定更改是否有用而不需要执行通常希望将asm循环或函数作为项目源代码的一部分的所有操作。

编译器生成的asm通常是优化循环的一个很好的起点,但实际上保持整个文件的原样并不是将循环的asm实现作为程序的一部分来实际维护的好方法,甚至不是可行的方法。看看@阿空加瓜的回答。

此外,它还违背了在用C++编写的文件中使用任何其他函数并可用于链接时间优化的目的。


Re:实际夹紧:

请注意,Chandler只是在尝试对非矢量化代码生成进行更改,并禁用了展开+自动矢量化。在现实生活中,希望您可以以SSE4.1或AVX2为目标,让编译器使用pminsdpminud自动向量化,以将有符号或无符号int箝位到上限。(也有其他元素大小。或者,如果没有SSE4.1,只有SSE2,也许您可以使用2xPACKSSDW=>packuswb(无符号饱和),然后使用零进行解包,最多可以使用4个双字元素向量。(如果你不能只使用uint8_t[]的输出!)

顺便说一句,在视频的评论中,Chandler说,事实证明他犯了一个错误,他看到的效果并不是因为一个可预测的分支与一个cmov。这可能是一件代码对齐的事情,因为从mov %ebx, (%rdi)更改为movl $255, (%rdi)会有所不同!

(众所周知,AMD CPU不会像P6家族那样出现寄存器读取停滞,应该可以很好地隐藏将存储耦合到负载的cmov的dep链,而不是用分支预测+通过分支的推测来破坏它。)


您很少真的想要使用手写循环。通常,只需修改C++源代码,就可以手动和/或欺骗编译器使asm更像您想要的。然后,未来的编译器可以自由地针对-march=some_future_cpu进行不同的调优。