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 plusFastCycest 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. plusFastCycest 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 plusFastCycin 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.hsfichier a deux pilotes:, vtTestqui s'exécute en ~ 3 secondes, et fcTest, qui s'exécute en ~ 83 secondes lorsqu'il est compilé avec -O3 en utilisant la forallspécialisation 'd.
Le noyau montre que pour le vtTesttest, le code d'addition est spécialisé dans les Unboxedvecteurs sur Ints, 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. ( main6appelle iterate main8 y, il en main8est de même où plusFastCycdevrait être spécialisé.)
Mon objectif est de faire fcTestaussi vite qu'en se vtTestspécialisant plusFastCyc. J'ai trouvé deux façons de procéder:
- Appel Explicity
inlinedeGHC.ExtsdansfcTest. - Supprimez la
Factored m Intcontrainte surplusFastCyc.
L'option 1 n'est pas satisfaisante car la base de code réelle plusFastCycest 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, INLINABLEet SPECIALIZE, mais rien ne semble fonctionner. ( EDIT : j'ai peut-être trop supprimé plusFastCycpour rendre mon exemple petit, donc cela INLINEpourrait entraîner l'inclusion de la fonction. Cela ne se produit pas dans mon vrai code car il plusFastCycest si grand.) Dans cet exemple particulier, je ne suis pas obtenir des avertissements match_co: needs more casesou RULE: LHS too complicated to desugar(et ici ), même si je recevais de nombreux match_coavertissements avant de minimiser l'exemple. Vraisemblablement, le «problème» est la Factored m Intcontrainte de la règle; si j'apporte des modifications à cette contrainte, fcTests'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 plusFastCycet 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.