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 <- [1..] ]
)是更好的选择。
【讨论】:
谢谢,我很确定是这样。我要求澄清的原因是我仍然在理解 Haskells 惰性评估,并且不确定 GHC 是否会进行某种优化,以便只评估一次 它很容易阅读,如果你在理解中使用let
:[x | n <- [1..], let x = n^2, odd x]
。顺便说一句,由于这是关于性能 (@Dave0504),您可以只保留列表的偶数部分,因此完全删除 odd
测试:takeWhile (<10000) [x^2 | x <- [1,3..]]
甚至 [x ^ 2| x <- [1,3..99]
。以上是关于Haskell 分析的主要内容,如果未能解决你的问题,请参考以下文章