高效输出数字

问题描述:

我想打印一个积分列表,用空格分隔到stdout。列表生成速度很快,所以我试图用序列[1..200000]来解决这个问题。高效输出数字

在C,我可以这样实现:

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

在Haskell我可以实现最快的解决方案约3倍慢:

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

我想在某些方面字节串,但与他们相比,它变得更慢。 大问题似乎是用show(或转换为ByteStrings)将整数转换为字符串。

任何建议如何加快这一点,而无需连接到C?它不应该变得复杂(尽可能简短和美观,使用其他Haskell模块也可以)。

+1

一如既往,分析>>>>>猜测。 – delnan 2010-11-26 13:47:45

+0

您是否检查过每次写入元素的速度?写入控制台通常很慢,所以这可能会更糟糕,但它可能值得一试。您可以尝试构建一定数量的元素而不是一个巨大的字符串,但是您可能需要使用append(++)或Hughes列表(DList)来执行此操作,这会添加额外的工作。这就是为什么我猜测写每个元素仍然有竞争力。 – 2010-11-26 16:17:51

+0

我试过一次写了一个数字的版本(就像我认为的一样),但速度较慢。 – 2010-11-26 16:24:58

第一个问题:

Post some code !!!

我猜(根据delnan :),这是因为以下情况的速度慢(跳过步骤4如果你不使用的字节字符串):

  1. 所有的数字都是一个接一个转换。输出是一个列表。
  2. 的产出清单已经再次因为你添加元素走过(空格!)
  3. 名单已经因为你Concat的它
  4. 名单必须再次经过再次穿越,因为它被转换为字节串(pack
  5. 整件事打印出来。

使用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 

这是未经测试,所以轮廓!你看,地图可以融合,所以列表将被遍历两次。

嗯,你可以重写代码位:

COST CENTRE     MODULE    %time %alloc 

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

您可以:

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

然后,你可以使用 “GHC -02 -prof - 自动所有杂项文件” 简介它请参阅intercalate占用运行时的一半。尽管如此,我并不认为你可以让整个事情变得更快,但不会诉诸一些低级别的诡计。如果切换到更快插入(例如,来自Data.ByteString.Lazy.Char8),则必须使用Int - > String转换的较慢变体。

下面是针对同一问题的另一种方法,即尝试利用字符串后缀中的共享。它在我的机器上运行速度比原来的Haskell*分之一左右,尽管毫无疑问仍然是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) 

这个程序运行得更快,如果我用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的结果是相当媲美℃;我认为这种差异是由于Haskell使用字符串(也可能是运行时开销)造成的。

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

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

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

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