我正在考虑更改一些当前需要16字节对齐数组的代码高性能代码,并使用_mm_load_ps
来放宽对齐约束并使用_mm_loadu_ps
。关于SSE指令的内存对齐的性能影响有很多神话,所以我做了一个小的测试用例,它应该是一个内存带宽限制的循环。使用对齐或未对齐的加载内在函数,它通过大型数组运行100次迭代,使用SSE内在函数对元素求和。源代码
在这儿。 https://gist.github.com/rmcgibbo/7689820
带有Sandy Bridge Core i5的64位Macbook Pro的结果如下。数字越小表示性能越快。在阅读结果时,我发现在未对齐的内存中使用_mm_loadu_ps基本上没有性能损失。
我觉得这很令人惊讶。这是一个公平的测试/合理的结论吗?在哪些硬件平台上存在差异?
$ gcc -O3 -msse aligned_vs_unaligned_load.c && ./a.out 200000000
Array Size: 762.939 MB
Trial 1
_mm_load_ps with aligned memory: 0.175311
_mm_loadu_ps with aligned memory: 0.169709
_mm_loadu_ps with unaligned memory: 0.169904
Trial 2
_mm_load_ps with aligned memory: 0.169025
_mm_loadu_ps with aligned memory: 0.191656
_mm_loadu_ps with unaligned memory: 0.177688
Trial 3
_mm_load_ps with aligned memory: 0.182507
_mm_loadu_ps with aligned memory: 0.175914
_mm_loadu_ps with unaligned memory: 0.173419
Trial 4
_mm_load_ps with aligned memory: 0.181997
_mm_loadu_ps with aligned memory: 0.172688
_mm_loadu_ps with unaligned memory: 0.179133
Trial 5
_mm_load_ps with aligned memory: 0.180817
_mm_loadu_ps with aligned memory: 0.172168
_mm_loadu_ps with unaligned memory: 0.181852
答案 0 :(得分:14)
你的结果中有很多噪音。我在运行Debian 7的Xeon E3-1230 V2 @ 3.30GHz上重新运行了这个,在200000000阵列上进行了12次运行(丢弃了第一次计算虚拟内存噪声),其中i
进行了10次迭代。基准函数,显式noinline
用于您提供的函数,以及您的三个基准测试中的每一个都是独立运行的:https://gist.github.com/creichen/7690369
这与gcc 4.7.2。
noinline
确保第一个基准未被优化。
确切的电话是
./a.out 200000000 10 12 $n
$n
从0
到2
。
结果如下:
load_ps align
min: 0.040655
median: 0.040656
max: 0.040658
loadu_ps已对齐
min: 0.040653
median: 0.040655
max: 0.040657
loadu_ps未对齐
min: 0.042349
median: 0.042351
max: 0.042352
正如您所看到的,这些是一些非常严格的界限,表明loadu_ps
在未对齐访问时速度较慢(减速约5%),但在对齐访问时则不然。显然,在特定的机器上,loadu_ps对对齐的内存访问不会有任何损失。
查看程序集时,load_ps
和loadu_ps
版本之间的唯一区别是后者包含movups
指令,重新排序其他一些指令以进行补偿,并略微使用不同的注册名称。后者可能完全无关紧要,前者可以在微码翻译过程中得到优化。
现在,很难说(不是英特尔工程师可以访问更详细的信息)是否/如何优化movups
指令,但考虑到CPU芯片只需简单地使用如果加载地址中的低位为零,则对齐数据路径,否则为未对齐数据路径,这对我来说似乎是合理的。
我在Core i7笔记本电脑上尝试了同样的效果,并得到了非常相似的结果。
总之,我会说是的,你确实为未对齐的内存访问付出了代价,但它足够小,以至于它可以被其他效果淹没。在您报告的运行中,似乎有足够的噪音来允许假设它对您来说也较慢(请注意,您应该忽略第一次运行,因为您的第一次试用将为预热页表和缓存付出代价。)
答案 1 :(得分:6)
这里有两个问题:给定相同的对齐地址,未对齐的加载是否比对齐的加载慢?并且具有未对齐地址的加载比具有对齐地址的加载慢吗?
与使用新地址的对齐加载相比,较旧的Intel CPU(在这种情况下“较旧”仅在几年前)对使用带对齐地址的未对齐加载指令确实有轻微的性能损失。较新的CPU往往没有这个问题。
较旧和较新的Intel CPU都会因未加载地址的加载而受到性能损失,特别是在超过缓存行时。
由于细节因处理器型号而异,因此您需要逐一检查每个细节。
有时可以掩盖性能问题。用于测量的简单指令序列可能无法揭示未对齐加载指令使得加载 - 存储单元比对齐加载指令更加繁忙,因此如果在前一种情况下尝试某些附加操作但不会导致性能下降在后者。
答案 2 :(得分:5)
这取决于架构,最近几代人已经显着改进了。另一方面,在较旧的Core2架构上:
$ gcc -O3 -fno-inline foo2.c -o a; ./a 1000000
Array Size: 3.815 MB
Trial 1
_mm_load_ps with aligned memory: 0.003983
_mm_loadu_ps with aligned memory: 0.003889
_mm_loadu_ps with unaligned memory: 0.008085
Trial 2
_mm_load_ps with aligned memory: 0.002553
_mm_loadu_ps with aligned memory: 0.002567
_mm_loadu_ps with unaligned memory: 0.006444
Trial 3
_mm_load_ps with aligned memory: 0.002557
_mm_loadu_ps with aligned memory: 0.002552
_mm_loadu_ps with unaligned memory: 0.006430
Trial 4
_mm_load_ps with aligned memory: 0.002563
_mm_loadu_ps with aligned memory: 0.002568
_mm_loadu_ps with unaligned memory: 0.006436
Trial 5
_mm_load_ps with aligned memory: 0.002543
_mm_loadu_ps with aligned memory: 0.002565
_mm_loadu_ps with unaligned memory: 0.006400
答案 3 :(得分:3)
见Intel® 64 and IA-32 Architectures Optimization Reference Manual中的“§2.4.5.1对准危险的有效处理”:
缓存和内存子系统处理每个工作负载中相当大比例的指令。不同的地址对齐方案将对内存和缓存操作产生不同的性能影响。例如,L1的1周期吞吐量(参见表2-25)通常适用于来自L1高速缓存的自然对齐的负载。但使用未对齐的加载指令(例如MOVUPS,MOVUPD,MOVDQU等)从L1访问数据将会遇到不同程度的延迟,具体取决于特定的微体系结构和对齐方案。
我无法在这里复制表格,它基本上表明对齐和未对齐的L1负载是1个周期;拆分缓存行边界约为4.5个周期。