J’ai des problèmes pour que GHC spécialise une fonction avec une contrainte de classe. J’ai un exemple minimal de mon problème ici: Foo.hs et Main.hs. Les deux fichiers comstacknt (GHC 7.6.2, ghc -O3 Main
) et s’exécutent.
NOTE: 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 apporte de nombreuses modifications mineures, GHC insiste simplement sur l’appel à plusFastCyc
. Cela ne se produira pas dans le code réel, car plusFastCyc
est trop grand pour que GHC soit en ligne, même s’il est marqué INLINE
. Le point est de spécialiser l’appel à plusFastCyc
, pas de l’inline. plusFastCyc
est appelé à de nombreux endroits dans le code réel, donc dupliquer une telle fonction ne serait pas souhaitable, même si je pouvais forcer GHC à le faire.
Le code d’intérêt est le plusFastCyc
dans 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 (tr)) => (FastCyc tr) -> (FastCyc tr) -> (FastCyc tr) plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2
Le fichier Main.hs
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 spécialisation forall
‘d.
Le kernel montre que pour le test vtTest
, le code d’addition est spécialisé dans les vecteurs Unboxed
sur Int
s, etc., tandis que le code vectoriel générique est utilisé pour fcTest
. Sur la ligne 10, vous pouvez voir que GHC écrit une version spécialisée de plusFastCyc
, comparée à la version générique de la ligne 167. La règle pour la spécialisation se trouve à la ligne 225. Je pense que cette règle devrait main6
sur la ligne 270. iterate main8 y
, alors main8
est l’endroit où plusFastCyc
devrait être spécialisé.)
Mon but est de rendre fcTest
aussi rapide que vtTest
en spécialisant plusFastCyc
. J’ai trouvé deux façons de le faire:
GHC.Exts
en fcTest
. plusFastCyc
Factored m Int
sur plusFastCyc
. L’option 1 n’est pas satisfaisante car dans la base de code actuelle, 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 utilisant (et n’utilisant pas) INLINE
, INLINABLE
et SPECIALIZE
, mais rien ne semble fonctionner. ( EDIT : J’ai peut-être enlevé trop de plusFastCyc
pour que mon exemple soit petit, donc INLINE
pourrait faire en sorte que la fonction soit intégrée. Cela ne se produit pas dans mon code réel, car plusFastCyc
est si grand.) n’obtiens aucun match_co: needs more cases
ou RULE: LHS too complicated to desugar
(et ici ) des avertissements, bien que je reçois beaucoup d’avertissements match_co
avant de minimiser l’exemple. Vraisemblablement, le “problème” est la contrainte Factored m Int
dans la règle; Si je modifie cette contrainte, fcTest
s’exécute aussi vite que vtTest
.
Est-ce que je fais quelque chose que GHC n’aime pas? Pourquoi GHC ne spécialise-t-il pas le plusFastCyc
et comment puis-je le faire?
METTRE À JOUR
Le problème persiste dans GHC 7.8.2, donc cette question est toujours pertinente.
GHC offre également une option pour SPECIALIZE
une déclaration d’instance de classe de type. J’ai essayé ceci avec le code (étendu) de Foo.hs
, en mettant ce qui suit:
instance (Num r, V.Vector vr, Factored mr) => Num (VT vmr) where {-# SPECIALIZE instance ( Factored m Int => Num (VT U.Vector m Int)) #-} VT x + VT y = VT $ V.zipWith (+) xy
Ce changement n’a cependant pas permis d’accélérer le rythme. L’amélioration des performances a permis d’append manuellement une instance spécialisée pour le type VT U.Vector m Int
avec les mêmes définitions de fonction, comme suit:
instance (Factored m Int) => Num (VT U.Vector m Int) where VT x + VT y = VT $ V.zipWith (+) xy
Cela nécessite l’ajout de OverlappingInstances
et de FlexibleInstances
dans LANGUAGE
.
Il est intéressant de noter que dans l’exemple de programme, l’accélération obtenue avec l’instance de chevauchement persiste même si vous supprimez chaque pragma SPECIALIZE
et INLINABLE
.