¿Los compiladores de Fortran realmente generan código más rápido que los compiladores de C?

17

Cuando estudiaba en la universidad, a menudo escuchaba la idea de que los compiladores de Fortran producían un código más rápido que los compiladores de C para un programa equivalente.

El razonamiento clave fue el siguiente: un compilador Fortran emite en promedio 1,1 instrucciones de procesador por línea de código, mientras que un compilador C emite en promedio 1,6 instrucciones de procesador por línea de código : no recuerdo los números exactos, pero el La idea era que los compiladores de C emitían notablemente más código de máquina y, por lo tanto, producían programas más lentos.

¿Qué tan válida es esa comparación? ¿Podemos decir que los compiladores de Fortran producen programas más rápidos que los compiladores de C o viceversa y por qué existe esta diferencia?

diente filoso
fuente
19
Eso puede significar simplemente que los programas de Fortran son más detallados que C ... Una comparación significativa solo podría hacerse implementando la misma funcionalidad en ambos idiomas y comparando el código de máquina resultante (tamaño y velocidad).
Péter Török
Además, ¿el código generado admite la ejecución paralela?
@ Péter Török, simplemente significa que, por ejemplo, BLAS y LAPACK en Fortran solían funcionar mucho mejor que cualquiera de sus puertos C / C ++. Ahora la brecha se está reduciendo rápidamente.
SK-logic
66
Solo puede argumentar que un compilador produce un código más rápido si tiene un programa 100% equivalente en ambos idiomas, escrito por expertos que conocen sus compiladores y que pueden dar cuenta del rendimiento.
Falcon
El antiguo Fortran no admitía la recursividad y, por lo tanto, no necesariamente tenía que empujar los argumentos de llamada de función a la pila, ya que habría un espacio estáticamente asignado para los argumentos de cada función. Esta es una de las razones por las que podría haber sido más rápido. Creo que puede encontrar una respuesta más completa aquí: amazon.com/Programming-Language-Pragmatics-Third-Edition/dp/…
Pedro Rolo

Respuestas:

36

IIRC, una de las principales razones por las que se dice que Fortran es más rápido es la ausencia de alias de puntero , por lo que pueden usar optimizaciones que los compiladores de C no pueden usar:

En FORTRAN, los argumentos de función pueden no tener alias entre sí, y el compilador asume que no lo hacen. Esto permite una excelente optimización y es una de las principales razones de la reputación de FORTRAN como lenguaje rápido. (Tenga en cuenta que el alias aún puede ocurrir dentro de una función FORTRAN. Por ejemplo, si A es una matriz e i y j son índices que tienen el mismo valor, entonces A [i] y A [j] son ​​dos nombres diferentes para el misma ubicación de memoria. Afortunadamente, dado que la matriz base debe tener el mismo nombre, se puede hacer un análisis de índice para determinar los casos en que A [i] y A [j] no pueden tener alias).

Pero estoy de acuerdo con otros aquí: comparar la cantidad promedio de instrucciones de ensamblador generadas para una línea de código es una tontería. Por ejemplo, un núcleo x86 moderno puede ejecutar dos instrucciones en paralelo si no acceden a los mismos registros. Por lo tanto, puede (en teoría) obtener un aumento del rendimiento del 100% para el mismo conjunto de instrucciones con solo reordenarlas . Los buenos compiladores a menudo también generarán más instrucciones de ensamblaje para obtener un código más rápido (piense en el desenrollado del bucle, en línea). El número total de instrucciones del ensamblador dice muy poco sobre el rendimiento de un fragmento de código.

nikie
fuente
Otra razón para mejores optimizaciones es el soporte nativo para números complejos.
SK-logic
Ciertamente correcto para Fortran IV más o menos. No estoy seguro si Fortrans modernos todavía no tienen punteros, etc. dinámica meory
Ingo
2
Esa es la misma razón por la que a menudo nos redujimos a un poco de ensamblaje en línea cuando desarrollamos en C y C ++ en la industria de los juegos. Las personas pueden afirmar tan a menudo como quieran que "los compiladores pueden optimizar mejor que los humanos escribiendo ensamblados", de hecho, el alias de puntero significa que a menudo no pueden . El código que podemos escribir a mano sería técnicamente ilegal para que el compilador lo emita, sabiendo que no hace nada sobre el alias de puntero.
Carson63000
55
La restrictpalabra clave de C permite que el autor de una función especifique que un puntero no tiene alias. ¿Es esto suficiente para abordar la diferencia, o hay más?
bk.
@bk .: los ataques "restringidos" de C "son la mitad del problema"; permite decir que un puntero específico no alias nada más durante su vida útil, pero no hay forma de decirle a un compilador que un objeto cuya dirección se pasó a una función no tendrá alias una vez que esa función regrese.
supercat
8

Comparación completamente inválida.

Primero, como señala @ Péter Török, primero debe comparar el número de líneas en programas equivalentes de Fortran y C para que esto sea una comparación válida en el número de líneas producidas.

Segundo, menos líneas de código no siempre equivalen a programas más rápidos . No todas las instrucciones de la máquina requieren la misma cantidad de ciclos para ejecutarse , pero también tiene otros problemas, como el acceso a la memoria , el almacenamiento en caché , etc.

