C ユーザーズガイド

_Restrict

コンパイラが効果的にループの並列実行を行うには、ある種の左辺値 (lvalue) が異なる記憶領域を指しているかどうかを判別する必要があります。別名とは、記憶領域が異なっていない左辺値のことです。オブジェクトを示す 2 つのポインタが別名かどうかを判別するのは、プログラム全体の解析を必要とすることがあるため、難しく時間のかかる作業です。

次の関数 vsq() を例にとって考えてみます。


void vsq(int n, double * a, double * b)
{
	int i;
	for (i=0; i<n; i++) b[i] = a[i] * a[i];
}

コンパイラは、ポインタ ab が異なるオブジェクトにアクセスすることがわかっていれば 、ループの異なる繰り返しの実行を並列化することができます。ポインタ ab でアクセスされるオブジェクトが重なりあっている場合には、コンパイラが並列でループを実行するのは安全ではありません。コンパイル時には、コンパイラは関数 vsq() を単純に解析しただけでは ab でアクセスするオブジェクトが重なり合っているかどうかがわかりません。この情報を得るにはプログラム全体の解析が必要です。

コンパイラがポインタ別名解析を実行できるよう、制限付きポインタを使用して異なるオ ブジェクトを示すポインタを指定します。制限付きポインタをサポートするために、キーワード _Restrict が Sun ANSI C コンパイラによって拡張として認識されます。次に vsq() の関数引数を制限付きポインタとして宣言する例を示します。


void vsq(int n, double * _Restrict a, double * _Restrict b)

ポインタ ab が制限付きポインタとして宣言されると、コンパイラは ab が示す記憶域の領域が異なるものだと認識します。この別名情報により、コンパイラはループを並列化することができます。

_Restrict キーワードは volatile と同じような型修飾子で、ポインタ型だけを修飾します。_Restrict は、コンパイルモード -Xa (デフォルト) と -Xt でのみキーワードとして認識されます。これらの 2 つのモードでは、コンパイラはマクロ __RESTRICT を定義して、ユーザーが制限付きポインタを用いて移植可能なコードを書けるようにします。

コンパイラはマクロ __RESTRICT を定義して、ユーザーが制限付きポインタを用いて移植可能なコードを書けるようにします。たとえば、次のコードは Sun ANSI C コンパイラのすべてのコンパイルモードで使用でき、制限付きポインタをサポートしていない他のコンパイラでも使用できます。


#ifdef __RESTRICT
#define restrict _Restrict
#else
#define restrict
#endif

void vsq(int n, double * restrict a, double * restrict b)
{
      int i;
      for (i=0; i<n; i++) b[i] = a[i] * a[i];
}

制限付きポインタが ANSI C 標準の一部に加わると、"restrict" がキーワードになるものと思われます。ユーザーが vsq() でのように制限付きポインタを用いてコードを書く場合の一例を以下に示します。


#define restrict _Restrict

こうしておけば、ANSI C で restrict がキーワードになっても、変更を最小限にとどめることができます。Sun ANSI C コンパイラではキーワードとして _Restrict を使用しています。これが処理系の名前空間に入っていて、ユーザーの名前空間にある識別子と衝突しないためです。

ユーザーがソースコードを変更したくない場合があります。その場合、コマンド行オプション -xrestrict を用いて、ポインタ値のある関数引数を制限付きポインタとして扱うよう指定することができます。詳細は、-xrestrict=fを参照してください。

関数リストを指定すると、指定した関数内のポインタ引数は制限付きポインタとして扱われます。関数リストを指定しない場合は、C ファイル全体のすべてのポインタ引数が制限付きとして扱われます。たとえば、-xrestrict=vsq は、関数 vsq() の例で示したポインタ ab をキーワード _Restrict で修飾します。

_Restrict は正しく使用することが重要です。制限付きポインタとして修飾されているポインタが、同一オブジェクトを指していると、ループは誤って並列化されることがあり、未定義の動作が発生します。たとえば、関数 vsq() のポインタ ab が重なり合ったオブジェクトを指し、b[i]a[i+1] は同じオブジェクトであるとします。ab が制限付きポインタとして宣言されなければ、ループは順次に実行されます。ab が誤って制限付きポインタとして宣言されると、コンパイラはループの実行を並列化することがあります。b[i+1] の計算は、b[i] の計算後に実行される必要が生じるため、これは安全とは言えません。