Me gusta cuándo usar SPECIALIZE
pragma y qué mejoras de rendimiento tiene.
Deje que el compilador especialice una función si tiene una función (tipo de clase) polimórfica, y espera que se la invoque a menudo en una o varias instancias de la (s) clase (s).
La especialización elimina la búsqueda del diccionario donde se utiliza, y a menudo permite una mayor optimización, las funciones de miembros de la clase a menudo pueden incluirse y están sujetas a análisis de rigor, ambas ofrecen ganancias de rendimiento potencialmente enormes. Si la única optimización posible es la eliminación de la búsqueda dicitonary, la ganancia generalmente no será enorme.
A partir del GHC-7, probablemente sea más útil darle a la función un pragma {-# INLINABLE #-}
, que hace que su fuente (casi sin modificar, normalizar y desgrasar) esté disponible en el archivo de interfaz, por lo que la función puede ser especializada y posiblemente incluso en línea en el sitio de llamadas.
Dónde usar RULES
. Escucho que las personas toman sobre una regla particular que no se dispara? ¿Cómo lo verificamos?
Puede verificar qué reglas se activaron utilizando la opción de línea de comando -ddump-rule-firings
. Por lo general, arroja una gran cantidad de reglas disparadas, por lo que debe buscar un poco sus propias reglas.
utiliza reglas
cuando se tiene una versión más eficiente de una función para tipos especiales, por ejemplo,
{-# RULES
"realToFrac/Float->Double" realToFrac = float2Double
#-}
cuando algunas de las funciones pueden ser reemplazados con una versión más eficiente para argumentos especiales, por ejemplo,
{-# RULES
"^2/Int" forall x. x^(2 :: Int) = let u = x in u*u
"^3/Int" forall x. x^(3 :: Int) = let u = x in u*u*u
"^4/Int" forall x. x^(4 :: Int) = let u = x in u*u*u*u
"^5/Int" forall x. x^(5 :: Int) = let u = x in u*u*u*u*u
"^2/Integer" forall x. x^(2 :: Integer) = let u = x in u*u
"^3/Integer" forall x. x^(3 :: Integer) = let u = x in u*u*u
"^4/Integer" forall x. x^(4 :: Integer) = let u = x in u*u*u*u
"^5/Integer" forall x. x^(5 :: Integer) = let u = x in u*u*u*u*u
#-}
al reescribir una expresión de acuerdo con las leyes generales podrían producir código que es mejor para optimizar, por ejemplo,
{-# RULES
"map/map" forall f g. (map f) . (map g) = map (f . g)
#-}
El uso extensivo de RULES
en el segundo estilo se hace en marcos de fusión, por ejemplo en la biblioteca de text
, y para las funciones de lista en base
, un tipo diferente de fusión (foldr/build
de fusión) se implementa usando reglas.
Cuándo hacer estrictos los argumentos de una función y cuándo ayuda eso? Entiendo que hacer un argumento estricto hará que los argumentos sean evaluados a la forma normal, entonces ¿por qué no debería agregar rigor a todos los argumentos de la función? ¿Cómo decido?
Haciendo un argumento estricta se asegurará de que se evalúa a débil cabeza de forma normal, no forma normal.
No hace todos los argumentos estrictos porque algunas funciones deben ser no estrictas en algunos de sus argumentos para funcionar y algunas son menos eficientes si son estrictas en todos los argumentos.
Para examplepartition
no debe ser estricta en su segundo argumento a trabajar en absoluto en las listas infinitas, más general cada función usada en foldr
debe ser no estricta en el segundo argumento para trabajar en listas infinitas. En listas finitas, tener la función no estricta en el segundo argumento puede hacerlo dramáticamente más eficiente (foldr (&&) True (False:replicate (10^9) True)
).
Hace un argumento estricto, si sabe que el argumento debe ser evaluado antes de que se pueda hacer cualquier trabajo que valga la pena de todos modos. En muchos casos, el analizador de rigor de GHC puede hacerlo por sí mismo, pero, por supuesto, no en todos.
Un caso muy típico son los acumuladores en bucles o repeticiones de cola, donde la rigidez de adición impide la construcción de enormes thunks en el camino.
No conozco reglas estrictas sobre dónde añadir rigor, para mí es una cuestión de experiencia, después de un tiempo aprendes en qué lugares es probable que la suma de rigor ayude a dónde dañar.
Como regla general, tiene sentido mantener los datos pequeños (como Int
) evaluados, pero hay excepciones.
¿Cómo puedo ver y verificar que tengo una pérdida de espacio en mi programa? ¿Cuáles son los patrones generales que constituyen una fuga de espacio?
El primer paso es usar la opción +RTS -s
(si el programa estaba vinculado con rtsopts habilitado). Eso le muestra cuánta memoria se utilizó en general, y a menudo puede juzgar por eso si tiene una fuga. Se puede obtener un resultado más informativo ejecutando el programa con la opción +RTS -hT
, que genera un perfil dinámico que puede ayudar a localizar la fuga de espacio (también, el programa debe vincularse con rtsopts habilitados).
Si se requiere un análisis más detallado, el programa tiene que ser compilado con seguimiento permitido (-rtsops -prof -fprof-auto
, en GHCs mayores, la opción -fprof-auto
no estaba disponible, la opción -prof-auto-all
es la correspondencia más cercana allí).
Luego lo ejecuta con varias opciones de perfil y mira los perfiles de montón generados.
Las dos causas más comunes de fugas de espacio son
- demasiada pereza
- demasiada rigurosidad
el tercer lugar es probablemente tomada por el intercambio no deseado, GHC hace poco eliminación de subexpresiones comunes , pero ocasionalmente comparte largas listas incluso donde no se desea.
Para encontrar la causa de una fuga, sé de nuevo que no existen reglas estrictas, y de vez en cuando, se puede corregir una fuga agregando rigor en un lugar o agregando pereza en otro.
¿Cómo puedo ver si hay un problema con demasiada pereza? Siempre puedo verificar el perfil del montón, pero quiero saber cuáles son las causas generales, los ejemplos y los patrones en los que la pereza duele.
En general, la pereza es buscado donde los resultados pueden ser construidos de forma incremental, y no deseados en ninguna parte del resultado puede ser entregada antes de la transformación es completa, al igual que en los pliegues hacia la izquierda o en general en funciones de cola recursiva.
RHW ch 25? http://book.realworldhaskell.org/read/profiling-and-optimization.html –
@DonStewart Gracias .. Ya leí RWH .. El problema es que sé lo que hacen, pero no tengo una intuición sobre cuándo y donde su uso es importante y no importante. Hice esta pregunta para encontrar otras fuentes para mejorar mi comprensión. – Satvik