gcc-4.x での SSE/SSE2 浮動小数点演算

前回は,SSE/SSE2 の SIMD 演算機能を使ってみました. が,SIMD 機能を利用するにはプログラムを書き直さなくてはいけません. というわけで,今回はお手軽な方法を.

-mfpmath オプション

gcc-4.x のコンパイルオプションで,double 型や float 型の浮動小数点演算の方法を指定できます.

-mfpmath=387
浮動小数点演算を x87 命令に変換します.
-mfpmath=sse
浮動小数点演算を SSE/SSE2 命令に変換します.-msse や -msse2 オプションも必要です.
-mfpmath=sse,387
浮動小数点演算を x87 と SSE/SSE2 命令の両方に変換します.

例えば

double a,b,c;
c = a + b;

というソースコードを

$ gcc -mfpmath=sse -msse2 foo.c

とオプションでコンパイルしてやると,SSE2 命令を使った実行プログラムが生成されます. つまり,ソースコードの修正無しにリコンパイルするだけでパフォーマンスの向上が期待できることになります.

ベンチマーク

例によってベンチマーク.

前回のプログラムをそのまま使いまわします.

まずは x87 演算.

$ gcc -msse2 -O2 -funroll-loops benchmark.c
$ ./a.out 
SSE double:	2.280000	3998000.000000
norm double:	5.830000	3998000.000000
SSE float:	1.050000	3998000.000000
norm float:	4.700000	3998000.000000

SSE/SSE2 命令を使うようオプションを付けてみます. norm double, norm float の演算が SSE/SSE2 命令でコンパイルされます.

$ gcc -msse2 -O2 -funroll-loops -mfpmath=sse benchmark.c  
$ ./a.out 
SSE double:	2.320000	3998000.000000
norm double:	4.000000	3998000.000000
SSE float:	1.010000	3998000.000000
norm float:	3.790000	3998000.000000
  • 単精度で 1.2 倍
  • 倍精度で 1.5 倍

の速度向上が見られました. が,SSE double, SSE float の値には遠く及びません.

でも,まぁ,コンパイルし直すだけでこれだけ速度が上がれば御の字とも言えるでしょう.

SIMD ではない

というのも,実はこのオプションで生成されるのはスカラ演算命令で,SIMD 命令ではありません. つまり,SSE レジスタを使って x87 のような浮動小数点演算を行う命令を生成するだけで,データ構造を束ねて SIMD 演算してくれるような機能はありません. が,その代わりに例の「16 で割り切れるアドレス」の制限は生じません.

double 型の精度

double 型の場合,メモリ上では 64 bit の浮動小数点数ですが,x87 レジスタ上では 80 bit の浮動小数点で演算されます. このため,演算結果をメモリに格納する時点で有効桁数が落ちることになります. 最適化によってメモリに格納するタイミングが変わったりすると計算結果が変わってしまう,という現象は有名ですね.

が,SSE レジスタ上での倍精度浮動小数点数は 64 bit 長です. つまり,倍精度演算では厳密には x87 演算と結果が異なるかもしれません. でも,倍精度演算の精度は「最適化オプションでも結果が変わる」というレベルなので,大抵のプログラムは SSE2 命令を使ったからといって直ちに誤動作することは無いと思います.


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2008-09-19 (金) 03:52:42 (5722d)