Je suis dans le milieu de portage de David Blei d'origine C mise en œuvre de Latent Dirichlet Allocation de Haskell, et je suis en train de décider de quitter le faible niveau de stuff en C. La fonction suivante est un exemple-c'est une approximation de la dérivée seconde de l' lgamma
:
double trigamma(double x)
{
double p;
int i;
x=x+6;
p=1/(x*x);
p=(((((0.075757575757576*p-0.033333333333333)*p+0.0238095238095238)
*p-0.033333333333333)*p+0.166666666666667)*p+1)/x+0.5*p;
for (i=0; i<6 ;i++)
{
x=x-1;
p=1/(x*x)+p;
}
return(p);
}
J'ai traduit cela en plus ou moins idiomatiques Haskell comme suit:
trigamma :: Double -> Double
trigamma x = snd $ last $ take 7 $ iterate next (x' - 1, p')
where
x' = x + 6
p = 1 / x' ^ 2
p' = p / 2 + c / x'
c = foldr1 (\a b -> (a + b * p)) [1, 1/6, -1/30, 1/42, -1/30, 5/66]
next (x, p) = (x - 1, 1 / x ^ 2 + p)
Le problème est que quand je lance la fois par Critère, mon Haskell version est de six ou sept fois plus lent (je suis de la compilation avec -O2
sur le GHC 6.12.1). Certaines des fonctions similaires sont encore pire.
Je connais pratiquement rien au sujet de Haskell performance, et je ne suis pas vraiment intéressée à creuser par le biais de Cœur ou quelque chose comme ça, car je peux toujours appeler la poignée de math-intensif C fonctions par le biais de FFI.
Mais je suis curieux de savoir si il y a des branches basses de fruits que je suis en manque, une sorte d'extension ou de la bibliothèque ou de l'annotation que je pourrais utiliser pour accélérer le numérique trucs sans en faire trop laid.
Mise à JOUR: Voici les deux meilleures solutions, grâce à Don Stewart et Yitz. J'ai modifié Yitz de réponse légèrement à utiliser Data.Vector
.
invSq x = 1 / (x * x)
computeP x = (((((5/66*p-1/30)*p+1/42)*p-1/30)*p+1/6)*p+1)/x+0.5*p
where p = invSq x
trigamma_d :: Double -> Double
trigamma_d x = go 0 (x + 5) $ computeP $ x + 6
where
go :: Int -> Double -> Double -> Double
go !i !x !p
| i >= 6 = p
| otherwise = go (i+1) (x-1) (1 / (x*x) + p)
trigamma_y :: Double -> Double
trigamma_y x = V.foldl' (+) (computeP $ x + 6) $ V.map invSq $ V.enumFromN x 6
Les performances des deux semble être presque exactement la même, avec l'un ou l'autre de gagner par un point de pourcentage ou deux selon les drapeaux du compilateur.
Comme camccann dit plus sur Reddit, la morale de l'histoire est "Pour de meilleurs résultats, utilisez Don Stewart comme votre GHC backend générateur de code." Sauf que la solution, le pari le plus sûr semble être simplement de traduire la C structures de contrôle directement dans Haskell, bien que la boucle de fusion peut donner des performances similaires à un plus idiomatique.
Je vais probablement jusqu'à la fin à l'aide de l' Data.Vector
approche dans mon code.