コンパイラが効率よくループを並列化できるようにするには、左辺値が記憶領域の特定の領域を示している必要があります。別名とは、記憶領域の決まった位置を示していない左辺値のことです。オブジェクトへの 2 個のポインタが別名であるかどうかを判断することは困難です。これを判断するにはプログラム全体を解析することが必要であるため、非常に時間がかかります。次の関数 vsq() を考えてみましょう。
extern "C" void vsq(int n, double *a, double *b) { int i; for (i=0; i<n; i++) { b[i] = a[i] * a[i]; } } |
ポインタ a および b が異なるオブジェクトをアクセスすることをコンパイラが知っている場合には、ループ内の異なる繰り返しを並列に実行することができます。しかし、ポインタ a および b でアクセスされるオブジェクトが重なりあっていれば、ループを安全に並列実行できなくなります。
コンパイル時に関数 vsq() を単純に解析するだけでは、a および b によるオブジェクトのアクセスが重なりあっているかどうかを知ることはできません。この情報を得るには、プログラム全体を解析することが必要になります。次のコマンド行オプションを使用することにより、ポインタ値の関数パラメータを制限付きポインタとして扱うよう指定できます。-xrestrict[ =func1,…,funcn] 関数リストを指定する場合は、指定した関数内のポインタパラメータは制限付きとして扱われます。それ以外の場合は、ソースファイル全体の中にあるすべてのポインタパラメータが制限つきとして扱われます (推奨しない)。たとえば、-xrestrict=vsq を使用すると、関数 vsq() についての例では、ポインタ a および b が修飾されます。
ポインタ引数を制限付きとして宣言する場合は、ポインタが個別のオブジェクトを指定すると明示することになります。コンパイラは、a および b が個別の記憶領域を指していると想定します。この別名情報によって、コンパイラはループの並列化を実行することができます。
-xrestrict を正しく使用することはとても重要です。区別できないオブジェクトを指しているポインタを制限付きポインタにしてしまうと、ループを正しく並列化できなくなり、不定な動作をすることになります。たとえば、関数 vsq() のポインタ a および b が重なりあっているオブジェクトを指している場合には、b[i] と a[i+1] などが同じオブジェクトである可能性があります。このとき a および b が制限付きポインタとして宣言されていなければ、ループは順次実行されます。a および b が間違って制限付きであると宣言されていれば、コンパイラはループを並列実行するようになりますが、この場合 b[i+1] の結果は b[i] を計算したあとでなければ得られないので、安全に実行することはできません。