gcc-4.x での SSE/SSE2 浮動小数点演算 †前回は,SSE/SSE2 の SIMD 演算機能を使ってみました. が,SIMD 機能を利用するにはプログラムを書き直さなくてはいけません. というわけで,今回はお手軽な方法を. -mfpmath オプション †gcc-4.x のコンパイルオプションで,double 型や float 型の浮動小数点演算の方法を指定できます.
例えば 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
の速度向上が見られました. が,SSE double, SSE float の値には遠く及びません. でも,まぁ,コンパイルし直すだけでこれだけ速度が上がれば御の字とも言えるでしょう. SIMD ではない †というのも,実はこのオプションで生成されるのはスカラ演算命令で,SIMD 命令ではありません. つまり,SSE レジスタを使って x87 のような浮動小数点演算を行う命令を生成するだけで,データ構造を束ねて SIMD 演算してくれるような機能はありません. が,その代わりに例の「16 で割り切れるアドレス」の制限は生じません. double 型の精度 †double 型の場合,メモリ上では 64 bit の浮動小数点数ですが,x87 レジスタ上では 80 bit の浮動小数点で演算されます. このため,演算結果をメモリに格納する時点で有効桁数が落ちることになります. 最適化によってメモリに格納するタイミングが変わったりすると計算結果が変わってしまう,という現象は有名ですね. が,SSE レジスタ上での倍精度浮動小数点数は 64 bit 長です. つまり,倍精度演算では厳密には x87 演算と結果が異なるかもしれません. でも,倍精度演算の精度は「最適化オプションでも結果が変わる」というレベルなので,大抵のプログラムは SSE2 命令を使ったからといって直ちに誤動作することは無いと思います. |