関数呼び出しによるオーバーヘッドの影響 その1

前置き

 プログラム初心者を脱してくれば、もう自作関数は使っていると思います。 その自作関数は再利用にはもってこいの自作関数ですが、演算速度を気にしてくると問題が出てくるかも!? それがオーバーヘッドと言うキーワードにつながります。 C++だとそれを回避するのに「インライン関数」という言葉も出てきます。 はてさて、実際どれくらい問題なのだろうとふと思ったのがこのページ作成のきっかけです。

比較内容

 単純に演算速度にどう影響するかを見ます。 一定回数にどれくらいの時間がかかるか、を見ても良いのです。 が、ここでは一定時間内に何回の演算が行えるかで比較を行います。
 main関数で直に演算させる、関数越しで演算させる、その関数をインライン関数指定してみるの3通りでの比較を行います。 比較時間はプロセッサ時間にして1000msec、n数を400とし平均値を見ていきたいと思います。

環境など

 Visual C++ 2010持ってるのですが、何となく2008でやっています。 ソリューション構成はデフォルトのままで、DebugとReleaseの2通りで比較します。 Expressなので生成される実行ファイルは32ビットです。 Standardだと64ビットの実行ファイルも作れる…と思う。

ソースコード

#include <stdio.h>
#include <time.h>
 
#define REPEAT 400
#define TIME   1000
 
unsigned long sum;
 
void add(void){
    ++sum;
}
 
inline void add2(void){
    ++sum;
}
 
int main(void){
    FILE *fp = ::fopen("record.csv","w");
    clock_t start;
 
    fprintf(fp,"default,add,add2\n");
 
    for(int i=0; i<REPEAT; i++){
        sum = 0;
        start = clock();
        while(clock()-start < TIME){
            ++sum;
        }
        fprintf(fp,"%d,",sum);
 
        sum = 0;
        start = clock();
        while(clock()-start < TIME){
            add();
        }
        fprintf(fp,"%d,",sum);
 
        sum = 0;
        start = clock();
        while(clock()-start < TIME){
            add2();
        }
        fprintf(fp,"%d,",sum);
 
        fprintf(fp,"\n");
    }
 
    fclose(fp);
 
    return 0;
}
 割合単純にやっているつもりです。 改良の余地があれとすればファイル書き込み回数を減らすところとか、関数をプロトタイプ宣言させるところとか? ごめんなさい、そこまでまじめにやってません。 見たとおり計測時間はwhileでループさせています。 インクリメントするfor文より早いんだとか?
 計算回数はunsigned longで記録させています。 intだと一応定義上、上限(65535)超えるので。ただVisual C++だとどーもintがlongと同一のような… ま、さておき上限は大丈夫なことは確認しています。

結果・考察?

 結果を以下に載せます。 デフォルトが演算をmain関数で直接行ったもの、addが関数越しの結果、add2がインライン関数指定した関数越しの結果です。 縦軸が演算回数を表しています。
 Debugですと、最適化が行われない状況がよく分かります。 どうやらインライン関数も展開されていないような結果です、多分。 びっくりはReleaseがほぼ横一線。恐るべし、Visual C++の最適化。 Visual C++なら下手にinline指定せずに任せっきりの方が良いんじゃないか!?とか思ったりもしました。
 defaultの結果はDebugの方が平均的に早かったという結果が出ています。これについてはこの下で解説します。
総合結果一覧
 前置きとしてReleaseとDebugはほぼ同時に実行されたと言うことを書いておきます。
 以下の結果はdefaultの400回における演算回数の変化についてプロットしたものです。 使用しているCPUは最大4スレッド同時に実行出来る性能を有していますが、4スレッド故に他の常駐プログラムから何らかの影響を受け得ます。 実際このグラフを見ますと120回付近までは確かにReleaseの方が演算性能で劣っています。 しかしそれ以降はほぼ2つがシンクロしているような結果が得られています。 このことから基本的にはDebugとReleaseではそれほど実効性能に差はないと言えます。 おそらく120回までは他のプログラムに影響されて演算性能が低下してしまったのでしょう。 そして120回の差が先ほどの1つ目のグラフでの差に表れたと言えます。
 いまいち分からないのはReleaseが150回付近でオーバーシュートしているかのような、突出した値が出ているところです。 2スレッドが稼働している状況でi5のブースト機能が働いたとは到底思えないので。
結果その2
 どう結論づけて良いか分からないのですが。
 とりあえずまず言えることではDebugモードでは特に関数呼び出しの影響は大きいと言えます。 今回の結果からはダブルスコア以上の違いが出ています。 ただしそれはそれで結局Releaseでビルドすれば関数呼び出しの影響はほぼありませんでした。
This site is created by ez-HTML