に比べ
windows上のC++で。
相対的なタイミング(100倍以上ずれてはいけません;-)
関数呼び出しは、メモリ内のフレームポインタをスタックにシフトし、その上に新しいフレームを追加するだけです。関数パラメーターは使用するためにローカルレジスターにシフトされ、スタックポインターは関数の実行のためにスタックの新しい最上位に進められます。
時間と比較して
関数呼び出し〜単純なメモリアクセス
関数呼び出し<ディスクアクセス
関数呼び出し<別のコンピューターのメモリアクセス
関数呼び出し<別のコンピューターでのディスクアクセス
単純なメモリアクセスと比較すると、わずかに多く、実際には無視できます。
リストされている他のすべてのものと比較して-桁違いに少ない。
これは、あらゆるOSのほぼすべての言語に当てはまるはずです。
一般に、関数呼び出しは、実際には呼び出しを実行するために複数のメモリアクセスを実行する必要があるため、メモリアクセスよりもわずかに遅くなります。たとえば、x86で__stdcallを使用するほとんどの関数呼び出しには、スタックの複数のプッシュとポップが必要です。ただし、L2キャッシュにないページへのメモリアクセスの場合、宛先とスタックがすべてCPUのメモリキャッシュにあると、関数呼び出しははるかに高速になります。
他のすべての場合、関数呼び出しは(多くの)桁数が速くなります。
関係する要因がたくさんあるので答えるのは難しいです。
まず第一に、「シンプルなメモリアクセス」はシンプルではありません。最新のクロック速度では、CPUはチップの一方の側からもう一方の側に数値を取得するよりも速く2つの数値を加算できるため(光の速度-それは良い考えではなく、法律です)
では、関数はCPUメモリキャッシュ内で呼び出されていますか?比較しているメモリアクセスもありますか?
次に、関数呼び出しでCPU命令パイプラインがクリアされます。これは、非決定論的な方法で速度に影響します。
呼び出し先が行うことではなく、呼び出し自体のオーバーヘッドを意味すると仮定すると、「単純な」メモリアクセス以外のすべてよりもはるかに高速です。
おそらくメモリアクセスよりも低速ですが、コンパイラはインライン化を実行できるため、関数呼び出しのオーバーヘッドがゼロになる場合があることに注意してください。そうでない場合でも、少なくとも一部のアーキテクチャでは、すでに命令キャッシュにあるコードへの呼び出しが、メイン(キャッシュされていない)メモリにアクセスするよりも高速である可能性があります。それは、呼び出しを行う前にスタックするためにいくつのレジスターをこぼす必要があるか、そしてそのようなことによって異なります。コンパイラと呼び出し規約のドキュメントを参照してください。ただし、発行されたコードを逆アセンブルするよりも早く理解できる可能性は低いです。
また、「単純な」メモリアクセスではない場合があることにも注意してください。OSがページをディスクから取り込む必要がある場合は、長い間待つ必要があります。現在ディスクにページアウトされているコードにジャンプした場合も同じです。
根本的な質問が「行われる関数呼び出しの総数を最小限に抑えるためにコードを最適化する必要があるのはいつですか?」である場合、答えは「非常に近い」です。
このリンクはグーグルでたくさん出てきます。将来の参考のために、関数呼び出しのコストについてC#で短いプログラムを実行しました。答えは、「インラインのコストの約6倍」です。詳細は以下のとおりです。//下部の出力を参照してください。更新:リンゴとリンゴをよりよく比較するために、Class1.Methodを変更して 'void'を返すようにしました。publicvoidMethod1(){// return 0; }
それでも、インラインは2倍高速です。インライン(平均):610ミリ秒。関数呼び出し(平均):1380ミリ秒。したがって、更新された答えは「約2回」です。
システムを使用する; System.Collections.Genericを使用する; System.Linqを使用する; System.Textを使用します。 System.Diagnosticsを使用します。
名前空間FunctionCallCost {クラスプログラム{静的voidMain(string [] args){Debug.WriteLine( "stop1"); int iMax = 100000000; // 100M DateTime funcCall1 = DateTime.Now; Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iMax; i++)
{
//gives about 5.94 seconds to do a billion loops,
// or 0.594 for 100M, about 6 times faster than
//the method call.
}
sw.Stop();
long iE = sw.ElapsedMilliseconds;
Debug.WriteLine("elapsed time of main function (ms) is: " + iE.ToString());
Debug.WriteLine("stop2");
Class1 myClass1 = new Class1();
Stopwatch sw2 = Stopwatch.StartNew();
int dummyI;
for (int ie = 0; ie < iMax; ie++)
{
dummyI = myClass1.Method1();
}
sw2.Stop();
long iE2 = sw2.ElapsedMilliseconds;
Debug.WriteLine("elapsed time of helper class function (ms) is: " + iE2.ToString());
Debug.WriteLine("Hi3");
}
}
//ここではシステムを使用してクラス1; System.Collections.Genericを使用する; System.Linqを使用する; System.Textを使用します。
名前空間FunctionCallCost {クラスClass1 {
public Class1()
{
}
public int Method1 ()
{
return 0;
}
}
}
//出力:メイン関数のstop1経過時間(ms)は:595ヘルパークラス関数の経過時間(ms)は:3780
メイン関数のstop1経過時間(ms)は:592ヘルパークラス関数の経過時間(ms)は:4042
メイン関数のstop1経過時間(ms)は:626ヘルパークラス関数のstop2経過時間(ms)は:3755
C++には仮想呼び出し(かなり高価で、約x10)があり、WindowsではVSがインライン呼び出しを期待できることを忘れないでください(バイナリに呼び出しが残っていないため、定義上0のコスト)
実際に関数を呼び出すが、完全には実行しない場合のコストは?または実際に関数を実行するコスト?関数呼び出しを設定するだけでは、コストのかかる操作ではありません(PCを更新しますか?)。しかし、明らかに、関数が完全に実行されるコストは、関数が何をしているかによって異なります。
関数呼び出しは、実際にはスタックへのパラメーターのコピー(複数のメモリアクセス)、レジスタの保存、実際のコードの実行、そして最後に結果のコピーとレジスタの復元です(レジスタの保存/復元はシステムによって異なります)。
だから..比較的話す:
その関数の機能によって異なりますが、メモリ内のオブジェクトを使用してロジックを実行している場合は、リストの2番目になります。ディスク/ネットワークアクセスが含まれている場合は、リストのさらに下にあります。
関数呼び出しよりも高速なのはメモリアクセスのみです。
ただし、インライン最適化を使用するコンパイラーの場合(GCCコンパイラーの場合、レベル3の最適化(-O3)を使用するときにアクティブ化されるだけでなく)、呼び出しを回避できます。
関数呼び出しのコストはアーキテクチャによって異なります。 x86はかなり低速ですが(関数の引数ごとに数クロックと1クロック程度)、64ビットは、ほとんどの関数の引数がスタックではなくレジスタで渡されるため、はるかに低速です。
関数呼び出しは通常、2、3のメモリコピー(多くの場合、レジスタへのコピーであるため、それほど時間はかからないはずです)とその後のジャンプ操作を伴います。これはメモリアクセスよりも低速ですが、他のハードウェアとの通信が必要なため、上記の他のどの操作よりも高速です。通常、OSと言語の組み合わせについても同じことが言えます。
関数がコンパイル時にインライン化される場合、関数のコストは0と同等になります。
0もちろん、関数呼び出しがない場合に得られるもの、つまり、自分でインライン化したものです。
もちろん、私がそのように書くとき、これは過度に明白に聞こえます。