为什么专门使用Identity的monadic函数比手动编写它的效率低?

时间:2017-09-14 13:39:26

标签: performance haskell monads higher-order-functions

我需要两个高阶函数,一个采用副作用自由函数,另一个采用monadic函数作为参数:

frobM :: Monad m => (a -> m a) -> b -> m b
frob :: (a -> a) -> b -> b

它们非常相似,第二个函数可以用第一个表示:

frob f = runIdentity . frobM (return . f)

我希望这与手写“frob”同样有效,因为Identity是一个应该在编译时删除的新类型。然而,无论如何,性能仍然受到影响,正如这一标准微基准所证明的那样:

import Criterion.Main (bench, defaultMain, nf)
import Data.Functor.Identity (Identity (runIdentity))

main :: IO ()
main = defaultMain
       [ bench "frob"  $ nf (frob (+23)) 42
       , bench "frob'" $ nf (frob' (+23)) 42
       ]

-- | Generalized function
frobM :: Monad m => (Int -> m Int) -> Int -> m Int
frobM f n = f $ 2^n `mod` 1024

-- | Specialized function based on the general function
frob' :: (Int -> Int) -> Int -> Int
frob' f = runIdentity . frobM (return . f)

-- | "Hand written" specialized function.
frob :: (Int -> Int) -> Int -> Int
frob f n = f $ 2^n `mod` 1024

输出:

benchmarking frob
time                 676.1 ns   (673.5 ns .. 678.9 ns)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 675.7 ns   (673.3 ns .. 677.3 ns)
std dev              6.600 ns   (5.984 ns .. 7.324 ns)

benchmarking frob'
time                 706.2 ns   (705.0 ns .. 707.9 ns)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 705.7 ns   (704.5 ns .. 707.4 ns)
std dev              4.630 ns   (3.729 ns .. 6.226 ns)

造成这种情况的原因是什么?有没有办法让frob'frob一样高效?

0 个答案:

没有答案