私はC++スタイルのキャストが初めてで、C++スタイルのキャストを使用するとアプリケーションのパフォーマンスが損なわれるのではないかと心配していますリアルタイムクリティカルデッドライン 私のinterrupt-service-routineで。
キャストによっては例外がスローされることもあると聞きました!
コードをより「堅牢」にするため、C++スタイルのキャストを使用したいと思います。ただし、パフォーマンスヒットがある場合の場合、おそらくC++スタイルのキャストを使用せず、代わりにCスタイルのキャストを使用するコードのテストにより多くの時間を費やします。
C++スタイルのキャストとCスタイルのキャストのパフォーマンスを比較するために、厳密なテスト/プロファイリングを行った人はいますか?
結果はどうでしたか?
どのような結論を導き出しましたか?
C++スタイルのキャストを概念的にCスタイルのキャストに置き換えることができる場合、オーバーヘッドは発生しません。 dynamic_cast
の場合のように、Cに相当するものがない場合は、何らかの方法でコストを支払う必要があります。
例として、次のコード:
int x;
float f = 123.456;
x = (int) f;
x = static_cast<int>(f);
vC++で両方のキャストに対して同一のコードを生成します-コードは次のとおりです。
00401041 fld dword ptr [ebp-8]
00401044 call __ftol (0040110c)
00401049 mov dword ptr [ebp-4],eax
スローできる唯一のC++キャストは、参照にキャストするときにdynamic_cast
です。これを回避するには、ポインターにキャストします。ポインターはキャストに失敗すると0を返します。
実行時に追加費用が発生するのはdynamic_cast
、とにかくCスタイルのキャストでは直接再現できない機能があります。だから問題はありません。
これを安心させる最も簡単な方法は、コンパイラにアセンブラ出力を生成するよう指示し、生成するコードを調べることです。たとえば、正しく実装されたコンパイラでは、reinterpret_cast
は「盲目的に進んでデータがこのタイプのふりをする」ことを意味するだけなので、完全に消えます。
パフォーマンスが低下するのはなぜですか? Cキャストと同じ機能をexactly実行します。唯一の違いは、コンパイル時により多くのエラーをキャッチし、ソースコード内で検索しやすいことです。
static_cast<float>(3)
は_(float)3
_とまったく同じであり、まったく同じコードを生成します。
_float f = 42.0f
_を指定すると、reinterpret_cast<int*>(&f)
は_(int*)&f
_とまったく同じであり、まったく同じコードを生成します。
等々。異なる唯一のキャストは_dynamic_cast
_であり、はい、例外をスローできます。しかし、それは、Cスタイルのキャストではできないことを行うためです。そのため、その機能が必要でない限り、_dynamic_cast
_を使用しないでください。
通常、コンパイラライターはインテリジェントであると想定するのが安全です。標準に従って同じセマンティクスを持つ2つの異なる式が与えられた場合、通常、それらがコンパイラーで同じように実装されると想定するのは安全です。
エラー:2番目の例は、もちろんdynamic_castではなく、reinterpret_castである必要があります。今それを修正しました。
わかりやすくするために、C++標準の説明を次に示します。
§5.4.5:
によって実行される変換
- _
const_cast
_(5.2.11)- _
static_cast
_(5.2.9)- _
static_cast
_に続く_const_cast
_- _
reinterpret_cast
_(5.2.10)、または- _
reinterpret_cast
_の後に_const_cast
_が続きます。明示的な型変換のキャスト表記を使用して実行できます。同じセマンティックの制限と動作が適用されます。変換が上記の複数の方法で解釈できる場合、リストの最初に表示される解釈が使用されます。その解釈から生じるキャストが不正な形式であってもです。
したがって、anythingの場合、CスタイルキャストはC++キャストの観点から実装されているため、Cスタイルキャストはslower。 (もちろん、そうではありません。コンパイラがいずれの場合も同じコードを生成するためですが、C++スタイルのキャストが遅いよりももっともらしいです。)
4つのC++スタイルのキャストがあります。
const_cast
_static_cast
_reinterpret_cast
_dynamic_cast
_すでに述べたように、最初の3つはコンパイル時の操作です。これらを使用しても実行時のペナルティはありません。これらは、ある方法で宣言されたデータに別の方法でアクセスする必要があるというコンパイラーへのメッセージです。 「これは_int*
_であると言いましたが、sizeof(int) char
sを指す_char*
_であるかのようにアクセスさせてください」または「このデータは読み取り専用だと言いました変更しない関数に渡す必要がありますが、パラメーターをconst参照として使用しません。」
間違った型にキャストしてデータを破棄することによるデータの破損は別として(常にCスタイルのキャストで可能)、これらのキャストの最も一般的な実行時の問題は、実際にconst
がキャストできないと宣言されたデータです非定数に。 const
と宣言されたものを非constにキャストし、それを変更することは未定義です。 未定義は、クラッシュすることさえ保証されていないことを意味します 。
_dynamic_cast
_は実行時構造であり、実行時コストが必要です。
これらのキャストの価値は、キャストしようとしているものを明確に言い、視覚的に突き出ており、脳死のツールで検索できることです。 Cスタイルのキャストを使用するよりも、それらを使用することをお勧めします。
dynamic_cast
を使用する場合、実行時に複数のチェックが行われ、バカなこと( GCCメーリングリスト で詳細を説明します)を防ぐため、1つのdynamic_cast
のコストはクラスの数に依存します影響を受けるクラス、影響を受けるクラスなど。
キャストが安全であると確信している場合でも、 reinterpret_cast
を使用できます。
「実行時に余分なコストがかかるのはdynamic_cast
"、コンパイラ固有の違いがある可能性があることに注意してください。
現在のコンパイラに対して、CスタイルとC++スタイルstatic_cast
キャスト。
心配な場合は、ホットスポットの分解を確認してください。それ以外の場合は、動的なキャストが不要な場合は避けてください。 (RTTIをオフにすると、dynamic_cast
とにかく。)
標準的な真実はアセンブリですので、両方を試して、異なるロジックが得られるかどうかを確認してください。
まったく同じアセンブリを取得する場合、違いはありません-ありえません。古いCキャストに固執する必要があるのは、純粋なCルーチンとライブラリ内だけです。ここでは、型キャストのためだけにC++依存関係を導入しても意味がありません。
知っておくべきことの1つは、まともなサイズのコードでキャストが至る所で発生することです。私のキャリア全体では、ロジックの一部で「すべてのキャスト」を検索したことはありません。「A」のような特定のタイプへのキャストを検索する傾向があり、「(A)」での検索は通常、 「static_cast <A>」のようなもの。型の検証などに新しいキャストを使用します。これは、決して簡単に実行できない検索を行うためです。