プログラム全体の最適化 (別名リンク時のコード生成) をサポートする優れた最適化コンパイラは、内部関数*の呼び出し規則を気にしません。カスタム呼び出し規約の発明や関数のインライン化など、その状況で最速/最高の呼び出し規約を使用します。
呼び出し規約が重要になるのは、パブリック API の一部を形成する関数の場合だけです。その場合、__fastcallおそらく悪い選択です。Windows ツールチェーンで広くサポートされている、__cdeclまたは などのより標準的な呼び出し規則を使用します。これは、標準化されていないため、ベンダーごとに異なる方法で実装されているため、相互運用性にとって特に悪い選択です。これは、別のツールチェーンでコンパイルされたアプリケーションで DLL を使用しようとするとすぐに悪夢になります。別の言語ではなおさらです。__stdcall__fastcall
もちろん、__fastcall規約が必要であると文書化されている VCL API を使用している場合は除きます。たとえば、ドキュメントには、VCL クラスのメンバー関数が__fastcall規則を使用することが記載されているため、すべてのオーバーライドで同じ呼び出し規則を使用する必要があります。
または、可変引数をサポートするなど、呼び出し元のクリーンアップが必要な場合。次に、が必要__cdeclです。
内部関数 (つまり、パブリック API の一部ではない関数) に特定の呼び出し規則を使用したい場合は、コンパイラ スイッチを使用してグローバルに指定することをお勧めします。これにより、プロトタイプが明示的にオーバーライドしないすべての関数に使用される呼び出し規約が指定されます。これにはいくつかの利点があります。1 つには、多数の呼び出し規約定型文でコードが乱雑になるのを回避できます。次に、後で簡単に変更を加えることができます (たとえば、プロファイリングによって、最初に選択した呼び出し規約がオプティマイザーで解決できないボトルネックであることが判明した場合)。
逸話的に__stdcallは、呼び出し元ではなく__cdecl呼び出し先がスタックを調整するという事実によって可能になったバイナリサイズの削減により、__fastcallよりも速い__stdcall. この記事では技術的な詳細については触れていませんが、問題は基本的に、32 ビット x86 で使用できるレジスタの数が非常に限られていることです。スタックではなくレジスターで値を渡すと、一般的にパフォーマンスが向上しますが、関数が大きくてレジスターが不足している場合は悲観的になる可能性があります。速度のペナルティを引き起こします)、さらにコードを膨らませます (これはキャッシュのペナルティを引き起こし、間接的に速度のペナルティを引き起こします)。また、値が既にスタック上にあるが、関数呼び出しを行うためにレジスタに移動する必要がある場合もペシミゼーションであり、両方の場所での最適化の可能性を妨げます。
64 ビット x86 アーキテクチャをターゲットにし始めると、これはすべて無関係になることに注意してください。呼び出し規約は、ベンダーに関係なく、すべての Windows アプリケーションで最終的に標準化されています。x64 の呼び出し規則は に多少似て__fastcallいますが、利用可能なレジスタの数が多いため、はるかにうまく機能します。オプティマイザーは、パラメーターを渡すためにレジスターを解放するために x86-32 の場合ほど多くのゆがみを経る必要はありません。
*ここで「内部」関数と言う場合、特定のアクセス修飾子を指すのではなく、単一のコンパイル ランド内にある関数や、外部コードによって決して呼び出されない関数を指すことに注意してください。