我定义了一个记忆版的阶乘函数。我观察到第二次再次使用相同的参数运行时,执行时间得到了极大的改善。但令我困惑的是,它们都比阶乘函数本身慢得多。例如,以下程序的一次运行
import Text.Printf
import Control.Exception
import System.CPUTime
-- test function
fact :: Int -> Int
fact n = product [1..n]
mem :: (Int -> Int) -> Int -> Int
mem f = (map f [0..] !!)
mem_fact = mem fact
time :: IO t -> IO t
time a = do
start <- getCPUTime
v <- a
end <- getCPUTime
let diff = (fromIntegral (end - start)) / (10^12)
printf "Computation time: %0.3f sec\n" (diff :: Double)
return v
main = do
putStrLn "Starting..."
time $ fact 3000000 `seq` return ()
time $ mem_fact 3000000 `seq` return ()
time $ mem_fact 3000000 `seq` return ()
putStrLn "Done."
产生以下输出:
Starting...
Computation time: 0.008 sec
Computation time: 0.621 sec
Computation time: 0.047 sec
Done.
为什么调用fact
的速度比mem_fact
快得多?有什么解释吗?
答案 0 :(得分:1)
我同意丹尼尔,就像我讨厌(/ s)一样,查找链表中第n个元素的速度很慢,因此不是正确的记忆方式。一种解决方案是使用map或trie,但由于这已经完成,我们可以利用hackage上的许多trie memoization包中的一个。
切入追逐:
fact
需要〜 300 us 这是通过使用您的代码和memotrie与Criterion确定的:
import Data.MemoTrie
import Criterion.Main
end :: Int
end = 300000
tmdMemo :: Int -> Int
tmdMemo x = memo2 go 1 x
where
go :: Int -> Int -> Int
go acc 1 = acc
go acc n = let new = n*acc in new `seq` memo2 go new (n-1)
-- test function
fact :: Int -> Int
fact n = product [1..n]
mem :: (Int -> Int) -> Int -> Int
mem f = (map f [0..] !!)
gaoMemo :: Int -> Int
gaoMemo = mem fact
main :: IO ()
main = defaultMain [ bench "Normal" $ nf fact end
, bench "TMD-Memo" $ nf tmdMemo end
, bench "Gao-Memo" $ nf gaoMemo end
]
结果:
benchmarking Normal
time 270.7 μs (268.8 μs .. 272.7 μs)
1.000 R² (1.000 R² .. 1.000 R²)
mean 271.7 μs (270.4 μs .. 273.6 μs)
std dev 4.997 μs (3.971 μs .. 6.812 μs)
variance introduced by outliers: 11% (moderately inflated)
benchmarking TMD-Memo
time 379.3 ns (376.2 ns .. 382.3 ns)
0.999 R² (0.999 R² .. 1.000 R²)
mean 381.5 ns (378.0 ns .. 386.3 ns)
std dev 13.66 ns (10.74 ns .. 17.59 ns)
variance introduced by outliers: 52% (severely inflated)
benchmarking Gao-Memo
time 1.439 ms (1.408 ms .. 1.469 ms)
0.996 R² (0.994 R² .. 0.998 R²)
mean 1.446 ms (1.430 ms .. 1.467 ms)
std dev 63.31 μs (53.75 μs .. 74.26 μs)
variance introduced by outliers: 31% (moderately inflated)