古いスタイルのFORTRANでは、配列の一部を関数で使用できるようにしたいプログラマーは、配列全体への参照と、開始添え字と終了添え字またはアイテムの数を指定する1つ以上の整数値を渡す必要がありました。 。Cは、これを単純化して、要素の数とともに、関心のある部分の開始点へのポインターを渡すことを可能にします。直接的に言えば、これは物事をより速くします(3つではなく2つの物を渡す)。ただし、間接的に、コンパイラが実行できる最適化の種類を制限することにより、結果が遅くなる場合があります。
機能を考えてみましょう:
void diff(float dest[], float src1[], float src2[], int n)
{
for (int i=0; i<n; i++)
dest[i] = src1[i] - src2[i];
}
コンパイラーが各ポインターが配列の開始を識別することを知っていた場合、x!= yの場合はdest [x ]はsrc1 [y]にもsrc2 [y]にも影響しません。たとえば、一部のシステムでは、コンパイラは次と同等のコードを生成することで恩恵を受ける場合があります。
void dif(float dest[], float src1[], float src2[], int n)
{
int i=0;
float t1a,t1b,t2a,t2b,tsa,tsb;
if (n > 2)
{
n-=4;
t1a = src1[n+3]; t1b = src2[n+3]; t1b=src2[n+2]; t2b = src2[n+2];
do
{
tsa = t1a-t2a;
t1a = src1[n+1]; t2a = src2[n+1];
tsb = t2b-t2b;
dest[n+3] = tsa;
t1b = src1[n]; t2b = src2[n];
n-=2;
dest[n+4] = tsb;
} while(n >= 0);
... add some extra code to handle cleanup
}
else
... add some extra code to handle small values of n
}
値をロードまたは計算するすべての操作には、その値とその値を使用する次の操作の間に少なくとも1つ以上の操作があることに注意してください。プロセッサの中には、このような条件が満たされたときに異なる操作の処理をオーバーラップできるものがあり、パフォーマンスが向上します。ただし、Cコンパイラには、共通配列の部分的に重複する領域へのポインタがコードに渡されないことを知る方法がないため、Cコンパイラは上記の変換を行うことができません。ただし、同等のコードが与えられたFORTRANコンパイラは、このような変換を行うことができ、実際に行いました。
Cプログラマーは、ループを展開し、隣接するパスの操作をオーバーラップするコードを明示的に記述することで同等のパフォーマンスを達成しようとすることができますが、コンパイラーはメモリ。FORTRANコンパイラのオプティマイザは、特定のシナリオでどのようなインターリーブ形式が最適なパフォーマンスをもたらすかについて、プログラマーよりも多くのことを知っていると思われます。C99は、多少追加することにより、Cの状況を改善しようとしたもののrestrict
場合のみ、ここで使用することができることを、修飾子をdest[]
両方から別々の配列だったsrc1[]
とsrc2[]
、あるいはプログラマがすべてのケース処理するために、ループの別のバージョンを追加した場合dest
からばらばらだったがsrc1
そしてsrc2
、どこsrc1[]
とdest
等しかったとsrc2
互いに素であり、ここで、src2[]
とdest[]
等しかったとsrc1
互いに素であり、すべての3つの配列は同じであった場合。対照的に、FORTRANは、同じソースコードと同じマシンコードを使用して、4つのケースすべてを問題なく処理できます。