この問題を調査し、GCC バグ レポートを提出したところ、これが MinGW64 関連の問題であることがわかりました。GCC Bug#49001を参照してください。どうやら、GCC は Windows で 32 バイトのスタック アラインメントをサポートしていないようです。これにより、256 ビット AVX 命令の使用が効果的に防止されます。
この問題に対処する方法をいくつか調べました。最も単純で鈍い解決策は、アラインされたメモリ アクセス VMOVAPS/PD/DQA をアラインされていない代替 VMOVUPS などに置き換えることです。 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 が (スタック ポインターに関して) アラインされたローカル変数を割り当てるという GCC の善意に頼る必要がありますが、これは通常行われます。これは常に当てはまるとは限りません。特に、関数呼び出しの前にすべての ymm レジスタを保存する必要があるため、重大なレジスタ スピルが発生する場合はそうです。(すべての ymm レジスターは callee-save です)。興味があればスクリプトを投稿できます。
最善の解決策は、GCC MinGW64 ビルドを修正することです。残念ながら、先週使い始めたばかりで、内部の仕組みについては知りません。