J'ai des problèmes pour que GHC spécialise une fonction avec une contrainte de classe. J'ai ici un exemple minimal de mon problème: Foo.hs et Main.hs . Les deux fichiers se compilent (GHC 7.6.2, ghc -O3 Main
) et s'exécutent.
REMARQUE:
Foo.hs
est vraiment dépouillé. Si vous voulez voir pourquoi la contrainte est nécessaire, vous pouvez voir un peu plus de code ici . Si je mets le code dans un seul fichier ou que j'apporte de nombreux autres changements mineurs, GHC intègre simplement l'appel à plusFastCyc
. Cela ne se produira pas dans le code réel car il plusFastCyc
est trop volumineux pour que GHC puisse être intégré, même lorsqu'il est marqué INLINE
. Le but est de spécialiser l'appel à plusFastCyc
, et non pas en ligne. plusFastCyc
est appelé à de nombreux endroits dans le code réel, donc dupliquer une fonction aussi volumineuse ne serait pas souhaitable même si je pouvais forcer GHC à le faire.
Le code d'intérêt est le plusFastCyc
in Foo.hs
, reproduit ici:
{-# 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
Le Main.hs
fichier a deux pilotes:, vtTest
qui s'exécute en ~ 3 secondes, et fcTest
, qui s'exécute en ~ 83 secondes lorsqu'il est compilé avec -O3 en utilisant la forall
spécialisation 'd.
Le noyau montre que pour le vtTest
test, le code d'addition est spécialisé dans les Unboxed
vecteurs sur Int
s, etc., tandis que le code vectoriel générique est utilisé pour fcTest
. À la ligne 10, vous pouvez voir que GHC écrit une version spécialisée de plusFastCyc
, par rapport à la version générique à la ligne 167. La règle de spécialisation est à la ligne 225. Je crois que cette règle devrait être déclenchée à la ligne 270. ( main6
appelle iterate main8 y
, il en main8
est de même où plusFastCyc
devrait être spécialisé.)
Mon objectif est de faire fcTest
aussi vite qu'en se vtTest
spécialisant plusFastCyc
. J'ai trouvé deux façons de procéder:
- Appel Explicity
inline
deGHC.Exts
dansfcTest
. - Supprimez la
Factored m Int
contrainte surplusFastCyc
.
L'option 1 n'est pas satisfaisante car la base de code réelle plusFastCyc
est une opération fréquemment utilisée et une très grande fonction, elle ne doit donc pas être intégrée à chaque utilisation. Au contraire, GHC devrait appeler une version spécialisée de plusFastCyc
. L'option 2 n'est pas vraiment une option car j'ai besoin de la contrainte dans le code réel.
J'ai essayé une variété d'options en utilisant (et non à l' aide) INLINE
, INLINABLE
et SPECIALIZE
, mais rien ne semble fonctionner. ( EDIT : j'ai peut-être trop supprimé plusFastCyc
pour rendre mon exemple petit, donc cela INLINE
pourrait entraîner l'inclusion de la fonction. Cela ne se produit pas dans mon vrai code car il plusFastCyc
est si grand.) Dans cet exemple particulier, je ne suis pas obtenir des avertissements match_co: needs more cases
ou RULE: LHS too complicated to desugar
(et ici ), même si je recevais de nombreux match_co
avertissements avant de minimiser l'exemple. Vraisemblablement, le «problème» est la Factored m Int
contrainte de la règle; si j'apporte des modifications à cette contrainte, fcTest
s'exécute aussi vite que vtTest
.
Est-ce que je fais quelque chose que GHC n'aime tout simplement pas? Pourquoi GHC ne se spécialise-t-il pas plusFastCyc
et comment puis-je le faire?
METTRE À JOUR
Le problème persiste dans GHC 7.8.2, donc cette question est toujours d'actualité.
m
, à savoirM
. Cela a fait le travail, mais je ne peux pas me spécialiser pour des types fantômes spécifiques dans le programme réel car ils sont réifiés.