Я пытаюсь оптимизировать скорость выполнения своей программы, и я натолкнулся на некоторые интересные результаты, и я надеюсь, что кто-то сможет ответить. Кажется, что внесение небольших изменений в одно из моих понятий списка резко меняет скорость выполнения, но я не знаю почему.
Вот моя программа, как сейчас.
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, предоставленным Проект Эйлера.
Если я изменю соседи из
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сек.
Другие незначительные изменения, такие как перемещение фильтра на следующей строке в понимание списка, дают тот же результат: ~ 1 с.
Может ли кто-нибудь объяснить, почему это происходит?
EDIT: похоже, этого не происходит в более ранних версиях GHC. Я пробовал GHC 7.6.3, и каждый из них выполнял примерно то же самое.
Я включил дампы из ghc -O2 -ddump-simpl -dsuppress-all
, как это было предложено cdk. Я действительно не знаю, на что я смотрю, поэтому, если кто-то сможет интерпретировать, это будет большой помощью, спасибо.
EDIT2 (Ответ на Прийатам): Я не думаю, что дело. Я изменил
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, и это все еще работает чуть больше секунды. Фактически, изменение neigbs из
neighbs = [(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сек.