Я только что попробовал сравнить это с Критерием и GHC 6.12.1, и цикл А выглядит для меня лишь немного быстрее. Я определенно не получаю странный эффект «оба вместе быстрее, чем один B».
Кроме того, если ваша пошаговая функция действительно является просто шагом и ничего не делает с ее аргументом, следующая версия for
выглядит немного быстрее, особенно для небольших массивов:
for' :: (Enum a, Num a, Ord a, Monad m) => a -> a -> (a -> a) -> (a -> m b) -> m ()
for' start end step = forM_ $ enumFromThenTo start (step start) end
Вот результаты Criterion, где loopA'
- ваш цикл A, использующий мои for'
, а loopC
- это и A, и B вместе:
benchmarking loopA...
mean: 2.372893 s, lb 2.370982 s, ub 2.374914 s, ci 0.950
std dev: 10.06753 ms, lb 8.820194 ms, ub 11.66965 ms, ci 0.950
benchmarking loopA'...
mean: 2.368167 s, lb 2.354312 s, ub 2.381413 s, ci 0.950
std dev: 69.50334 ms, lb 65.94236 ms, ub 73.17173 ms, ci 0.950
benchmarking loopB...
mean: 2.423160 s, lb 2.419131 s, ub 2.427260 s, ci 0.950
std dev: 20.78412 ms, lb 18.06613 ms, ub 24.99021 ms, ci 0.950
benchmarking loopC...
mean: 4.308503 s, lb 4.304875 s, ub 4.312110 s, ci 0.950
std dev: 18.48732 ms, lb 16.19325 ms, ub 21.32299 ms, ci 0.950<
А вот код:
module Main where
import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed
import Criterion.Main
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 ()
for' :: (Enum a, Num a, Ord a, Monad m) => a -> a -> (a -> a) -> (a -> m b) -> m ()
for' start end step = forM_ $ enumFromThenTo start (step start) end
loopA arr n = for 4 n (+ 2) $ flip (writeArray arr) False
loopA' arr n = for' 4 n (+ 2) $ flip (writeArray arr) False
loopB arr n =
let f i | i <= n = do writeArray arr i False
f (i+2)
| otherwise = return ()
in f 4
loopC arr n = do
loopA arr n
loopB arr n
runPrimes loop n = do
let sr = floor . (sqrt::Double->Double) . fromIntegral $ n+1
a <- newArray (2,n) True :: (ST s (STUArray s Int Bool))
loop a n
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
primesA n = [i | (i,p) <- assocs $ runSTUArray $ runPrimes loopA n, p]
primesA' n = [i | (i,p) <- assocs $ runSTUArray $ runPrimes loopA' n, p]
primesB n = [i | (i,p) <- assocs $ runSTUArray $ runPrimes loopB n, p]
primesC n = [i | (i,p) <- assocs $ runSTUArray $ runPrimes loopC n, p]
main = let n = 10000000 in
defaultMain [ bench "loopA" $ nf primesA n
, bench "loopA'" $ nf primesA' n
, bench "loopB" $ nf primesB n
, bench "loopC" $ nf primesC n ]