自分でチューニングをするというのは逆にプログラムを遅くしてしまう場合があるということを検証してみたかったので、コンパイラで最適化しずらそうなコードと最適化しやすそうなコードで最適化前と後で速度の変化はどのようになるかを実験してみました。
実験環境は以下です。
OS: Windows 7
CPU: core i7 860
メモリ: 8GB
次のようなコードでコンパイラによる最適化の前と後でfunc1とfunc2それぞれの速度を比較しました。
このコードのままであれば、func2の方が余計なポインタ演算が減るので高速になるはずです。下が実験の結果で、最適化を行わないとfunc2の方が高速になるという予想通りの結果になりました。
最適化なしの結果
では次に最適化オプションをつけて速度を比較してみます。下が実験結果で、最適化前とうってかわってfunc1の方が圧倒的に速くなりました。
Visual C++の実行速度の最大化 (/O2)オプションで最適化したときの結果
実験結果から、コンパイラの挙動をよく知らない上でチューニングを行うことは逆にコードを複雑にしてしまい、コンパイラの自動的な最適化を妨げる原因になり得て、プログラムの動作速度低下を招く危険をはらんでいるということがわかりました。
どこでどのようなチューニングを行えば速度が向上するのかは当然コンパイラに依存するところなので、そこまで考慮するのはとても高度な知識が必要になるかと思います。私にはそこまで考えられる技量がないので、試しに速くなるかやってみて速くなれば採用するという風にしています。
ただ、コンパイラを変えたら遅くなるということも当然あり得ることなので、シンプルなコードとチューニングしたコードは両方とも残しておくようにして、いつでも切り替えられるようにしておくのがベストなのかなと思います。
実験環境は以下です。
OS: Windows 7
CPU: core i7 860
メモリ: 8GB
次のようなコードでコンパイラによる最適化の前と後でfunc1とfunc2それぞれの速度を比較しました。
#include <stdlib.h>
void func1(int *data, int len)
void func1(int *data, int len)
{
int i, j;
for(i=0; i<len; i++){
data[i]=0;
for(j=0; j<i; j++){
data[i]+=j;
}
}
}
void func2(int *data, int len)
{
int i, j;
int *ptr;
for(i=0; i<len; i++){
ptr = &data[i];
*ptr = 0;
for(j=0; j<i; j++){
*ptr += j;
}
}
}
void main(){
int len = 100000;
int *data = (int*)malloc(len*sizeof(int));
func1(data, len);
func2(data, len);
free(data);
free(data);
}
このコードのままであれば、func2の方が余計なポインタ演算が減るので高速になるはずです。下が実験の結果で、最適化を行わないとfunc2の方が高速になるという予想通りの結果になりました。
最適化なしの結果
func1: 18.88 secs
func2: 15.65 secs
では次に最適化オプションをつけて速度を比較してみます。下が実験結果で、最適化前とうってかわってfunc1の方が圧倒的に速くなりました。
Visual C++の実行速度の最大化 (/O2)オプションで最適化したときの結果
func1: 3.13 secs
func2: 9.26 secs
func2: 9.26 secs
実験結果から、コンパイラの挙動をよく知らない上でチューニングを行うことは逆にコードを複雑にしてしまい、コンパイラの自動的な最適化を妨げる原因になり得て、プログラムの動作速度低下を招く危険をはらんでいるということがわかりました。
どこでどのようなチューニングを行えば速度が向上するのかは当然コンパイラに依存するところなので、そこまで考慮するのはとても高度な知識が必要になるかと思います。私にはそこまで考えられる技量がないので、試しに速くなるかやってみて速くなれば採用するという風にしています。
ただ、コンパイラを変えたら遅くなるということも当然あり得ることなので、シンプルなコードとチューニングしたコードは両方とも残しておくようにして、いつでも切り替えられるようにしておくのがベストなのかなと思います。