Tengo problemas para que GHC especialice una función con una restricción de clase. Tengo un ejemplo mínimo de mi problema aquí: Foo.hs y Main.hs . Los dos archivos se compilan (GHC 7.6.2, ghc -O3 Main) y se ejecutan.
NOTA:
Foo.hs está realmente despojado. Si desea ver por qué se necesita la restricción, puede ver un poco más de código aquí . Si pongo el código en un solo archivo o hago muchos otros cambios menores, GHC simplemente alinea la llamada plusFastCyc. Esto no sucederá en el código real porque plusFastCyces demasiado grande para que GHC lo conecte, incluso cuando está marcado INLINE. El punto es especializar la llamada a plusFastCyc, no en línea. plusFastCycse llama en muchos lugares en el código real, por lo que duplicar una función tan grande no sería deseable incluso si pudiera forzar a GHC a hacerlo.
El código de interés es el plusFastCycen Foo.hs, reproducido aquí:
{-# INLINEABLE plusFastCyc #-}
{-# SPECIALIZE plusFastCyc ::
forall m . (Factored m Int) =>
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) #-}
-- Although the next specialization makes `fcTest` fast,
-- it isn't useful to me in my real program because the phantom type M is reified
-- {-# SPECIALIZE plusFastCyc ::
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int #-}
plusFastCyc :: (Num (t r)) => (FastCyc t r) -> (FastCyc t r) -> (FastCyc t r)
plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2
El Main.hsarchivo tiene dos controladores: vtTestque se ejecuta en ~ 3 segundos y fcTestque se ejecuta en ~ 83 segundos cuando se compila con -O3 utilizando la forallespecialización 'd.
El núcleo muestra que para la vtTestprueba, el código de adición se está especializando en Unboxedvectores sobre Ints, etc., mientras que se usa el código vectorial genérico fcTest. En la línea 10, se puede ver que no GHC escribir una versión especializada de plusFastCyc, en comparación con la versión genérica de la línea 167. La regla para la especialización está en la línea 225. Creo que esta regla debe disparar en la línea 270. ( main6llamadas iterate main8 y, por lo que main8es donde plusFastCycdebe estar especializado)
Mi objetivo es hacerlo fcTesttan rápido como vtTestespecializándome plusFastCyc. He encontrado dos formas de hacer esto:
- Llamada explícita
inlinedesdeGHC.ExtsadentrofcTest. - Eliminar la
Factored m Intrestricción enplusFastCyc.
La opción 1 no es satisfactoria porque en la base del código real plusFastCyces una operación de uso frecuente y una función muy grande, por lo que no se debe incluir en cada uso. Más bien, GHC debería llamar a una versión especializada de plusFastCyc. La opción 2 no es realmente una opción porque necesito la restricción en el código real.
He probado una variedad de opciones usando (y no usar) INLINE, INLINABLEy SPECIALIZE, pero nada parece funcionar. ( EDITAR : puede que me haya quitado demasiado plusFastCycpara hacer que mi ejemplo sea pequeño, por lo que INLINEpodría hacer que la función esté en línea. Esto no sucede en mi código real porque plusFastCyces muy grande). En este ejemplo en particular, no estoy obteniendo alguna match_co: needs more caseso RULE: LHS too complicated to desugar(y aquí ) advertencias, aunque recibí muchas match_coadvertencias antes de minimizar el ejemplo. Presumiblemente, el "problema" es la Factored m Intrestricción en la regla; si hago cambios a esa restricción, se fcTestejecuta tan rápido como vtTest.
¿Estoy haciendo algo que a GHC simplemente no le gusta? ¿Por qué no se especializa GHC plusFastCycy cómo puedo hacerlo?
ACTUALIZAR
El problema persiste en GHC 7.8.2, por lo que esta pregunta sigue siendo relevante.

m, a saberM. Esto hizo el trabajo, pero no puedo especializarme para tipos fantasmas específicos en el programa real ya que están reificados.Respuestas:
GHC también ofrece una opción para
SPECIALIZEuna declaración de instancia de tipo de clase. Intenté esto con el código (expandido) deFoo.hs, poniendo lo siguiente:Sin embargo, este cambio no logró la aceleración deseada. Lo que logró esa mejora en el rendimiento fue agregar manualmente una instancia especializada para el tipo
VT U.Vector m Intcon las mismas definiciones de funciones, como sigue:Esto requiere la adición
OverlappingInstancesyFlexibleInstancesenLANGUAGE.Curiosamente, en el programa de ejemplo, la aceleración obtenida con la instancia superpuesta permanece incluso si elimina todo
SPECIALIZEyINLINABLEpragma.fuente