为什么一种生成二进制序列的方法比另一种更快?

时间:2014-06-24 11:52:59

标签: haskell

我想生成长度为n的所有可能的二进制序列作为列表。我想出了两种方法。我更喜欢第二种,因为它似乎更容易阅读,但我不知道为什么它比第一种慢得多。

bin_seqs_1 :: Int -> [[Int]]
bin_seqs_1 n = iterate add_bit [[]] !! n
    where add_bit seqs = [b : seq | seq <- seqs, b <- [-1,1]]

bin_seqs_2 :: Int -> [[Int]]
bin_seqs_2 n = sequence $ replicate n [-1,1]

main :: IO ()
main = putStrLn $ show $ bin_seqs_2 23

当我使用优化进行编译并使用bin_seqs_1运行(将输出重定向到/ dev / null)时,需要13秒。使用bin_seqs_2,需要29秒。

为什么第二种方法这么慢?

编辑: 所以它与sequence的实现有关。如果我重新定义序列以首先评估列表的尾部,那么头部,性能可与bin_seqs_1相比

bin_seqs_3 n = sequence $ replicate n [-1,1]
    where sequence ms = foldr k (return []) ms
          k m m' = do { xs <- m'; x <- m; return (x:xs) }

但我仍然不明白为什么,在“k”函数中,评估xs然后x比评估x然后xs快得多(就像在Control.Monad中那样)。

1 个答案:

答案 0 :(得分:1)

这是部分答案“为什么第二种方法这么慢?”

当对列表融合的性能有疑问时,请使用coredump,Luke!这很容易。

给定test.hs

module Test (_1_bin_seqs, _2_bin_seqs) where

_1_bin_seqs :: Int -> [[Int]]
_1_bin_seqs n = iterate add_bit [[]] !! n
    where add_bit seqs = [b : seq | seq <- seqs, b <- [-1,1]]

_2_bin_seqs :: Int -> [[Int]]
_2_bin_seqs n = sequence $ replicate n [-1,1]

你必须运行

$ ghc test.hs -fforce-recomp -O2 -ddump-simpl -dsuppress-all
  • -fforce-recomp - 始终重建
  • -O2 - 首选优化级别
  • -ddump-simpl - 将核心代码输出到stdout
  • -dsuppress-all - 让人类可读的输出
  • -ddump-rule-rewrites - (可选)显示使用的代码重写规则

评论模块导入可以控制coredump的功能。您会看到GHC将第一个版本转换为iterate +循环,而第二个版本并不那么甜。