有效的数字输出

时间:2010-11-26 13:43:16

标签: haskell bytestring

我想打印一个用空格分隔的积分列表到stdout。列表生成很快,所以我尝试用序列[1..200000]来解决这个问题。

在C中,我可以像这样实现它:

#include "stdio.h"
int main()
{
  int i;
  for(i = 0; i <= 200000; ++i)
    printf("%d ", i);
  return 0;
}

我可以实现的Haskell中最快的解决方案慢了三倍:

import Data.List (intercalate)
main = putStr . intercalate " " . map (show) $ [1..(200000)]

我在某些方面尝试了ByteStrings,但是对它们来说它变得更慢了。 最大的问题似乎是将整数转换为带有show的字符串(或转换为ByteStrings)。

如何在不与C接口的情况下加快速度的建议?它不应该变得复杂(尽可能短而美观,使用其他Haskell模块就可以了)。

5 个答案:

答案 0 :(得分:4)

好吧,你可以稍微改写一下代码:

import Data.List (intercalate)
main = output
output = putStr one_string
one_string = intercalate " " strings
strings = map show $ [1..2000000]

然后你可以用“ghc -O2 -prof -auto-all .hs”来描述它:

COST CENTRE                    MODULE               %time %alloc

one_string                     Main                  42.2   55.9
strings                        Main                  39.2   43.1
output                         Main                  18.6    1.0

您可以看到插入占用了运行时的一半。我不认为你可以让整个事情变得更快,但不会诉诸一些低级别的诡计。如果你切换到更快的插入(例如,从Data.ByteString.Lazy.Char8),你将不得不使用较慢的Int - &gt;变体。字符串转换。

答案 1 :(得分:2)

如果我使用ghc-6.10.4而不是ghc-6.12.1,这个程序运行得更快。 IIRC 6.12系列引入了unicode感知IO,我认为这会导致很多放缓。

我的系统:

C  (gcc -O2):        0.141s
HS (ghc-6.10.4 -O2): 0.191s (ave.)
HS (ghc-6.12.1 -O2): 0.303 (ave.)

使用ghc-6.10时,结果与C相当;我认为存在差异是由于Haskell使用字符串(也可能是运行时开销)。

如果你想从该编译器获得更好的性能,我认为可以绕过ghc-6.12的I / O中的unicode转换。

答案 2 :(得分:1)

第一个问题:

发布一些代码!!!

我猜(根据delnan :),它很慢,因为发生以下情况(如果你不使用bytestring,则跳过步骤4):

  1. 所有数字都是逐个转换的。输出是一个列表。
  2. 必须再次遍历输出列表,因为您添加了元素(空格!)
  3. 该列表必须再次遍历因为你连接
  4. 必须再次遍历 列表,因为它已转换为bytestring(pack
  5. 整件事打印出来。
  6. 使用bytestring可能会更快,但您应该实现自己的show,它可以使用字节串。然后,如此聪明并避免多次转换,在创建列表后输入空格。

    也许是这样的:

    import qualified Data.Bytestring.Lazy.Char8 as B
    
    showB :: Int -> Bytestring -- Left as an exercise to the reader
    
    main = B.putStr $ pipeline [0..20000] where
      pipeline = B.tail . B.concat . map (B.cons' ' ') . map showB
    

    这是未经测试的,所以个人资料!你看,to maps可以融合,所以列表可能会被遍历两次。

答案 3 :(得分:0)

这是针对同一问题的不同方法,尝试利用字符串后缀中的共享。它在我的机器上比原来的Haskell快了约1/3,尽管它仍然远离C版本。执行1到999999之外的数字作为练习:

basic :: [Char]
basic = ['0'..'9']

strip :: String -> String
strip = (' ' :) . dropWhile (== '0')

numbers :: Int -> [String]
numbers 0 = [""]
numbers n = [x : xs | x <- basic, xs <- rest]
  where
    rest = numbers (n - 1)

main = mapM_ (putStr . strip) (tail $ numbers 6)

答案 4 :(得分:0)

这个版本比你的好一点。我想这是改善它的一种方法。

showWithSpaces        :: (Show a) => [a] -> ShowS
showWithSpaces []     = showString ""
showWithSpaces (x:xs) = shows x . showChar ' ' . showWithSpaces xs

main = putStrLn $ showWithSpaces [1..2000000] $ ""