performance - site - Consejos y trucos para mejorar el rendimiento del código Fortran
seo off page (3)
Lo siento, pero todos los trucos que mencionaste son simplemente ... ridículos. Más exactamente, no tienen ningún significado en la práctica. Por ejemplo:
- ¿Cuál podría ser la ventaja de usar la mitad (= 0.5) en lugar de 0.5?
- idem para calcular
a**4
oa*a*a*a
.(a*a)** 2
sería otra posibilidad también. Mi gusto personal es un ** 4 porque es un buen compilador que elige automáticamente la mejor manera.
Para **
, el único punto que podría importar es la diferencia entre a ** 4
y a ** 4.
, el último consume mucho más tiempo de la CPU. Pero incluso este punto no tiene sentido sin una medida en una simulación real.
De hecho, su enfoque es incorrecto. Desarrolla tu código lo mejor posible. Después de eso, mida objetivamente el costo de las diferentes partes de su código. Optimizar sin medir antes es simplemente sin sentido.
Si una parte exhibe un alto porcentaje de la CPU, 50% por ejemplo, no olvide que la optimización de esa parte solo no puede dividir el costo del código general por un factor mayor que dos. De todos modos, comience la optimización por la parte más cara (el cuello de la botella).
No olvide también que las principales mejoras provienen generalmente de mejores algoritmos.
Como parte de mi Ph.D. investigación, estoy trabajando en el desarrollo de modelos numéricos de la atmósfera y la circulación oceánica. Estos implican la resolución numérica de sistemas de PDE del orden de ~ 10 ^ 6 puntos de cuadrícula, más de ~ 10 ^ 4 pasos de tiempo. Por lo tanto, una simulación de modelo típica tarda horas o días en completarse cuando se ejecuta en MPI en docenas de CPU. Naturalmente, es importante mejorar la eficiencia del modelo tanto como sea posible, asegurándose al mismo tiempo de que los resultados sean idénticos byte a byte.
Aunque me siento bastante cómodo con mi programación de Fortran, y conozco algunos trucos para hacer que el código sea más eficiente, siento que todavía hay espacio para mejorar y trucos que no conozco.
Actualmente, me aseguro de usar las menos divisiones posibles e intento no usar constantes literales (me enseñaron a hacer esto desde muy temprano, por ejemplo, usar la mitad = 0.5 en lugar de 0.5 en cálculos reales), usar tan pocas funciones trascendentales como sea posible, etc.
¿Qué otros factores sensibles al rendimiento hay? Por el momento, me pregunto sobre algunos:
1) ¿Importa el orden de las operaciones matemáticas? Por ejemplo si tengo:
a=1E-7 ; b=2E4 ; c=3E13
d=a*b*c
evaluaría con una eficiencia diferente según el orden de multiplicación? Hoy en día, esto debe ser específico del compilador, pero ¿hay una respuesta directa? Observé d obteniendo un valor (ligeramente) diferente en función del orden (límite de precisión), pero ¿afectará esto a la eficiencia o no?
2) ¿Pasar muchos (por ejemplo, docenas) de matrices como argumentos a una subrutina en lugar de acceder a estas matrices desde un módulo dentro de la subrutina?
3) Construcciones Fortran 95 (FORALL y WHERE) versus DO e IF? Sé que esto importó en la década de los 90, cuando la vectorización del código era una gran cosa, pero ¿hay alguna diferencia ahora con los compiladores modernos que puedan vectorizar los bucles DO explícitos? (Estoy usando compiladores de PGI, Intel e IBM en mi trabajo)
4) ¿Subir un número a un poder entero versus multiplicación? P.ej:
b=a**4
o
b=a*a*a*a
Me han enseñado a usar siempre este último siempre que sea posible. ¿Afecta esto la eficiencia y / o la precisión? (probablemente el compilador también depende)
Discuta y / o agregue trucos y consejos que conozca sobre la mejora de la eficacia del código Fortran. ¿Qué más hay? Si sabe algo específico sobre lo que cada uno de los compiladores anteriores hace en relación con esta pregunta, inclúyalo también.
Agregado: Tenga en cuenta que no tengo ningún cuello de botella ni problemas de rendimiento per se. Pregunto si hay reglas generales para optimizar el código en el sentido de las operaciones.
¡Gracias!
Apoyo el consejo de que estos trucos que te han enseñado son tontos en esta época. Los compiladores hacen esto por ti ahora; Es poco probable que esas microoptimizaciones hagan una diferencia significativa y que no sean portátiles. Escriba un código claro y comprensible. Con cuidado selecciona tu algoritmo. Una cosa que puede marcar la diferencia es usar índices de matrices multidimensionales en el orden correcto ... la refundición de una matriz de MXN a NXM puede ayudar dependiendo del patrón de acceso a datos de su programa. Después de esto, si su programa es demasiado lento, mida dónde se consume la CPU y mejore solo esas partes. La experiencia muestra que conjeturar es frecuentemente incorrecto y lleva a escribir código más opaco para la razón. Si crea una sección de código en la que su programa gasta el 1% de su tiempo el doble de rápido, no hará ninguna diferencia.
Aquí están las respuestas anteriores en FORALL y WHERE: ¿Cómo puedo asegurarme de que mi construcción Fortran FORALL se está paralelizando? y las construcciones de Do Fortran 95 como WHERE, FORALL y SPREAD generalmente dan como resultado un código paralelo más rápido?
Tienes ideas a priori sobre qué hacer, y algunas de ellas pueden ayudar, pero la mayor recompensa es a posteriori.
( Agregado: en otras palabras, obtener a*b*c
en un orden diferente puede ahorrarle un par de ciclos (lo cual dudo), mientras que al mismo tiempo no sabe que no se está quedando ciego por algo que gasta 1000 ciclos sin una buena razón.)
No importa qué tan cuidadosamente lo codifique, habrá oportunidades de aceleración que no previó. Así es como los encuentro. (Algunas personas consideran este método controvertido).
Lo mejor es comenzar con las marcas de optimización desactivadas cuando hagas esto, por lo que el código no está codificado. Más tarde puede activarlos y dejar que el compilador haga su trabajo.
Haz que se ejecute bajo un depurador con una carga de trabajo suficiente para que se ejecute durante un período de tiempo razonable. Mientras se está ejecutando, interrúmpalo manualmente, y analiza bien lo que está haciendo y por qué. Haga esto varias veces, como 10, para que no saque conclusiones erróneas sobre lo que está pasando tiempo.
Aquí hay ejemplos de cosas que puede encontrar:
- Podría pasar una gran parte del tiempo llamando innecesariamente a las funciones de la biblioteca matemática debido a la forma en que se codificaron algunas expresiones, o con los mismos valores de argumento que en las llamadas anteriores.
- Podría pasar una gran parte del tiempo haciendo algo de E / S de archivo, o abrir / cerrar un archivo, en el fondo de una rutina que parecía inofensivo llamar.
- Podría ser en una función de biblioteca de propósito general, llamando a una subrutina subordinada, con el fin de verificar indicadores de argumento a la función superior. En tal caso, gran parte de ese tiempo podría eliminarse escribiendo una función de propósito especial y llamando a eso en su lugar.
Si haces toda esta operación dos o tres veces, habrás eliminado las cosas estúpidas que encuentran su camino en cualquier software cuando se haya escrito por primera vez. Después de eso, puede activar la optimización, el paralelismo o lo que sea, y estar seguro de que no se está gastando tiempo en tonterías.