人間は制限修飾子から何を生み出すことができるでしょうか?
-
19-09-2019 - |
質問
C99を手に入れたら restrict
キーワードを正しく使用すると、ポインタを修飾することで、参照するデータがエイリアシングによってコンパイラの背後で変更されないことが約束されます。
対照的に、私の理解方法は、 const
qualifier は、コードを作成する人間の背後で特定のオブジェクトが変更されないことを示す、コンパイラによって強制されるドキュメントです。コンパイラは副作用としてヒントを取得するかもしれませんが、プログラマとしてはあまり気にしません。
同様に、次のように考えるのが適切でしょうか。 restrict
ユーザーが呼び出し中に排他的アクセス (「エイリアスを避ける」、またはおそらくより強力なもの) を保証する要件として、関数プロトタイプの修飾子を使用しますか?「文書」として使用する必要がありますか?
また、次の点で理解できることはありますか? restrict
ポインタが指すデータではなく、ポインタを修飾します (次のように) const
する)?
編集:私はもともとそう信じていました restrict
スレッド化されたコードに影響を与える可能性がありますが、これは間違っていると思われるため、読者の混乱を避けるために質問からスレッドへの参照を削除します。
解決
restrictキーワードについて持っているための最良の「直感」は、ポインタの寿命のために、そのポインタを介してアクセスメモリはONLYそのポインタを介してアクセスしていないであろう、ということ(コンパイラにプログラマによって)その保証ということです別のポインタまたは参照またはグローバルアドレスを経由して。ポインタがスコープ外になるまで、そのポインタとメモリの両方のその性質として、ポインタには、2つを結ぶように、その重要ます。
他のヒント
Chris Dodd がキーワードを正確に説明しています。特定のプラットフォームでは、これはパフォーマンス上の理由から非常に重要です。これにより、コンパイラーは、そのポインターを介してデータをレジスターに一度ロードすると、再度ロードする必要がないことがわかります。この保証がない場合、コンパイラは、エイリアシングの可能性のある他のポインタが書き込まれるたびに、ポインタを介してデータを再ロードする必要があり、これにより、「エラー」と呼ばれる重大なパイプライン ストールが発生する可能性があります。 ロードヒットストア.
const
そして restrict
は異なる概念であり、そうではありません const
暗示する restrict
. 。全て const
そのポインタを介して書き込みはしないということです その機能の範囲内で. 。あ const
ポインタはまだエイリアス化されている可能性があります。たとえば、次のことを考えてみましょう。
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
キーワードとそのパフォーマンスへの影響の詳細, 、 そして マイク・アクトンもそうだ. 。特定のインオーダー PowerPC について話していますが、ロード ヒット ストアの問題は x86 にも存在しますが、x86 のアウトオブオーダー実行により、プロファイルでストールを切り分けることが難しくなります。
そして、強調しておきます。これは ない パフォーマンスを少しでも重視する場合は、難解な最適化や時期尚早な最適化が必要になります。 restrict
正しく使用すると、大幅な高速化につながる可能性があります。
あなたが知っていることのほとんどは間違っている!
constが何かは、コンパイラの背中の後ろに変更されないことの のない保証を行います。それがないすべてがその場所への書き込みからのあなたはの停止です。コンパイラは、それが一定だと仮定することはできませんので、何か他のものはまだ、しかしその場所に書き込むことができるかもしれません。
他の人が言ったように、、限定修飾子はエイリアシングについてです。実際には、Cの標準化の最初のラウンド中に、「NOALIAS」キーワードの提案がありました。残念ながら、提案はかなり下手に書かれた - 。彼は「NOALIASが行かなければならない旨の何かがこの交渉に開かれていないと述べた手紙を書いたときには、デニス・リッチーは、そのプロセスの間に巻き込まれた唯一無二の時間を促しました。 「
制限が標準で含まれていたこと、それは再度お試しに時間が来たとき「NOALIAS」はC.の一部にはなりませんでした、提案が十分に良く書かれた、言うまでもなく - とNOALIASはおそらくあったであろうにもかかわらず、そのため、より意味のある名前を、その名前は、私は誰もそれを使用しようと考え疑うほど汚染された。
いずれの場合も、制限の主な目的は、この項目のエイリアスがないことをコンパイラに伝えることです。この理由の一つは、物事が一時レジスタに格納できるようにすることです。例えば、のようなものを考えてみます:
void f(int *a, int *b, int *c) {
for (int i=0; i<*a; i++)
*b += c[i];
}
コンパイラは、実際にそれがループの別の反復を実行するかどうかを決定するための時間が来るときので、それだけでお互いにレジスタにそれらの値を比較し、レジスタにロード* Aのレジスタに私を置くことを望んでいる、と。残念ながら、それはそれを行うことはできません - この機能を使用し、誰かが完全に正気だった、と== bの、それがループ内* bに書き込むたびにでそれを呼び出した場合、新しい値も* Aの値であること - それは、それは完全に狂っていたと呼ばれる誰の念のためを、ループのすべての反復で*メモリからAを読み取ることがあります。そう* bの(またはその逆)を変更することはありません*への書き込み、それはaとbは常に明瞭になることを想定したコードを生成することができます制限するようコンパイラーに指示し使用します。
あなたの理解は大部分が正しいです。 restrict
修飾子は、単純にそう修飾ポインタによってアクセスされるデータは、ののみのその正確なポインタによってアクセスされていることを述べています。これは、書き込みとして井戸として読み込むために適用されます。
コンパイラは、それがどんな違ったコードを生成するつもりではなかった、同時実行スレッドを気にせず、好きなようにあなたはあなた自身のデータを壊して。しかし、それはどのようなグローバルメモリを変更することが何ポインタの操作を知っている必要がありません。
Restrict
はまた、それに与えられた関数は、非エイリアスパラメータの仮定を用いて実施されるヒトへのAPIの警告を運ぶ。
ユーザによるロック限りコンパイラが懸念している必要はありません。それだけで必ずそれが正しく、のコンパイラがケースに何restrict
修飾子が存在しない、の生成になっていた。は、コードによって、上書きされるために、のはずだったデータを読み込むしたいと考えています。 restrict
を追加すると、その懸念からそれを解放します。
最後に、コンパイラはすでに高い最適化レベルで、データの種類に基づいて可能エイリアシングを分析する可能性があることに注意してください、そう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]
を読み、レジスタ%edx
でそれを維持して、ちょうどメモリに二度レジスタを保存することができました。 a
へのストアがb
を変更された可能性がある場合は、左側の列で、それは知りませんでした。
標準に精通誰かが、おそらくより良い答えを与えることができるが、私はそれに打撃を与えるでしょう。
「データは、コンパイラの背中の後ろに変更されることはありません」より「揮発性」の反対のように私には聞こえます。
「CONST」データは、プログラマの前に変更されないことを意味します。 (int const *pi
で、名前pi
がconstのではなく、*pi
あるので、私が書く「シニフィアン」)つまり、彼女は「CONST」としてマークされたシニフィアンを介してデータを変更することはできません。データは別の記号表現を介して変更可能であるかもしれない(非constデータはすべての後、CONSTデータとしての機能に渡すことができる)。
「制限」は、ポインタを修飾することが重要です。ポインタはCで別名データへの唯一の方法であるので、彼らはあなたが2人の異なる名前を介してデータのいくつかの部分にアクセスすることができる唯一の方法です。すべて一つのアクセスパスへのデータアクセスを制限する程度である「制限」ます。
これはの非常にの狭い領域からの一例かもしれませんが、アルテラのNios IIプラットフォームでは、FPGA内カスタマイズできるソフトコア・マイクロコントローラです。その後、そのマイクロのCソース・コード内で、あなたはむしろソフトウェアよりも、カスタムハードウェアを使用して内部ループをスピードアップするためにC・ツー・ハードウェアツールを使用することができます。
は、そこに、(C99の__restrict__
と同じである)restrict
キーワードの使用は、C2Hツールが適切に平行の代わりに、順次にポインタ操作のハードウェアアクセラレーションを最適化することを可能にします。少なくとも、この場合には、restrict
は、単にをされていないの人間の消費のためのもの。最初の行は、
restrict
で、上も Sunのページをご覧ください。
コンパイラが著しく速い実行可能ファイルを生成することを可能にするCプログラムで適切
restrict
修飾子を使用します。
誰もがC2Hにもっと読んで興味を持っていた場合は、このPDF にC2H結果の最適化について説明します。 __restrict__
上のセクションには、20ページにあります。