首先,它很棒。但是,我遇到了一个情况,我的基准测试结果出现了奇怪的结果。我是Haskell的新手,这是我第一次用可变阵列和Monads弄脏手。以下代码基于this example。
我写了一个通用的monadic for
函数,它接受数字和步长函数而不是范围(如forM_
那样)。我使用我的通用for
函数(循环A)与嵌入等效递归函数(循环B)进行比较。有一个循环A明显快于循环B. Weirder同时拥有循环A和B的速度比循环B本身快(但比循环A略慢)。
我可以想到一些可能的解释。请注意,这些只是猜测:
这是代码。我使用GHC版本6.10.4使用ghc -O2 for.hs
对其进行了测试。
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
答案 0 :(得分:2)
或许与Shootout nsieve计划进行比较和对比?无论如何,了解真实情况的唯一方法是look at the core(例如the ghc-core tool)。
{-# OPTIONS -O2 -optc-O -fbang-patterns -fglasgow-exts -optc-march=pentium4 #-}
--
-- The Computer Language Shootout
-- http://shootout.alioth.debian.org/
--
-- Contributed by Don Stewart 2005
-- nsieve over an ST monad Bool array
--
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Base
import System
import Control.Monad
import Data.Bits
import Text.Printf
main = do
n <- getArgs >>= readIO . head :: IO Int
mapM_ (\i -> sieve (10000 `shiftL` (n-i))) [0, 1, 2]
sieve n = do
let r = runST (do a <- newArray (2,n) True :: ST s (STUArray s Int Bool)
go a n 2 0)
printf "Primes up to %8d %8d\n" (n::Int) (r::Int) :: IO ()
go !a !m !n !c
| n == m = return c
| otherwise = do
e <- unsafeRead a n
if e then let loop j
| j < m = do
x <- unsafeRead a j
when x $ unsafeWrite a j False
loop (j+n)
| otherwise = go a m (n+1) (c+1)
in loop (n `shiftL` 1)
else go a m (n+1) c
答案 1 :(得分:2)
我只是尝试使用Criterion和GHC 6.12.1对此进行基准测试,而Loop A对我来说只是稍微快一点。我绝对不会得到奇怪的“两者都比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 ]