【发布时间】:2014-09-01 15:31:18
【问题描述】:
我正在尝试优化我的程序的执行速度,我遇到了一些有趣的结果,希望有人能回答。似乎对我的一个列表推导进行小改动会极大地改变执行速度,但我不知道为什么。
这是我现在的程序。
import Data.Ord
import Control.Monad
import Data.Array
import Data.Ix
import qualified Data.Map as M
import qualified Data.Set as S
import Data.List (minimumBy, foldl')
arrayMatrix lists = let rlen = length lists
clen = length $ head lists
r = ((1,1), (rlen, clen))
in array r . zip (range r) $ concat lists
a_star start goal h m = search S.empty (S.singleton start)
(M.singleton start (m ! start))
$ M.singleton start (m ! start + h ! start)
where neighbors (r,c) = filter (inRange $ bounds m) [ (r-1,c), (r,c+1), (r+1,c) , (r,c-1)]
search closed open gs fs
| S.null open = 0
| current == goal = gs M.! goal
| otherwise = let open' = S.delete current open
closed' = S.insert current closed
neighbs = [(n, ts) | n <- neighbors current, S.notMember n closed
, let ts = gs M.! current + m ! n ]
actionable = filter (\(n,ts) -> S.notMember n open' || ts < (gs M.! n)) neighbs
(op',gs',fs') = foldl' (\(o,ng,nf) (n,ts) -> (S.insert n o, M.insert n ts ng, M.insert n (ts + h ! n) nf)) (open',gs,fs) actionable
in search closed' op' gs' fs'
where current = minimumBy (comparing (fs M.!)) $ S.toList open
main = do
matrix <- liftM (arrayMatrix . map (read . ('[':) . (++"]")) . lines)
$ readFile "matrix.txt"
let bds = bounds matrix
ulim = snd bds
heuristic = let m = minimum $ elems matrix
in listArray bds . map (\(r,c) -> (uncurry (+) ulim)-r-c) $ range bds
print $ a_star (1,1) ulim heuristic matrix
现在程序在我的电脑上运行约 350 毫秒(使用 GHC 7.8.2 -O2 编译),matrix.txt 由 Project Euler 提供。
如果我改变邻居
neighbs = [(n, ts) | n <- neighbors current, S.notMember n closed
, let ts = gs M.! current + m ! n ]
到
neighbs = [(n, gs M.! current + m ! n) | n <- neighbors current, S.notMember n closed]
执行时间增加到超过 1 秒。
其他小的更改,例如将下一行的过滤器移动到列表推导中会产生相同的结果:~1sec。
谁能解释为什么会这样?
编辑:这似乎不会发生在早期版本的 GHC 上。我尝试了 GHC 7.6.3,每一个的表现都差不多。
按照cdk 的建议,我已经包含了运行ghc -O2 -ddump-simpl -dsuppress-all 的转储。我真的不知道我在看什么,所以如果有人能够解释,那将是一个很大的帮助,谢谢。
EDIT2(对 Priyatham 的回应):我认为情况并非如此。我变了
neighbs = [(n, ts) | n <- neighbors current, S.notMember n closed
, let ts = gs M.! current + m ! n ]
actionable = filter ((n,ts) -> S.notMember n open' || ts < (gs M.! n)) neighbs
到
neighbs = [(n, gs M.! current + m ! n) | n <- neighbors current, S.notMember n closed ]
actionable = filter ((n,!ts) -> S.notMember n open' || ts < (gs M.! n)) neighbs
使用 BangPatterns,它仍然运行一秒钟多一点。实际上,从
修改 neigbsneighbs = [(n, ts) | n <- neighbors current, S.notMember n closed
, let ts = gs M.! current + m ! n ]
到
neighbs = [(n, ts) | n <- neighbors current, S.notMember n closed
, let !ts = gs M.! current + m ! n ] -- Added bang before ts
将运行时间也增加到 1 秒以上。
【问题讨论】:
-
我正在尝试重现它。你能把你的
matrix.txt粘贴到某个地方吗? -
啊忘记了,here你去吧。
-
一个很好的练习是使用
ghc -O2 -ddump-simpl -dsuppress-all检查neighbs的每个变体生成的核心GHC -
我已将信息包含在原帖中
-
看看 [this] (haskell.org/haskellwiki/Let_vs._Where) 的最后一点。它似乎与您的示例相似。在这种情况下,问题是每次都会重新计算地图,但我不能确定这是否是这里发生的事情,我也无法解释为什么。
标签: list haskell optimization list-comprehension ghc