参照とポインターの実行速度
-
22-08-2019 - |
質問
最近、マネージ言語がネイティブ言語 (特に C# と C++) より遅い (または速い) かどうかに関する議論を読みました。議論に貢献したある人は、マネージ言語の JIT コンパイラーは、ポインターを使用する言語では不可能な参照に関する最適化を行うことができるだろうと述べました。
私が知りたいのは、ポインターではなく参照ではどのような最適化が可能ですか?
ここでの議論はメモリ使用量ではなく実行速度に関するものであることに注意してください。
解決
いくつかの利点があります JITコンパイル ウィキペディアで言及されています:
JIT コードは通常、インタープリターよりもはるかに優れたパフォーマンスを提供します。さらに、多くの最適化は実行時にのみ実行できるため、静的コンパイルよりもパフォーマンスが向上する場合があります。
- コンパイルは、アプリケーションが実行される対象の CPU およびオペレーティング システム モデルに合わせて最適化できます。たとえば、JIT は、CPU が SSE2 CPU 命令をサポートしていることを検出した場合に、SSE2 CPU 命令を選択できます。静的コンパイラでは、場合によってはインライン アセンブリを使用して、2 つのバージョンのコードを作成する必要があります。
- システムは、プログラムが置かれている環境で実際にどのように実行されているかに関する統計を収集でき、最適なパフォーマンスを得るために再配置および再コンパイルできます。ただし、一部の静的コンパイラはプロファイル情報を入力として受け取ることもできます。
- システムはグローバルなコード最適化を実行できます (例:動的リンクの利点を損なうことなく、また静的コンパイラやリンカーに固有のオーバーヘッドを発生させることなく、ライブラリ関数のインライン化を行うことができます。具体的には、グローバル インライン置換を行う場合、静的コンパイラは実行時チェックを挿入し、オブジェクトの実際のクラスがインライン メソッドをオーバーライドする場合に仮想呼び出しが確実に発生するようにする必要があります。
- これは静的にコンパイルされたガベージ コレクション言語で可能ですが、バイトコード システムではメモリをより簡単に再配置してキャッシュの利用率を高めることができます。
ポインターの代わりに参照を使用することに直接関連するものは思いつきません。
他のヒント
C++ では、最適化の側面に関連する参照には 2 つの利点があります。
参照は定数です (存続期間全体にわたって同じ変数を参照します)
このため、コンパイラーは、どの名前が同じ基になる変数を参照しているかを推測しやすくなり、最適化の機会が生まれます。コンパイラが参照をより適切に処理するという保証はありませんが、可能性はあります...
参照は何かを参照していると想定されます (null 参照はありません)。
「何も参照しない」参照 (NULL ポインターに相当) を作成することもできますが、これは NULL ポインターを作成するほど簡単ではありません。このため、参照の NULL チェックを省略できます。
ただし、これらの利点はいずれもマネージ言語に直接引き継がれないため、議論のトピックの文脈ではそれが関連性があるとは思えません。
一般話すには、参照は、異なる場所から同じオブジェクトを参照することを可能にする。
A「ポインタは、」参照を実装するためのメカニズムの名前です。 C ++、パスカル、Cは...ポインタを持っている、C ++は、「リファレンス」と呼ばれる(少し他のユースケース付き)別のメカニズムを提供していますが、基本的にこれらは、一般的な参照の概念のすべての実装されます。
だから、参照が定義によってポインタよりも速い/遅いない理由はありません。
本当の違いは、JITまたは古典的な「フロントアップ」のコンパイラを使用している:JITは、データがアップフロントコンパイラでは使用できません考慮に入れることができます。それはコンセプト「参照」の実装とは何の関係もありません。
他の回答が正しいです。
私はそれがプログラムカウンタが実際に(このような文字列を比較するような)関数呼び出しが含まれていないタイトなループでのように、多くの時間を費やしているコードでない限り、任意の最適化が差異のやじるをすることはありませんことを追加します。
管理フレームワークにおけるオブジェクト参照は、C ++に渡さ基準とは非常に異なっています。彼らは特別なものを理解するには、次のシナリオがガベージコレクションオブジェクト参照せずに、マシンレベルで、どのように処理されるかを想像:メソッド「Fooのは、」様々なコレクションに保存され、コードの異なる部分に渡された文字列を返します。何もこれ以上の文字列を必要としないならば、それはそれを格納する際に使用されるすべてのメモリを再利用することが可能なはずであるが、それは文字列を使用する最後の1になり、コードのどの部分は不明です。
非GCシステムでは、すべてのコレクションは、文字列の独自のコピーを持っている必要があります、または他の文字列内の文字を保持している共有オブジェクトへのポインタを含むものを保持する必要があるのいずれか。後者の状況では、共有オブジェクトは、それへの最後のポインタが解消されます際に何らかの形で知っておく必要があります。そこさまざまな方法がこれを処理することが可能ですが、それらのすべての重要な共通の特徴は、共有オブジェクトがそれらへのポインタをコピーまたは破壊されたときに通知する必要があるということです。このような通知は作業を必要とします。
一方GCシステムでは、プログラムは、レジスタまたはスタックフレームの部分が根付いたオブジェクト参照を保持するための任意の時点で使用されると言ってメタデータが施されています。ガベージコレクションサイクルが発生すると、ガベージコレクタは、このデータを解析する必要がすべてのライブオブジェクトを識別し、保存、および他のすべてをNUKEます。他のすべての回で、しかし、プロセッサは、関係するオブジェクトのいずれかを通知することなく、任意のパターンや、それが好きな順序で参照を、置き換えるシャッフル、または破壊し、コピーすることができます。マルチプロセッサシステムにおけるポインタ利用通知を使用する場合、異なるスレッドが同じオブジェクトへの参照をコピーしたり、破壊する可能性がある場合、同期コードが必要な通知スレッドセーフにするために必要とされるであろうことに留意されたいです。対照的に、GCシステムでは、各プロセッサが他のプロセッサとその動作を同期化することなく、いつでも参照変数を変更してもよい。