Tout d'abord, c'est génial. Cependant, j'ai rencontré une situation où mes benchmarks ont donné des résultats bizarres. Je suis nouveau en Haskell, et c'est la première fois que je me salis les mains avec des tableaux mutables et des Monads. Le code ci-dessous est basé sur cet exemple .
J'ai écrit un monadique générique for
qui prend des nombres et une fonction d'échelon plutôt qu'une plage (comme forM_
fait). J'ai comparé en utilisant mon for
(boucle A) contre l'intégration d'une fonction récursive équivalente (boucle B). La boucle A est nettement plus rapide que la boucle B. Plus étrange encore, la combinaison des boucles A et B est plus rapide que la boucle B seule (mais légèrement plus lente que la boucle A seule).
Je pense à quelques explications possibles pour ces divergences. Notez qu'il ne s'agit que de suppositions :
- Quelque chose que je n'ai pas encore appris sur la façon dont Haskell extrait les résultats des fonctions monadiques.
- La boucle B défaille le tableau d'une manière moins efficace en termes de cache que la boucle A. Pourquoi ?
- J'ai fait une erreur stupide ; la boucle A et la boucle B sont en fait différentes.
- Notez que dans les trois cas de boucles A et B, le programme produit la même sortie.
Voici le code. Je l'ai testé avec ghc -O2 for.hs
en utilisant GHC version 6.10.4 .
import Control.Monad
import Control.Monad.ST
import Data.Array.IArray
import Data.Array.MArray
import Data.Array.ST
import Data.Array.Unboxed
for :: (Num a, Ord a, Monad m) => a -> a -> (a -> a) -> (a -> m b) -> m ()
for start end step f = loop start where
loop i
| i <= end = do
f i
loop (step i)
| otherwise = return ()
primesToNA :: Int -> UArray Int Bool
primesToNA n = runSTUArray $ do
a <- newArray (2,n) True :: ST s (STUArray s Int Bool)
let sr = floor . (sqrt::Double->Double) . fromIntegral $ n+1
-- Loop A
for 4 n (+ 2) $ \j -> writeArray a j False
-- Loop B
let f i
| i <= n = do
writeArray a i False
f (i+2)
| otherwise = return ()
in f 4
forM_ [3,5..sr] $ \i -> do
si <- readArray a i
when si $
forM_ [i*i,i*i+i+i..n] $ \j -> writeArray a j False
return a
primesTo :: Int -> [Int]
primesTo n = [i | (i,p) <- assocs . primesToNA $ n, p]
main = print $ primesTo 30000000