gcc 4.x の 3DNow! 命令サポートについての考察 †日記/2008-09-03/gcc-4.x での SIMD 命令サポートでは,3DNow! 命令セットで簡単なベンチマークプログラムを書いて,x87 命令に対しパフォーマンスアップが確認できました. 今回は,もう少し突っ込んで見ていきます. v2sf 型の代入 †-m3dnow オプションを付けると,実は __builtin__ia32_… 関数を明示的に使わないところでも拡張命令が使用されてしまいます. v2sf 型変数への代入部分です. 例えば v2sf a; a = (v2sf){ 1.0, 2.0 }; というコードがあると,ここで movq 命令が使用されます(movq は本当は MMX 命令なのですが,gcc ではこれも 3DNow! 命令セットの一部とみなしてしまうようです). したがって float f = 3.0; … a = (v2sf){ f + 1.0, 2.0 }; は,x87 命令と 3DNow! 命令の「混ぜたら危険」ルールに反することになります. 本来は gcc が良きに計らって欲しいところですが,残念ながらケアしてくれないようです. ループ展開の効果 †ベンチマークの最後で -funroll-loops オプションを付けた場合の実行結果を示しました. 3DNow! 命令ではパフォーマンスが上昇したのに対し,x87 命令ではパフォーマンスはほとんど差がありませんでした. なぜでしょう? CPU の中を覗いてきたわけではないので断言はできないのですが,ある程度想像はつきます. x87 では,中の構造はスタックマシン,要するに逆ポーランド電卓です. スタックに値を詰め込み,スタックトップに対して演算を指示することにより計算を行います. 複数の演算を続けて行う場合,前の演算結果に依存することになります. このため,パイプラインによる並列処理の恩恵を受けにくくなっているのではないかと想像できます. 一方,3DNow! では普通のレジスタマシンです. 8 本の MMX レジスタを自在に使って計算を行います. 実際に逆アセンブルしてみたところ,ループ展開を行うと,複数の MMX レジスタを使って計算を行うコードが生成されていました. プログラムの並列性が上がったために複数の演算が同時進行することになり,パフォーマンスが上がったのではないかと思われます. |