私はいくつかの Criterion ベンチマークを実行して、モナド スタック上でコードを実行することによって失われるパフォーマンスを推定しました。結果はかなり興味深いものでした。おそらく、ベンチマークで怠惰の落とし穴に出くわしたことでしょう。
ベンチマークによると、 を使用していない場合でも、WriterT String IO
実行は plain の実行よりも 20 倍 (!) 遅いことがわかります。奇妙なことに、スタックすると5 倍遅くなります。これはおそらく私のベンチマークのバグです。ここで何が間違っていますか?IO
tell
WriterT
ReaderT
ContT
ベンチマーク
{-#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 ミリ秒、lb 1.846508 ミリ秒、ub 2.052165 ミリ秒、ci 0.950 標準偏差: 519.7248 us、lb 428.4684 us、ub 709.3670 us、ci 0.950 ベンチマークライター 平均: 39.50431 ミリ秒、lb 38.25233 ミリ秒、ub 40.74437 ミリ秒、ci 0.950 標準偏差: 6.378220 ミリ秒、lb 5.738682 ミリ秒、ub 7.155760 ミリ秒、ci 0.950 ベンチマークリーダー 平均: 12.52823 ミリ秒、lb 12.03947 ミリ秒、ub 13.09994 ミリ秒、ci 0.950 標準偏差: 2.706265 ミリ秒、lb 2.324519 ミリ秒、ub 3.462641 ミリ秒、ci 0.950 ベンチマーク 続き 平均: 8.100272 ミリ秒、lb 7.634488 ミリ秒、ub 8.633348 ミリ秒、ci 0.950 標準偏差: 2.562829 ミリ秒、lb 2.281561 ミリ秒、ub 2.878463 ミリ秒、ci 0.950 RWC のベンチマーク 平均: 9.871992 ミリ秒、lb 9.436721 ミリ秒、ub 10.37302 ミリ秒、ci 0.950 標準偏差: 2.387364 ミリ秒、lb 2.136819 ミリ秒、ub 2.721750 ミリ秒、ci 0.950