16

C99restrictキーワードを正しく取得した場合、ポインターをそれで修飾すると、ポインターが参照するデータが、エイリアシングによってコンパイラーの背後で変更されないことが約束されます。

対照的に、私がconst修飾子を理解する方法は、特定のオブジェクトがコードを書いている人間の背後で変更されないというコンパイラーによって強制されたドキュメントです。コンパイラーは副作用としてヒントを得るかもしれませんが、プログラマーとして私は本当に気にしません。

restrict同様の方法で、関数プロトタイプの修飾子を、ユーザーが呼び出し中に排他的アクセスを保証する(「エイリアシングを回避する」、またはおそらくより強力なもの)という要件と見なすのが適切でしょうか?「ドキュメンテーション」として使用する必要がありますか?

restrictまた、(そうであるように)ポインターが指すデータではなく、ポインターを修飾するという事実に理解すべきことがありconstますか?

編集:私は当初、それがスレッデッドコードに影響を与える可能性があると信じていrestrictましたが、これは間違っているように思われるので、読者を混乱させないように、質問からスレッドへの参照を削除します。

4

6 に答える 6

22

Chris Dodd がキーワードの正しい説明をしています。特定のプラットフォームでは、パフォーマンス上の理由から非常に重要になる場合があります。これは、そのポインターを介してデータをレジスターにロードすると、再度ロードする必要がないことをコンパイラーに知らせるためです。この保証がない場合、コンパイラは、他の可能性のあるエイリアシング ポインターが書き込まれるたびに、ポインターを介してデータを再読み込みする必要があります。これにより、load-hit-storeと呼ばれる深刻なパイプライン ストールが発生する可能性があります。

constとは異なる概念であり、 を意味restrictする場合ではありません。つまり、その関数のスコープ内でそのポインターを介して書き込みを行わないということです。ポインターはまだエイリアス化されている可能性があります。たとえば、次のように考えてください。constrestrictconstconst

int foo( const int *a, int * b )
{
   *b *= 2;
   return *a + *b; // induces LHS: *a must be read back immediately
                   // after write has cleared the store queue
}

この関数に直接書き込むことはできませんがa、次のように foo を呼び出すことは完全に合法です。

int x = 3;
foo( &x, &x );  // returns 12

restrictは別の保証です:a != bすべての呼び出しでfoo().

私はこのキーワードとそのパフォーマンスへの影響について長々と書いてきましたrestrict。Mike Acton も同様です。特定のインオーダー PowerPC について話しますが、ロード ヒット ストアの問題は x86 でも存在しますが、x86 のアウト オブ オーダー実行により、そのストールをプロファイルで分離するのが難しくなります。

強調しておきたいのは、パフォーマンスをまったく気にするのであれば、これは難解な、または時期尚早の最適化ではありません。restrict正しく使用すると、非常に大幅なスピードアップにつながる可能性があります。

于 2009-10-01T22:49:01.883 に答える
5

あなたの理解はおおむね正しいです。restrict修飾子は、そのように修飾されたポインターによってアクセスされるデータは、その正確なポインターによってのみアクセスされることを単に示していますこれは、書き込みだけでなく読み取りにも適用されます。

コンパイラは並行スレッドを気にせず、別の方法でコードを生成するつもりはありませんでした。また、必要に応じて独自のデータを上書きすることもできます。ただし、どのポインター操作がどのグローバル メモリを変更する可能性があるかを知る必要があります。

Restrictまた、指定された関数がエイリアス化されていないパラメーターを想定して実装されているという人間への API 警告も伴います。

コンパイラに関する限り、ユーザーによるロックは必要ありません。修飾子がない場合、コンパイラが生成するはずだったコードによって、上書きされるはずだったデータを正しく読み取ることを確認したいだけです。追加することで、その懸念から解放されます。restrictrestrict

最後に、コンパイラは、より高い最適化レベルで、データ型に基づいて考えられるエイリアシングを既に分析している可能性が高いことに注意してください。これrestrictは、主に同じ型のデータへの複数のポインターを持つ関数にとって重要です。このテーマから教訓を得て、意図的なエイリアシングはunion.

実際の動作を確認できますrestrict:

void move(int *a, int *b) {     void move(int *__restrict a, int *__restrict b) {
    a[0] = b[0];                    a[0] = b[0];
    a[1] = b[0];                    a[1] = b[0];
}                               }
    movl    (%edx), %eax            movl    (%edx), %edx
    movl    %eax, (%ecx)            movl    %edx, (%eax)
    movl    (%edx), %eax            movl    %edx, 4(%eax)
    movl    %eax, 4(%ecx)

右側の列ではrestrict、コンパイラはb[0]メモリから再読み取りする必要がありませんでした。それを読み取っb[0]て register に保持し%edx、次にレジスタを 2 回メモリに格納することができました。a左の列では、店舗が変更された可能性があるかどうかはわかりませんでしたb

于 2009-10-01T22:37:26.103 に答える