Optimization SSE是多余的还是令人沮丧的?

Optimization SSE是多余的还是令人沮丧的?,optimization,sse,simd,auto-vectorization,x86,Optimization,Sse,Simd,Auto Vectorization,X86,环顾这里和互联网,我可以找到很多关于现代编译器在许多实际情况下击败SSE的帖子,我刚刚在我继承的一些代码中遇到,当我禁用2006年为基于整数的图像处理编写的SSE代码并强制将代码下放到标准C分支时,它运行得更快 在具有多核和高级流水线等功能的现代处理器上,较旧的SSE代码的性能是否低于gcc-O2?您很可能会看到现代编译器使用SSE4。但即使他们坚持相同的ISA,他们通常也更擅长安排日程。让SSE单元保持忙碌意味着对数据流进行仔细管理 内核是不相关的,因为每个指令流(线程)都在单个内核上运行。是

环顾这里和互联网,我可以找到很多关于现代编译器在许多实际情况下击败SSE的帖子,我刚刚在我继承的一些代码中遇到,当我禁用2006年为基于整数的图像处理编写的SSE代码并强制将代码下放到标准C分支时,它运行得更快


在具有多核和高级流水线等功能的现代处理器上,较旧的SSE代码的性能是否低于gcc-O2?

您很可能会看到现代编译器使用SSE4。但即使他们坚持相同的ISA,他们通常也更擅长安排日程。让SSE单元保持忙碌意味着对数据流进行仔细管理

内核是不相关的,因为每个指令流(线程)都在单个内核上运行。

是的,但主要是在同样的意义上,不鼓励编写内联程序集

SSE指令(和其他向量指令)已经存在了足够长的时间,编译器现在已经很好地理解了如何使用它们生成高效代码


除非你对自己正在做的事情有一个很好的了解,否则你不会比编译器做得更好。即使这样,为了打败编译器所付出的努力也往往是不值得的。即使这样,我们为一个特定CPU进行优化的努力也可能无法为其他CPU生成良好的代码。

您必须小心使用微基准点。测量你认为自己是什么以外的东西真的很容易。就一级I-cache/uop缓存和分支预测器条目的压力而言,微基准通常根本不考虑代码大小

在大多数情况下,微基准点通常能够预测所有分支,而经常调用但不在紧密循环中的例程在实践中可能做得不好


这些年来,苏格兰和南方能源公司增加了许多新成员。新代码的合理基线是SSSE3(可在Intel Core2及更高版本、AMD推土机及更高版本中找到),只要存在标量回退。快速字节洗牌(
pshufb
)的加入在某些方面改变了游戏规则。SSE4.1也为整数代码添加了很多好东西。若旧代码不使用它,编译器输出或新的手写代码可以做得更好

目前我们使用的是AVX2,它在256b寄存器中同时处理两个128b通道。这里有一些256b洗牌指令。AVX/AVX2提供了以前所有SSE指令的3个操作数(非破坏性dest、src1、src2)版本,这有助于提高代码密度,即使使用256b操作的双通道方面是一个缺点(或者针对不使用AVX2的AVX1的整数代码)

一两年后,第一款AVX512台式机硬件可能会面世。这增加了大量功能强大的功能(屏蔽寄存器,填补高度非正交SSE/AVX指令集中的更多空白),以及更宽的寄存器和执行单元


如果旧的SSE代码在编写时只比标量代码稍微快一点,或者没有人对其进行基准测试,那么这可能就是问题所在。编译器的进步可能会导致标量C生成的代码击败旧的SSE,这需要大量的洗牌。有时,将数据混洗到向量寄存器中的成本会消耗掉数据到达后的所有加速

或者根据您的编译器选项,编译器甚至可能是自动矢量化的。IIRC,
gcc-O2
无法启用
-ftree矢量化
,因此您需要
-O3
进行自动矢量控制


另一件可能阻碍旧SSE代码的事情是,它可能假设未对齐的加载/存储速度很慢,并使用
palignr
或类似技术在寄存器中未对齐的数据和对齐的加载/存储之间切换。因此,旧代码可能会以一种实际上比最近的更慢的方式为旧的微阵列进行调优

因此,即使不使用任何以前不可用的指令,调整不同的微体系结构也很重要


编译器输出很少是最优的,尤其是如果您没有告诉它指针没有别名(
restrict
),或者没有对齐。但它通常运行得相当快。通常,您可以对其进行一些改进(特别是通过减少UOP/INSN来完成相同的工作,从而使其更易于超线程读取),但您必须这样做。例如,Intel Sandybridge及更高版本只能使用一个寄存器寻址模式对内存操作数进行微融合。维基上的其他链接


因此,要回答这个问题,SSE指令集绝不是多余的或不受鼓励的。不鼓励在asm中直接使用它,以供随意使用(改用intrinsic)。不鼓励使用intrinsic,除非您可以实际获得比编译器输出更快的速度。如果它们现在被绑定了,对于未来编译器来说,用标量代码做得更好,比用向量本质做得更好。

< P>只是添加到已经很好的一个基本点,就是编译器不知道程序员对问题域所知道的一切,程序员通常没有简单的方法来表达有用的约束和其他相关信息,而真正智能的编译器可能会利用这些约束和信息来帮助矢量化。在许多情况下,这会给程序员带来巨大的优势

例如,对于以下简单情况:

//添加两个浮点数组
浮点数a[N],b[N],c[N];
对于(int i=0;i
任何一个好的编译器都应该能够用SSE/AVX/任何东西很好地实现这一点,而用SIMD intrinsic实现这一点也没有什么意义。除了相对较小的问题,如数据对齐,或N值的可能范围外,编译器生成的代码应该接近最优

但是如果你有一些不那么直截了当的事情,例如

//使用LUT将4位值的数组映射为8位值
const uint8_t LUT[1