に比べ
- シンプルなメモリアクセス
- ディスクアクセス
- 別のコンピュータ (同じネットワーク上) でのメモリ アクセス
- 別のコンピュータ (同じネットワーク上) でのディスク アクセス
Windows 上の C++ で。
に比べ
Windows 上の C++ で。
相対的なタイミング (100 倍以上ずれてはいけません ;-)
関数呼び出しは、メモリ内のフレーム ポインターをスタックにシフトし、その上に新しいフレームを追加するだけです。関数パラメータは使用のためにローカル レジスタにシフトされ、スタック ポインタは関数の実行のためにスタックの新しいトップに進められます。
時間に比べて
関数呼び出し ~ 単純なメモリ アクセス
関数呼び出し < ディスク アクセス
関数呼び出し < 別のコンピュータのメモリ アクセス 関数呼び出し < 別のコンピュータ
のディスク アクセス
単純なメモリアクセスと比較して、わずかに多く、実際には無視できます。
リストされている他のすべてのものと比較して、桁違いに少ない.
これは、どの OS のほぼすべての言語にも当てはまります。
一般に、関数呼び出しは実際には呼び出しを実行するために複数のメモリ アクセスを行う必要があるため、メモリ アクセスよりもわずかに遅くなります。たとえば、x86 で __stdcall を使用するほとんどの関数呼び出しでは、スタックのプッシュとポップを複数回行う必要があります。ただし、メモリ アクセスが L2 キャッシュにさえないページに対するものである場合、宛先とスタックがすべて CPU のメモリ キャッシュにあれば、関数呼び出しははるかに高速になります。
それ以外の場合は、関数呼び出しの方がはるかに高速です。
いろいろな要素が絡むので答えにくいです。
まず、「単純なメモリアクセス」は単純ではありません。現代のクロック速度では、CPU はチップの一方の側から他方の側に数値を取得するよりも速く 2 つの数値を加算できるため (光の速度 -- これは単なる良い考えではなく、法則です)
では、関数は CPU メモリ キャッシュ内で呼び出されているのでしょうか。あなたもそれを比較しているメモリアクセスですか?
次に、関数呼び出しで CPU 命令パイプラインをクリアします。これは非決定的な方法で速度に影響します。
呼び出し先が何をするかではなく、呼び出し自体のオーバーヘッドを意味すると仮定すると、「単純な」メモリアクセス以外のすべてよりもはるかに高速です。
おそらくメモリ アクセスよりも遅くなりますが、コンパイラはインライン化できるため、関数呼び出しのオーバーヘッドがゼロになる場合があることに注意してください。そうでない場合でも、少なくとも一部のアーキテクチャでは、既に命令キャッシュにあるコードへの呼び出しが、メイン (キャッシュされていない) メモリにアクセスするよりも高速になる可能性があります。それは、呼び出しを行う前にスタックにスピルする必要があるレジスタの数、およびそのようなものに依存します。出力されたコードを逆アセンブルするよりも早く解決できる可能性は低いですが、コンパイラと呼び出し規約のドキュメントを参照してください。
また、「単純な」メモリアクセスはそうでない場合があることに注意してください-OSがディスクからページを取得する必要がある場合、長い待ち時間が発生します。現在ディスク上でページアウトされているコードにジャンプする場合も同様です。
根底にある質問が「実行される関数呼び出しの総数を最小限に抑えるためにコードを最適化する必要があるのはいつですか?」である場合、答えは「まったくないに近い」です。
このリンクは Google でよく出てきます。今後の参考のために、関数呼び出しのコストについて C# で短いプログラムを実行したところ、「インラインの約 6 倍のコスト」という答えが返ってきました。以下に詳細を示します。下部の //Output を参照してください。更新: リンゴとリンゴをよりよく比較するために、Class1.Method を変更して「void」を返すようにしました。それでも
、インラインは 2 倍高速です: インライン (平均): 610 ミリ秒; 関数呼び出し (平均): 1380 ミリ秒。したがって、更新された答えは「約2回」です。
システムを使用して; System.Collections.Generic の使用; System.Linq を使用します。System.Text を使用します。System.Diagnostics を使用します。
名前空間 FunctionCallCost { クラス プログラム { static void Main(string[] args) { Debug.WriteLine("stop1"); int iMax = 100000000; //100M DateTime funcCall1 = DateTime.Now; ストップウォッチ 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");
}
}
// System を使用したクラス 1; System.Collections.Generic の使用; System.Linq を使用します。System.Text を使用します。
名前空間 FunctionCallCost { クラス Class1 {
public Class1()
{
}
public int Method1 ()
{
return 0;
}
}
}
// 出力: メイン関数の stop1 経過時間 (ms): 595 ヘルパー クラス関数の stop2 経過時間 (ms): 3780
メイン関数の stop1 経過時間 (ms): 592 ヘルパー クラス関数の stop2 経過時間 (ms): 4042
メイン関数の stop1 経過時間 (ms): 626 ヘルパー クラス関数の stop2 経過時間 (ms): 3755
C++ には仮想呼び出し (かなり高価で、約 x10) があり、Windows では VS が呼び出しをインライン化することを期待できることを忘れないでください (バイナリに呼び出しが残っていないため、定義によりコストはゼロです)。
関数を実際に呼び出すが、完全に実行しない場合のコストは? または実際に機能を実行するコストは?関数呼び出しを設定するだけでは、コストのかかる操作ではありません (PC を更新しますか?)。しかし明らかに、関数を完全に実行するコストは、関数が何をしているかによって異なります。
関数呼び出しは、実際にはスタックへのパラメーターのコピー(複数のメモリアクセス)、レジスタの保存、実際のコードの実行、そして最後に結果のコピーとレジスタの復元です(レジスタの保存/復元はシステムによって異なります)。
だから..比較的話す:
メモリーアクセスのみが関数呼び出しよりも高速です。
ただし、インライン最適化を使用するコンパイラー(GCCコンパイラーの場合、レベル3の最適化(-O3)を使用するときにアクティブ化されるだけでなく)を使用すると、呼び出しを回避できます。
関数呼び出しのコストは、アーキテクチャによって異なります。x86はかなり低速ですが(関数の引数ごとに数クロックと1クロック程度)、64ビットは、ほとんどの関数の引数がスタックではなくレジスタで渡されるため、はるかに低速です。
その関数が何をするかによって異なりますが、メモリ内のオブジェクトでロジックを実行している場合、リストの 2 番目になります。ディスク/ネットワーク アクセスが含まれている場合は、リストのさらに下に移動します。
通常、関数呼び出しには、2、3 のメモリ コピー (多くの場合、レジスタへのコピーであるため、それほど時間はかからないはずです) と、その後のジャンプ操作のみが含まれます。これはメモリ アクセスよりも遅くなりますが、他のハードウェアとの通信が必要なため、上記の他の操作よりも高速です。通常、どの OS と言語の組み合わせでも同じことが当てはまります。
関数がコンパイル時にインライン化されている場合、関数のコストは 0 と同等になります。
0 もちろん、関数呼び出しを持たないことで得られるもの、つまり自分でインライン化したものです。
もちろん、私がそのように書くと、これは過度に明白に聞こえます。