Haskell 分析

Posted

技术标签:

【中文标题】Haskell 分析【英文标题】:Haskell Profiling 【发布时间】:2014-07-26 17:50:04 【问题描述】:

我正在浏览 LYAH,并且一直在研究在处理列表时使用列表理解与映射/过滤器。我已经分析了以下两个函数,并且还包括了 prof 输出。如果我正确地阅读了教授 ID,则表示 FiltB 的运行速度比 FiltA 慢很多(尽管它只有千分之一秒)。

说这是因为FiltB 必须评估x^2 两次是否正确?

FiltA.hs (filter odd)

-- FiltA.hs

module Main
    where

main = do
    let x = sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
    print x
    Sat Jul 26 18:26 2014 Time and Allocation Profiling Report  (Final)

       Filta.exe +RTS -p -RTS

    total time  =        0.00 secs   (0 ticks @ 1000 us, 1 processor)
    total alloc =      92,752 bytes  (excludes profiling overheads)

COST CENTRE MODULE           %time %alloc

main        Main               0.0   10.1
main.x      Main               0.0   53.0
CAF         GHC.IO.Handle.FD   0.0   36.3


                                                         individual     inherited
COST CENTRE MODULE                     no.     entries  %time %alloc   %time %alloc

MAIN        MAIN                        37           0    0.0    0.2     0.0  100.0
 CAF        GHC.IO.Encoding.CodePage    61           0    0.0    0.1     0.0    0.1
 CAF        GHC.IO.Encoding             58           0    0.0    0.1     0.0    0.1
 CAF        GHC.IO.Handle.FD            52           0    0.0   36.3     0.0   36.3
 CAF        Main                        44           0    0.0    0.2     0.0   63.3
  main      Main                        74           1    0.0   10.1     0.0   63.1
   main.x   Main                        75           1    0.0   53.0     0.0   53.0

FiltB(列表推导)

-- FiltB.hs

module Main
    where

main = do
    let x = sum (takeWhile (<10000) [n^2 | n <- [1..], odd (n^2)])
    print x
    Sat Jul 26 18:30 2014 Time and Allocation Profiling Report  (Final)

       FiltB.exe +RTS -p -RTS

    total time  =        0.00 secs   (2 ticks @ 1000 us, 1 processor)
    total alloc =     107,236 bytes  (excludes profiling overheads)

COST CENTRE MODULE           %time %alloc

main        Main              50.0    8.8
CAF         Main              50.0    0.1
main.x      Main               0.0   59.4
CAF         GHC.IO.Handle.FD   0.0   31.4


                                                         individual     inherited
COST CENTRE MODULE                     no.     entries  %time %alloc   %time %alloc

MAIN        MAIN                        37           0    0.0    0.2   100.0  100.0
 CAF        GHC.IO.Encoding.CodePage    61           0    0.0    0.1     0.0    0.1
 CAF        GHC.IO.Encoding             58           0    0.0    0.0     0.0    0.0
 CAF        GHC.IO.Handle.FD            52           0    0.0   31.4     0.0   31.4
 CAF        Main                        44           0   50.0    0.1   100.0   68.3
  main      Main                        74           1   50.0    8.8    50.0   68.1
   main.x   Main                        75           1    0.0   59.4     0.0   59.4

【问题讨论】:

【参考方案1】:

是的。在这种特殊情况下,由于n^2 将是奇数当且仅当n 为奇数时,您可以通过将odd (n^2) 替换为odd n 来将FiltB 加速到与FiltA 相同的速度。

就像你说的那样,问题是对于每个元素 n 都对其进行平方,检查是否这是奇数,如果是,则平方 n 并将其添加到列表中。

更一般地说,不同之处在于,在列表推导中,过滤发生在您映射之前,而使用映射和过滤器,您可以选择顺序。因此,如果您实际要根据映射后列表中的值进行过滤,则使用 map 和 filter 可能是更好的选择。你仍然可以做这样的事情来根据 squared 值是否为奇数进行过滤:

sum (takeWhile (<10000) [ x | x <- [ n^2 | n <- [1..] ], odd x ])

但这变得很难阅读。显式地映射和过滤或过滤列表理解(即filter odd [ n^2 | n &lt;- [1..] ])是更好的选择。

【讨论】:

谢谢,我很确定是这样。我要求澄清的原因是我仍然在理解 Haskells 惰性评估,并且不确定 GHC 是否会进行某种优化,以便只评估一次 它很容易阅读,如果你在理解中使用let[x | n &lt;- [1..], let x = n^2, odd x]。顺便说一句,由于这是关于性能 (@Dave0504),您可以只保留列表的偶数部分,因此完全删除 odd 测试:takeWhile (&lt;10000) [x^2 | x &lt;- [1,3..]] 甚至 [x ^ 2| x &lt;- [1,3..99]

以上是关于Haskell 分析的主要内容,如果未能解决你的问题,请参考以下文章

在 Haskell 分析时排除开销

为啥 Haskell 要求所有库都启用分析功能以进行分析?

编译器优化后如何分析 Haskell?

Haskell,分析导入库的内存使用情况

Haskell 分析

你如何在 Haskell 中安装分析库?