我做了一些 Criterion 基准测试来估计通过在 monad 堆栈上运行我的代码会损失多少性能。结果相当奇怪,我可能在我的基准测试中偶然发现了一些懒惰的陷阱。
基准测试告诉我,运行WriterT String IO
比普通运行慢 20 倍(!)IO
,即使不使用tell
. 奇怪的是,如果我堆叠WriterT
起来ReaderT
,ContT
它只会慢 5 倍。这可能是我的基准测试中的一个错误。我在这里做错了什么?
基准
{-#LANGUAGE BangPatterns#-}
module Main where
import Criterion.Main
import Control.Monad
import Control.Monad.Writer
import Control.Monad.Reader
import Control.Monad.Cont
process :: Monad m => Int -> m Int
process = foldl (>=>) return (replicate 100000 (\(!x) -> return (x+1)))
test n = process n >> return ()
main = defaultMain [
bench "Plain" t0
,bench "Writer" t1
,bench "Reader" t2
,bench "Cont" t3
,bench "RWC" t4
]
t0 = test 1 :: IO ()
t1 = (runWriterT (test 1:: WriterT String IO ()) >> return ()) :: IO ()
t2 = (runReaderT (test 1:: ReaderT String IO ()) "" >> return ()) :: IO ()
t3 = (runContT (test 1:: ContT () IO ()) (return) >> return ()) :: IO ()
t4 = ((runWriterT . flip runReaderT "" . flip runContT return $
(test 1 :: ContT () (ReaderT String (WriterT String IO)) ())) >> return ()) :: IO ()
结果
标杆平原 平均值:1.938814 ms,lb 1.846508 ms,ub 2.052165 ms,ci 0.950 标准开发:519.7248 us,lb 428.4684 us,ub 709.3670 us,ci 0.950 基准测试作家 平均值:39.50431 ms,lb 38.25233 ms,ub 40.74437 ms,ci 0.950 标准开发:6.378220 毫秒,磅 5.738682 毫秒,ub 7.155760 毫秒,ci 0.950 基准阅读器 平均值:12.52823 ms,lb 12.03947 ms,ub 13.09994 ms,ci 0.950 标准开发:2.706265 毫秒,磅 2.324519 毫秒,ub 3.462641 毫秒,ci 0.950 基准测试续 平均值:8.100272 ms,lb 7.634488 ms,ub 8.633348 ms,ci 0.950 标准开发:2.562829 毫秒,磅 2.281561 毫秒,ub 2.878463 毫秒,ci 0.950 基准 RWC 平均值:9.871992 ms,lb 9.436721 ms,ub 10.37302 ms,ci 0.950 标准开发:2.387364 ms,lb 2.136819 ms,ub 2.721750 ms,ci 0.950