プリフェッチのサポート

データ・プリフェッチとは、アプリケーションによってデータが必要になる前に、メモリーよりも高速なキャッシュにデータをロードしておくことです。データ・プリフェッチの動作はアーキテクチャーによって異なります。

ほとんどの場合、プリフェッチ命令を発行するとパフォーマンスが向上します。ただし、プリフェッチ命令を発行することでアプリケーションのパフォーマンスが低下する場合もあります。このような場合、プリフェッチを調整します。コンパイラー・オプションでプリフェッチをオンまたはオフにすると、他の最適化はそのままでプリフェッチによるパフォーマンス低下の原因を追求するのに役立ちます。コンパイラー・オプションを使用したデータのプリフェッチに関する詳細は、「オプションを使用したプリフェッチ」を参照してください。

プリフェッチ命令を発行するには 2 つの方法があります。1 つはコンパイラー宣言子、もう 1 つはコンパイラー組み込み関数を使用する方法です。

prefetch プラグマと noprefetch プラグマ

prefetch 宣言子および noprefetch 宣言子は、インテル(R) Itanium(R) プロセッサーでのみサポートされています。これらの宣言子は、データ・プリフェッチがメモリー参照に生成されるか、されないかを指定します。これは、コンパイラーが使用するヒューリスティックに影響を与えます。これらのプラグマの一般的な構文は次のとおりです

構文

#pragma noprefetch

#pragma prefetch

#pragma prefetch a,b

ループの前に prefetch A を置いて、ループ内で式 A(j) を使用する場合、コンパイラーはループ内の A(j+d) のプリフェッチを挿入します。d はデータをプリフェッチするための残りの反復回数で、コンパイラーによって決定されます。この宣言子は、-O3 (Linux) または /O3 (Windows) オプションがオンの場合にサポートされます。-O1-O2 (Linux) または /O1/O2 (Windows) が指定された場合もサポートされます。-O2 および /O2 は、デフォルトの最適化レベルである点に注意してください。

#pragma noprefetch b

#pragma prefetch a

for(i=0; i<m; i++)

{

  a[i]=b[i]+1;

}

次の例は、IA-64 アーキテクチャーでのみ有効で、prefetchnoprefetch、および memref_control プラグマを一緒に使用する方法を説明します。

#define SIZE 10000

int prefetch(int *a, int *b)

{

  int i, sum = 0;

  #pragma memref_control a:l2

  #pragma noprefetch a

  #pragma prefetch b

  for (i = 0; i<SIZE; i++)

    sum += a[i] * b[i];

  return sum;

}

#include <stdio.h>

int main()

{

  int i, arr1[SIZE], arr2[SIZE];

  for (i = 0; i<SIZE; i++) {

    arr1[i] = i;

    arr2[i] = i;

  }

  printf("Demonstrating the use of prefetch, noprefetch,\n"

      "and memref_control pragma together.\n");

  prefetch(arr1, arr2);

  return 0;

}

組み込み関数

コンパイラー組み込み関数を挿入する前に、サポートされる他のコンパイラー・オプションとプラグマを試してみてください。コンパイラー組み込み関数は、コンパイラー・オプションやコンパイラー・プラグマよりも、移植性および柔軟性が低くなります。

プラグマは、コンパイラーの最適化を有効にしますが、組み込み関数は最適化を実行します。プラグマを使用したプログラムは移植性が高いため、コンパイラーは別のプロセッサーに適用することができますが、組み込み関数を使用したプログラムを別のプロセッサーに適用するには、書き換えや移植が必要です。これは、組み込み関数がアセンブリー言語のプログラミングに近いためです。

一部のプリフェッチ組み込み関数を次に示します。

組み込み関数

説明

__lfetch

lfetch.lfhint 命令を生成します。

__lfetch_fault

lfetch.fault.lfhint 命令を生成します。

__lfetch_excl

lfetch.excl.lfhint 命令を生成します。

__lfetch_fault_excl

lfetch.fault.excl.lfhint 命令を生成します。

__mm_prefetch

1 キャッシュライン分のデータを、アドレス a からプロセッサーに近い位置にロードします。

これらの組み込み関数に関する詳細は、「コンパイラー・リファレンス」の「オペレーティング・システムに関連する組み込み関数」および「ストリーミング SIMD 拡張命令によるキャッシュ制御」を参照してください。

次の例では、プリフェッチ組み込み関数を使用して lfetch.nt2 命令を生成する方法を説明します。

for (i=i0; i!=i1; i+=is) {

    float sum = b[i];

    int ip = srow[i];

    int c = col[ip];

    for(; ip<srow[i+1]; c=col[++ip])

       lfetch(2, &value[ip+40]);

      _// mm_prefetch(&value[ip+40], 2);

      sum -= value[ip] * x[c];

    y[i] = sum;

  }

SSE がサポートされているプロセッサーでは、次の SSE 組み込み関数も使用できます。

IA-64 アーキテクチャー命令についての詳細は、「その他の資料」にリストされているハードウェアおよびソフトウェアのプログラミング・マニュアルでも参照できます。