考虑函数reverseAndMinimum
(稍微修改自another answer nearby):
import Control.Monad.State.Strict
reverseAndMinimum' :: Ord a => [a] -> State a [a] -> State a [a]
reverseAndMinimum' [ ] res = res
reverseAndMinimum' (x:xs) res = do
smallestSoFar <- get
when (x < smallestSoFar) (put x)
reverseAndMinimum' xs ((x:) <$> res)
reverseAndMinimum :: Ord a => [a] -> ([a], a)
reverseAndMinimum [ ] = error "StateSort.reverseAndMinimum: This branch is unreachable."
reverseAndMinimum xs@(x:_) = runState (reverseAndMinimum' xs (return [ ])) x
它只遍历一次参数;然而,它比两次执行它的天真函数慢约30%:
reverseAndMinimum_naive :: Ord a => [a] -> ([a], a)
reverseAndMinimum_naive xs = (reverse xs, minimum xs)
它也消耗了大约57%的内存。
以下是+RTS -s
reverseAndMinimum
176,672,280 bytes allocated in the heap
...
INIT time 0.000s ( 0.000s elapsed)
MUT time 0.064s ( 0.063s elapsed)
GC time 0.311s ( 0.311s elapsed)
EXIT time 0.005s ( 0.005s elapsed)
Total time 0.379s ( 0.380s elapsed)
%GC time 82.0% (82.0% elapsed)
reverseAndMinimum_naive
112,058,976 bytes allocated in the heap
...
INIT time 0.000s ( 0.000s elapsed)
MUT time 0.041s ( 0.040s elapsed)
GC time 0.245s ( 0.245s elapsed)
EXIT time 0.005s ( 0.005s elapsed)
Total time 0.291s ( 0.291s elapsed)
%GC time 84.2% (84.3% elapsed)
发生了什么,我该如何诊断,是否有可能改善?
P.S。用于运行测试的方便main
:
main = do
top <- (read :: String -> Int) . (!! 0) <$> getArgs
val <- evaluate . force $ reverseAndMinimum (take top [top, top - 1.. 1 :: Int])
print $ (\x -> (last . fst $ x, snd x)) $ val
答案 0 :(得分:6)
编辑:这是指reverseAndMinimum :: Ord a => [a] -> State a ([a] -> [a])
的问题的先前版本。
在天真版本中,reverse
是有效的,因为它可以在遍历列表时直接构建反向列表,并且minimum
被忽略,因为它不需要。
“一次通过”reverseAndMinimum
分配差异列表,必须应用该列表以产生实际列表,然后再次遍历该列表以查找其最后一个元素。
复制reverse
中使用的累加器技术,以下代码编译为紧密循环,以便在一次传递中计算列表的反向和最小值。
import Control.Monad.State.Strict
reverseAndMinimum :: Ord a => [a] -> ([a], a)
reverseAndMinimum [ ] = error "Empty list!"
reverseAndMinimum (x:xs) = runState (reverseAndMinimum' xs [x]) x
reverseAndMinimum' :: Ord a => [a] -> [a] -> State a [a]
reverseAndMinimum' [ ] acc = return acc
reverseAndMinimum' (x:xs) acc = do
smallestSoFar <- get
when (x < smallestSoFar) (put $ x)
reverseAndMinimum' xs (x : acc)