繁体   English   中英

Haskell:两个版本代码之间的速度差异

[英]Haskell: speed difference between two versions of code

我已经开始尝试解决一些小问题而潜入Haskell。

我偶然发现了“标准的haskell友好”解决方案与我的“非常丑陋且不易受欢迎版本之间的巨大性能差异(~100-200x)。

我敢肯定,对于其他的haskellers来说,这种性能上的差异有一个很好的理由,我很遗憾,并且可以就这个话题教育我。

问题:找到数字字符串中的最大5位数字

两者在解决时都使用相同的概念 :生成所有5位数字并找到最大值。

优雅而快速的代码

digit5 :: String -> Int
digit5 = maximum . map (read . take 5) . init . tails

代码丑陋且速度很慢(一旦字符串大小很大)

digit5' :: String -> String -> String
-- xs - input string
-- maxim - current maximal value
digit5' xs maxim 
  | (length xs) < 5 = maxim
  | y > maxim = digit5' (drop 1 xs) y -- use new detected maximum value
  | otherwise = digit5' (drop 1 xs) maxim
  where y = take 5 xs

digit5 :: String -> Int
digit5 xs
-- return the original string if the input size is smaller than 6
  | length (xs) < 6 = read xs 
  | otherwise = read $ digit5' xs "00000"

对于小输入,我获得大致相同的执行时间,对于大输入,我开始看到非常大的差异(对于44550元素的输入):

Computation time for ugly version: 2.047 sec
Computation time for nice version: 0.062 sec

我对此的肤浅理解是快速代码使用了预先可用的高阶函数 对于较慢的版本使用递归,但我认为应该可以进行尾部咬合。 但在一个天真的层面上,对我来说,两者似乎也做同样的事情。

虽然较慢的函数会对字符串进行比较而不是将它们转换为数字,但我也尝试将字符串转换为整数但没有任何大的改进

我尝试使用没有任何标志的ghc进行编译,并使用以下命令:

ghc 
ghc -O2 
ghc -O2 -fexcess-precision -optc-O3 -optc-ffast-math -no-
recomp 
stack runhaskell 
ghc -O3

为了重现性,我正在添加一个代码链接,同时包含测试向量: https//pastebin.com/kuS5iKgd

你的“慢”版本的问题是这一行:

| length xs < 5 = maxim

这会计算xs的长度,并且因为Haskell列表是单链表,所以此操作需要完整遍历整个列表,即O(n)。 它发生在每次迭代中。 并且有N次迭代。 这使得整个过程为O(n ^ 2)。

另一方面,“快速”代码只是线性的,在大输入上生动地显示出来。

如果你只是用这个替换违规行:

| null xs = maxim

它将使整个事物变得线性,并且它将与“优雅”解决方案一样快。 当然,这将导致额外的5次迭代,但是通过降低整体复杂性来弥补损失。

或者,您可以通过过滤掉5个或更短的尾部来使“优雅”解决方案变得缓慢:

digit5 = maximum . map (read . take 5) . filter ((>= 5) . length) . tails

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM