Old-style FORTRAN required that a programmer who wanted to make part of an array available to a function needed to pass a reference to the whole array, along with one or more integer values specifying the starting subscript and either the ending subscript or number of items. C makes it possible to simplify this to passing a pointer to the start of the portion of interest along with the number of elements. In direct terms, this would make things faster (passing two things rather than three). Indirectly, however, it may end up slowing things down by limiting the kinds of optimization a compiler can perform.
Consider the function:
void diff(float dest[], float src1[], float src2[], int n)
{
for (int i=0; i<n; i++)
dest[i] = src1[i] - src2[i];
}
if a compiler knew that each of the pointers would identify the start
of an array, it could generate code that would act upon elements of the
array in parallel, or in any order, since for any x!=y, operations on
dest[x] will not affect src1[y] nor src2[y]. For example, on some systems
a compiler may benefit from generating code equivalent to:
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
}
Note that every operation that loads or computes a value has at least one more operation between it and the next operation that uses that value. Some processors can overlap the processing of different operations when such conditions are met, thus improving performance. Note, however, that because a C compiler has no way of knowing that the code won't be passed pointers to partially-overlapping regions of a common array, a C compiler can't make the above transformation. FORTRAN compilers given equivalent code, however, could and did make such a transformation.
While a C programmer could attempt to achieve comparable performance by explicitly writing out code that unrolled the loop and overlapped the operations of adjacent passes, such code could easily degrade performance if it used so many automatic variables that a compiler had to "spill" them to memory. A FORTRAN compiler's optimizer would likely know more than a programmer about what forms of interleaving would yield optimal performance in a given scenario, and such decisions are often best left to such compilers. While C99 attempted to improve C's situation somewhat by adding a restrict
qualifier, that could only be used here if dest[]
was a separate array from both src1[]
and src2[]
, or if the programmer added separate versions of the loop to handle the cases where all dest
was disjoint from src1
and src2
, where src1[]
and dest
were equal and src2
was disjoint, where src2[]
and dest[]
were equal and src1
was disjoint, and where all three arrays were equal. FORTRAN, by contrast, could handle all four cases without difficulty using the same source code and the same machine code.