O FORTRAN à moda antiga exigia que um programador que quisesse disponibilizar parte de uma matriz para uma função passasse uma referência a toda a matriz, juntamente com um ou mais valores inteiros, especificando o índice inicial e o índice final ou o número de itens . C torna possível simplificar isso passando um ponteiro para o início da parte de interesse junto com o número de elementos. Em termos diretos, isso tornaria as coisas mais rápidas (passando duas coisas em vez de três). Indiretamente, no entanto, isso pode resultar em lentidão, limitando os tipos de otimização que um compilador pode executar.
Considere a função:
void diff(float dest[], float src1[], float src2[], int n)
{
for (int i=0; i<n; i++)
dest[i] = src1[i] - src2[i];
}
se um compilador soubesse que cada um dos ponteiros identificaria o início de uma matriz, ele poderia gerar código que atuaria sobre os elementos da matriz em paralelo ou em qualquer ordem, pois para qualquer x! = y, operações em dest [x ] não afetará src1 [y] nem src2 [y]. Por exemplo, em alguns sistemas, um compilador pode se beneficiar da geração de código equivalente a:
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
}
Observe que toda operação que carrega ou calcula um valor tem pelo menos mais uma operação entre ele e a próxima operação que usa esse valor. Alguns processadores podem se sobrepor ao processamento de diferentes operações quando essas condições forem atendidas, melhorando assim o desempenho. Observe, no entanto, que como um compilador C não tem como saber que o código não passará ponteiros para regiões parcialmente sobrepostas de uma matriz comum, um compilador C não pode fazer a transformação acima. Os compiladores FORTRAN que receberam código equivalente, no entanto, puderam e fizeram essa transformação.
Enquanto um programador C poderia tentar obter desempenho comparável escrevendo explicitamente o código que desenrolava o loop e sobrepunha as operações de passagens adjacentes, esse código poderia facilmente prejudicar o desempenho se usasse tantas variáveis automáticas que um compilador precisava "derramar" para memória. O otimizador de um compilador FORTRAN provavelmente saberia mais do que um programador sobre quais formas de intercalação produziriam um desempenho ideal em um determinado cenário, e é melhor deixar essas decisões para esses compiladores. Enquanto o C99 tentava melhorar um pouco a situação de C adicionando um restrict
qualificador, isso só poderia ser usado aqui se houvesse dest[]
uma matriz separada de ambos src1[]
e src2[]
, ou se o programador adicionasse versões separadas do loop para lidar com os casos em que tudo dest
estava separado.src1
e src2
, ondesrc1[]
e dest
eram iguais e src2
eram disjuntos, onde src2[]
e dest[]
eram iguais e src1
eram disjuntos, e onde todas as três matrizes eram iguais. O FORTRAN, por outro lado, poderia lidar com todos os quatro casos sem dificuldade usando o mesmo código-fonte e o mesmo código de máquina.