Os compiladores Fortran realmente geram código mais rápido que os compiladores C?


17

Quando eu estava estudando na universidade, ouvi muitas vezes a idéia de que os compiladores Fortran produziam código mais rápido que os compiladores C para um programa equivalente.

O principal raciocínio foi o seguinte: um compilador Fortran emite em média 1,1 instruções do processador por linha de código, enquanto um compilador C emite em média 1,6 instruções do processador por linha de código - não me lembro dos números exatos, mas o A idéia era que os compiladores C emitissem visivelmente mais código de máquina e, portanto, produzissem programas mais lentos.

Quão válida é essa comparação? Podemos dizer que os compiladores Fortran produzem programas mais rápidos que os compiladores C ou vice-versa e por que essa diferença existe?


19
Isso pode significar simplesmente que os programas Fortran são mais detalhados que C ... Uma comparação significativa só pode ser feita implementando a mesma funcionalidade nos dois idiomas e comparando o código de máquina resultante (tamanho e velocidade).
Péter Török 21/03

Além disso, o código gerado suporta execução paralela?

@ Péter Török, significa simplesmente que, digamos, BLAS e LAPACK no Fortran costumavam ter um desempenho muito melhor do que qualquer uma de suas portas C / C ++. Agora a diferença está diminuindo rapidamente.
SK-logic

6
Você só pode argumentar que um compilador produz código mais rápido se você tiver um programa equivalente a 100% nos dois idiomas, escrito por especialistas que conhecem seus compiladores e que podem dar conta do desempenho.
Falcon

O antigo Fortran não suportava recursão e, portanto, não precisava necessariamente empurrar os argumentos de chamada de função para a pilha, pois haveria um espaço alocado estaticamente para os argumentos de cada função. Essa é uma das razões pelas quais pode ter sido mais rápido. Eu acho que você pode encontrar uma resposta mais completa aqui: amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/…
Pedro Rolo

Respostas:


36

O IIRC, um dos principais motivos pelos quais o Fortran é mais rápido, é a ausência de alias de ponteiro , para que eles possam usar otimizações que os compiladores C não podem usar:

No FORTRAN, os argumentos da função podem não ter um nome alternativo eo compilador assume que não. Isso permite excelente otimização e é um dos principais motivos da reputação da FORTRAN como uma linguagem rápida. (Observe que o aliasing ainda pode ocorrer em uma função FORTRAN. Por exemplo, se A é uma matriz e iej são índices que têm o mesmo valor, então A [i] e A [j] são dois nomes diferentes para o Felizmente, como a matriz base deve ter o mesmo nome, é possível fazer uma análise de índice para determinar os casos em que A [i] e A [j] não podem usar o pseudônimo).

Mas eu concordo com outras pessoas aqui: comparar o número médio de instruções do assembler geradas para uma linha de código é um absurdo completo. Por exemplo, um núcleo x86 moderno pode executar duas instruções em paralelo se não acessar os mesmos registros. Assim, você pode (em teoria) obter um aumento de desempenho de 100% para o mesmo conjunto de instruções apenas reordenando-as . Bons compiladores também geralmente geram mais instruções de montagem para obter um código mais rápido (pense em desenrolar o loop, embutir). O número total de instruções do assembler diz muito pouco sobre o desempenho de um pedaço de código.


Outro motivo para melhores otimizações é o suporte nativo a números complexos.
SK-logic

Certamente correto para Fortran IV ou mais. Não tenho certeza se os FORTRANs modernos ainda não têm ponteiros, memória dinâmica etc.
Ingo

2
Essa é a mesma razão pela qual muitas vezes reduzimos para um pouco de montagem em linha ao desenvolver em C e C ++ na indústria de jogos. As pessoas podem alegar quantas vezes quiserem que "os compiladores podem otimizar melhor do que os humanos que escrevem montagem"; na verdade, alias de ponteiro significa que muitas vezes não podem . O código que podemos escrever à mão seria tecnicamente ilegal para o compilador emitir, sabendo que ele não faz nada sobre o alias do ponteiro.
Carson63000

5
A restrictpalavra-chave C permite que o autor de uma função especifique que um ponteiro não possui aliases. Isso é suficiente para resolver a diferença ou há mais?
bk.

@bk .: Os ataques "restritos" de C "metade do problema"; torna possível dizer que um ponteiro específico não fará o apelido de qualquer outra coisa durante sua vida útil, mas não há como dizer a um compilador que um objeto cujo endereço foi passado para uma função não terá o apelido de nada quando a função retornar.
Supercat

8

Comparação completamente inválida.

Primeiro, como @ Péter Török salienta, você deve primeiro comparar o número de linhas em programas equivalentes do Fortran e C para que isso seja uma comparação válida no número de linhas produzidas.

Segundo, menos linhas de código nem sempre são iguais a programas mais rápidos . Nem todas as instruções da máquina levam o mesmo número de ciclos para serem executadas , mas você também tem outros problemas, como acesso à memória , cache , etc.

Além disso, execuções longas de código podem ser mais rápidas, pois resultam em um número menor de linhas de execução (ou seja, Contagem de Linhas! = Contagem de Linhas Executadas ).


5

Dan está correto, programas mais longos não significam programas mais lentos. Depende muito do que eles estão fazendo.

Não sou especialista em Fortran, sei um pouco. Comparando-os, eu pensaria que C bem escrito teria um desempenho muito melhor com estruturas e funcionalidade de dados mais complexas que o Fortran. Alguém (por favor) me corrija se eu estiver errado aqui, mas acho que o Fortran está um pouco em um 'nível mais baixo' do que o C.

Outra coisa, à primeira vista, pensei que você estivesse perguntando se os compiladores são mais rápidos. Na verdade, acho que o Fortran geralmente compila mais rapidamente para quantidades semelhantes de código, mas o programa resultante e como ele é executado seria uma história diferente. É apenas mais simples de analisar.


2
Se você estiver usando estruturas de dados complexas, o FORTRAN provavelmente é a escolha errada. O FORTRAN é otimizado para fazer trituração de números simples muito rapidamente.
Zachary K

4

Eu acho que parte disso é que os compiladores FORTRAN são projetados para fazer alguns tipos de matemática muito rapidamente. É por isso que as pessoas usam o FORTRAN para fazer cálculos o mais rápido possível


4

A afirmação pode ter sido verdadeira nos velhos tempos (por volta dos anos 70), quando C estava em sua infância, e o Fortran era apoiado por todos os principais fabricantes e era altamente otimizado. Os primeiros Fortrans eram baseados na arquitetura da IBM, coisas tão simples como a aritmética se certamente teriam sido uma instrução por instrução de montagem. Isso é verdade em máquinas mais antigas, como Data General e Prime, que tinham saltos de 3 vias. Isso não funciona em conjuntos de instruções modernos que não possuem um salto de três direções.

Linhas de código não são iguais a instruções de código. As versões anteriores do Fortran permitiam apenas uma instrução por linha. Versões posteriores do Fortran podem receber várias instruções por linha. C pode ter várias instruções por linha. Nos compiladores de produção mais rápidos, como o IVF da Intel (anteriormente CVF, MS Powerstation) e o C da Intel, realmente não há diferença entre os dois. Esses compiladores são altamente otimizados.


4

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 restrictqualificador, 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 destestava separado.src1e src2, ondesrc1[]e desteram iguais e src2eram disjuntos, onde src2[]e dest[]eram iguais e src1eram 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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.