How to optimize loop with floating point counter?

by syoyo

RSL(RenderMan Shading Language) では整数型が無いので(prman 拡張だと整数型はあるのかもしれません)、
たとえばループを記述するときのカウンタには float 型を使います.

こんな感じ.


float i;
for (i = 0; i < 1000; i += 1) {
    ...
}

float 型でも、2^24 までの整数は IEEE754 フォーマットで exact に表現できるので、
まあこの記述自体には問題ないのですが…

float 型のカウンタを使う場合、ループ最適化が効かない

で、問題は、このような float 変数をカウンタにしたループはなぜか LLVM や gcc では最適化されないのです.

たとえば、gcc(4.4) で試してみます
(gcc 4.3 からは mfpr と gmp の導入により、cos(2.4) というような式に対する定数展開の最適化がマシン非依存かつ正確に行われるようになったため, より float 演算に対する最適化が出来ていると推測する)

入力のプログラムは以下.
ループ内で何もしないので、ループが削除されることを期待します.


int
func() {
    float i;
    for (i = 0.0f; i < 1000.0f; i += 1.0f) {
    }
}


$ gcc-4.4 -O3 -S floop.c

残念ながら 1000 回ループするアセンブリにコンパイルされています.
(ループカウンタは整数型に変換されているのに. 不思議です)

LLVM で試してみます.


$ clang -emit-llvm-bc floop.c
$ opt -std-compile-opts floop.bc | llvm-dis

残念ながらこちらもループは DCE(Dead Code Elimination) されませんでした.
-licm (Loop-invariant Code Motion) とかループ展開とか、
それらしきループ最適化オプションを試しましたが結果は変わりませんでした.

float の演算の場合は、たとえば a + b + c という式を a + (b + c) と計算してしまうと丸めや精度の問題で計算結果が変わるので、
演算順序を変えたり演算をまとめるなどのアグレッシブな最適化は難しいことは分かるのですが、
(LLVM はそのためあまり IR レベルでは fp 最適化は行われないようです [1])
しかし、不変なループ処理くらいは最適化できてほしいところ.

さすがに? icc だと最適化してくれます.
(icc はバージョン 10.0 とちょっと古いですが)


$ icc -O3 -S floop.c

ret 命令だけになっています(期待する結果).

というわけで、どなたか、
「このオプションを指定すればループ最適化される!」とか
「最適化されないのはこれが原因だ」とか、
分かる方がいましたらご教示をお願いします.

参考

[1] Loop Invariant Code Motion and LLVM
http://stlab.adobe.com/wiki/index.php/Performance/Analysis/Example2

Advertisements