FORTRAN w starym stylu wymagał, aby programista, który chciał udostępnić część tablicy dla funkcji, musiał przekazać odwołanie do całej tablicy, wraz z jedną lub większą liczbą liczb całkowitych określającą indeks dolny i indeks dolny końcowy lub liczbę elementów . C umożliwia uproszczenie tego do przekazywania wskaźnika na początek interesującej części wraz z liczbą elementów. Mówiąc wprost, przyspieszyłoby to sprawę (przekazując dwie rzeczy zamiast trzech). Pośrednio może to jednak spowolnić proces, ograniczając rodzaje optymalizacji, które może przeprowadzić kompilator.
Rozważ funkcję:
void diff(float dest[], float src1[], float src2[], int n)
{
for (int i=0; i<n; i++)
dest[i] = src1[i] - src2[i];
}
jeśli kompilator wiedziałby, że każdy ze wskaźników identyfikuje początek tablicy, mógłby wygenerować kod, który działałby na elementy tablicy równolegle lub w dowolnej kolejności, ponieważ dla dowolnego x! = y operacje na dest [x ] nie wpłynie na src1 [y] ani src2 [y]. Na przykład w niektórych systemach kompilator może skorzystać z generowania kodu odpowiadającego:
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
}
Zauważ, że każda operacja, która ładuje lub oblicza wartość, ma co najmniej jedną dodatkową operację między nią a następną operacją, która korzysta z tej wartości. Niektóre procesory mogą nakładać się na przetwarzanie różnych operacji, gdy takie warunki są spełnione, co poprawia wydajność. Zauważ jednak, że ponieważ kompilator C nie ma możliwości dowiedzenia się, że kod nie zostanie przekazany wskaźnikom do częściowo pokrywających się regionów wspólnej tablicy, kompilator C nie może dokonać powyższej transformacji. Kompilatory FORTRAN, które otrzymały równoważny kod, mogły jednak dokonać takiej transformacji.
Podczas gdy programista C mógłby próbować osiągnąć porównywalną wydajność poprzez jawne wypisanie kodu, który rozwinął pętlę i nałożył się na operacje sąsiednich przejść, taki kod mógłby łatwo obniżyć wydajność, gdyby użył tak wielu zmiennych automatycznych, że kompilator musiałby je „przelać” do pamięć. Optymalizator kompilatora FORTRAN prawdopodobnie wiedziałby więcej niż programista o tym, jakie formy przeplotu zapewniłyby optymalną wydajność w danym scenariuszu, i takie decyzje często najlepiej pozostawić takim kompilatorom. Podczas gdy C99 próbował nieco poprawić sytuację C, dodając restrict
kwalifikator, można go tutaj użyć tylko wtedy, gdy dest[]
jest oddzielną tablicą od obu src1[]
i src2[]
, lub jeśli programista dodał osobne wersje pętli, aby obsłużyć przypadki, w których wszystko dest
było rozłącznesrc1
i src2
gdziesrc1[]
i dest
były równe i src2
były rozłączne, gdzie src2[]
i dest[]
były równe i src1
były rozłączne, i gdzie wszystkie trzy tablice były równe. FORTRAN, z drugiej strony, mógłby bez problemu obsługiwać wszystkie cztery przypadki przy użyciu tego samego kodu źródłowego i tego samego kodu maszynowego.