我认为我正在测试partition
函数的性能,并得到了一些奇怪的结果。
我们有那个partition p xs == (filter p xs, filter (not . p) xs)
,但是我们选择了第一个实现,因为它只对列表执行一次遍历。但是,我得到的结果是,最好使用使用两次遍历的实现。
这是显示我所见内容的最少代码
import Criterion.Main
import System.Random
import Data.List (partition)
mypartition :: (a -> Bool) -> [a] -> ([a],[a])
mypartition p l = (filter p l, filter (not . p) l)
randList :: RandomGen g => g -> Integer -> [Integer]
randList gen 0 = []
randList gen n = x:xs
where
(x, gen') = random gen
xs = randList gen' (n - 1)
main = do
gen <- getStdGen
let arg10000000 = randList gen 10000000
defaultMain [
bgroup "filters -- split list in half " [
bench "partition100" $ nf (partition (>= 50)) arg10000000
, bench "mypartition100" $ nf (mypartition (>= 50)) arg10000000
]
]
我同时使用
-O
和不使用ghc-7.10.3
进行了测试,两次都得到了更好的两次遍历。我正在使用
criterion-1.1.1.0
和(filter p xs, filter (not . p) xs)
我的问题是:
非常感谢!
最佳答案
这个问题没有黑白答案。要分析该问题,请考虑以下代码:
import Control.DeepSeq
import Data.List (partition)
import System.Environment (getArgs)
mypartition :: (a -> Bool) -> [a] -> ([a],[a])
mypartition p l = (filter p l, filter (not . p) l)
main :: IO ()
main = do
let cnt = 10000000
xs = take cnt $ concat $ repeat [1 .. 100 :: Int]
args <- getArgs
putStrLn $ unwords $ "Args:" : args
case args of
[percent, fun]
-> let p = (read percent >=)
in case fun of
"partition" -> print $ rnf $ partition p xs
"mypartition" -> print $ rnf $ mypartition p xs
"partition-ds" -> deepseq xs $ print $ rnf $ partition p xs
"mypartition-ds" -> deepseq xs $ print $ rnf $ mypartition p xs
_ -> err
_ -> err
where
err = putStrLn "Sorry, I do not understand."
我没有使用Criterion更好地控制评估顺序。为了获得时间,我使用
+RTS -s
运行时选项。使用不同的命令行选项执行不同的测试用例。第一个命令行选项定义谓词占数据百分比。第二个命令行选项在不同的测试之间进行选择。测试区分两种情况:
partition
或mypartition
)。 partition-ds
或mypartition-ds
)。 分区的结果总是从左到右评估,即从包含谓词所持有的所有元素的列表开始。
在情况1中,
partition
的优点是,甚至在生成输入列表的所有元素之前,第一个结果列表的元素也会被丢弃。如果谓词与许多元素匹配,即第一个命令行参数很大,则情况1特别好。在情况2中,由于所有元素已经在内存中,因此
partition
无法发挥这一优势。对于
mypartition
,无论如何,在评估第一个结果列表之后,所有元素都保留在内存中,因为再次需要它们来计算第二个结果列表。因此,这两种情况之间没有太大区别。似乎,使用的内存越多,垃圾收集就越困难。因此,如果谓词与许多元素匹配并且使用了lazy变体,则
partition
非常适合。相反,如果谓词与许多元素不匹配,或者所有元素都已在内存中,则
mypartition
的性能会更好,因为与partition
相比,它的递归不处理对。Stackoverflow问题“Irrefutable pattern does not leak memory in recursion, but why?”可能会提供更多关于
partition
递归中对对的处理的见解。关于haskell - 基准过滤器和分区,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38671397/