Haskell中奇怪的执行时间

时间:2015-11-28 09:02:44

标签: haskell functional-programming

我定义了一个记忆版的阶乘函数。我观察到第二次再次使用相同的参数运行时,执行时间得到了极大的改善。但令我困惑的是,它们都比阶乘函数本身慢得多。例如,以下程序的一次运行

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快得多?有什么解释吗?

1 个答案:

答案 0 :(得分:1)

我同意丹尼尔,就像我讨厌(/ s)一样,查找链表中第n个元素的速度很慢,因此不是正确的记忆方式。一种解决方案是使用map或trie,但由于这已经完成,我们可以利用hackage上的许多trie memoization包中的一个。

切入追逐:

  • fact需要〜 300 us
  • 您的列表记忆需要〜 1000到2000我们
  • 使用MemoTrie会导致运行时间为〜 0.4 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)