我一直在探索这个问题,提交了一份 GCC 错误报告,发现这是一个与 MinGW64 相关的问题。请参阅GCC 错误#49001。显然,GCC 不支持 Windows 上的 32 字节堆栈对齐。这有效地防止了使用 256 位 AVX 指令。
我研究了几种方法来处理这个问题。最简单和最直接的解决方案是用未对齐的替代方案 VMOVUPS 等替换对齐的内存访问 VMOVAPS/PD/DQA。所以我昨晚学习了 Python(顺便说一句,这是一个非常好的工具)并使用以下脚本完成了这项工作GCC 生成的输入汇编文件:
import re
import fileinput
import sys
# fix aligned stack access
# replace aligned vmov* by unaligned vmov* with 32-byte aligned operands
# see Intel's AVX programming guide, page 39
vmova = re.compile(r"\s*?vmov(\w+).*?((\(%r.*?%ymm)|(%ymm.*?\(%r))")
aligndict = {"aps" : "ups", "apd" : "upd", "dqa" : "dqu"};
for line in fileinput.FileInput(sys.argv[1:],inplace=1):
m = vmova.match(line)
if m and m.group(1) in aligndict:
s = m.group(1)
print line.replace("vmov"+s, "vmov"+aligndict[s]),
else:
print line,
这种方法非常安全且万无一失。尽管我在极少数情况下观察到了性能损失。当堆栈未对齐时,内存访问会跨越高速缓存行边界。幸运的是,代码的执行速度在大多数情况下与对齐访问一样快。我的建议:关键循环中的内联函数!
我还尝试使用另一个 Python 脚本修复每个函数序言中的堆栈分配,尝试始终将其对齐在 32 字节边界。这似乎适用于某些代码,但不适用于其他代码。我必须依靠 GCC 的善意,它会分配对齐的局部变量(相对于堆栈指针),它通常会这样做。情况并非总是如此,尤其是当由于需要在函数调用之前保存所有 ymm 寄存器而导致严重的寄存器溢出时。(所有 ymm 寄存器都是被调用者保存的)。如果有兴趣,我可以发布脚本。
最好的解决方案是修复 GCC MinGW64 构建。不幸的是,我不知道它的内部工作原理,上周才开始使用它。