Además de eso, las ejecuciones de código largas pueden ser más rápidas ya que da como resultado un número menor de líneas de ejecución (es decir, ¡Recuento de líneas! = Recuento de líneas ejecutadas ).

Dan McGrath
fuente
5

Dan tiene razón, los programas más largos no significan programas más lentos. Depende mucho de lo que estén haciendo.

No soy un experto en Fortran, lo sé un poco. Comparándolos, creo que una C bien escrita funcionaría mucho mejor con estructuras de datos y funcionalidades más complejas que Fortran. Alguien (por favor) corríjame si me equivoco aquí, pero creo que Fortran está en un "nivel más bajo" que C. Si es así, estoy seguro de que algunos problemas saldrían más rápido en Fortran.

Otra cosa, a primera vista, pensé que estabas preguntando si los compiladores son más rápidos. De hecho, creo que Fortran generalmente compilará más rápido para cantidades similares de código, pero el programa resultante y su funcionamiento sería una historia diferente. Es más sencillo analizarlo.

Garet Claborn
fuente
2
Si está utilizando estructuras de datos complejas, FORTRAN es probablemente la elección incorrecta. FORTRAN está optimizado para hacer un simple cálculo de números muy rápido.
Zachary K
4

Creo que parte de esto es que los compiladores FORTRAN están diseñados para hacer algunos tipos de matemática muy rápido. Es por eso que las personas usan FORTRAN, para hacer cálculos lo más rápido posible

Zachary K
fuente
4

La declaración puede haber sido cierta en los viejos tiempos (alrededor de finales de los 70) cuando C estaba en su infancia, y Fortran fue respaldado por todos los principales fabricantes y estaba altamente optimizado. Los primeros Fortrans se basaban en la arquitectura de IBM, por lo que cosas tan simples como la aritmética habrían sido una declaración por instrucción de ensamblaje. Esto es cierto para las máquinas más antiguas como Data General y Prime, que tenían saltos de 3 vías. Esto no funciona en conjuntos de instrucciones modernos que no tienen un salto de 3 vías.

Las líneas de código no son iguales a las declaraciones de código. Las versiones anteriores de Fortran solo permitían una declaración por línea. Las versiones posteriores de Fortran pueden tomar múltiples declaraciones por línea. C puede tener múltiples declaraciones por línea. En los compiladores de producción más rápidos como el IVF de Intel (anteriormente CVF, MS Powerstation) y el C de Intel, realmente no hay diferencia entre los dos. Estos compiladores están altamente optimizados.

taza
fuente
4

FORTRAN de estilo antiguo requería que un programador que quisiera poner a disposición de una función parte de una matriz necesitaba pasar una referencia a toda la matriz, junto con uno o más valores enteros que especificaran el subíndice inicial y el subíndice final o el número de elementos . C hace posible simplificar esto al pasar un puntero al inicio de la parte de interés junto con el número de elementos. En términos directos, esto aceleraría las cosas (pasar dos cosas en lugar de tres). Sin embargo, indirectamente, puede terminar ralentizando las cosas al limitar los tipos de optimización que puede realizar un compilador.

Considere la función:

void diff(float dest[], float src1[], float src2[], int n)
{
  for (int i=0; i<n; i++)
    dest[i] = src1[i] - src2[i];
}

Si un compilador supiera que cada uno de los punteros identificaría el inicio de una matriz, podría generar código que actuaría sobre los elementos de la matriz en paralelo, o en cualquier orden, ya que para cualquier operación x! = y, en dest [x ] no afectará a src1 [y] ni a src2 [y]. Por ejemplo, en algunos sistemas un compilador puede beneficiarse de generar 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
}

Tenga en cuenta que cada operación que carga o calcula un valor tiene al menos una operación más entre él y la siguiente operación que usa ese valor. Algunos procesadores pueden superponer el procesamiento de diferentes operaciones cuando se cumplen tales condiciones, mejorando así el rendimiento. Sin embargo, tenga en cuenta que debido a que un compilador de C no tiene forma de saber que el código no se pasará a punteros a regiones parcialmente superpuestas de una matriz común, un compilador de C no puede realizar la transformación anterior. Los compiladores de FORTRAN a los que se les dio un código equivalente, sin embargo, pudieron hacer una transformación.

Si bien un programador en C podría intentar lograr un rendimiento comparable escribiendo explícitamente el código que desenrollaba el ciclo y superponía las operaciones de los pases adyacentes, dicho código podría degradar fácilmente el rendimiento si utilizara tantas variables automáticas que un compilador tuvo que "derramarlas" memoria. El optimizador de un compilador FORTRAN probablemente sabría más que un programador acerca de qué formas de entrelazado producirían un rendimiento óptimo en un escenario dado, y esas decisiones a menudo son mejores para esos compiladores. Si bien C99 intentó mejorar la situación de C agregando un restrictcalificador, eso solo se podría usar aquí si dest[]fuera una matriz separada de ambos src1[]y src2[], o si el programador agregó versiones separadas del bucle para manejar los casos en los que todo destera disjunto desrc1y src2, dónde src1[]y desteran iguales y src2eran disjuntos, dónde src2[]y dest[]eran iguales y src1eran disjuntos, y dónde las tres matrices eran iguales. FORTRAN, por el contrario, podría manejar los cuatro casos sin dificultad utilizando el mismo código fuente y el mismo código de máquina.

Super gato
fuente