【问题标题】:Why is [Char]-based input so much slower than the [Char]-based output in Haskell?为什么在 Haskell 中基于 [Char] 的输入比基于 [Char] 的输出慢得多?
【发布时间】:2011-11-22 12:43:26
【问题描述】:

众所周知,在 Haskell 中不使用[Char] 读取大量数据。一个人使用ByteStrings 来完成这项工作。 通常对此的解释是 Chars 很大,列表会增加开销。

但是,这似乎不会导致输出出现任何问题。

例如下面的程序:

main = interact $ const $ unwords $ map show $ replicate 500000 38000000

在我的电脑上运行仅需 131 毫秒,而以下:

import Data.List

sum' :: [Int] -> Int
sum' = foldl' (+) 0

main = interact $ show . sum' . map read . words

如果将第一个程序的输出作为输入,则需要 3.38 秒!

使用Strings 的输入和输出性能出现这种差异的原因是什么?

【问题讨论】:

  • 我的快速分析显示输入程序分配的内存是输出程序的 13 倍。这肯定会造成差距。

标签: string performance haskell io


【解决方案1】:

我认为这个问题不一定与 I/O 有关。相反,它表明IntRead 实例效率很低。

首先,考虑以下仅处理惰性列表的程序。在我的机器上需要 4.1s(使用-O2 编译):

main = print $ sum' $ map read $ words
        $ unwords $ map show $ replicate 500000 38000000

read 函数替换为length 可将时间缩短至0.48 秒:

main = print $ sum' $ map length $ words
        $ unwords $ map show $ replicate 500000 38000000

此外,将read 函数替换为手写版本会导致时间为 0.52 秒:

main = print $ sum' $ map myread $ words
        $ unwords $ map show $ replicate 500000 38000000

myread :: String -> Int
myread = loop 0
  where
    loop n [] = n
    loop n (d:ds) = let d' = fromEnum d  - fromEnum '0' :: Int
                        n' = 10 * n + d'
                    in loop n' ds

我猜为什么read 效率如此之低是因为它的实现使用了Text.ParserCombinators.ReadP 模块,对于读取单个整数的简单情况,这可能不是最快的选择。

【讨论】:

  • 哦,所以不使用Strings 的主要原因与Strings 没有任何关系。这太不公平了。
  • 公平地说,read 做了一些 myread 没有做的事情:错误检查、空格跳过、负数、十六进制、八进制,甚至(惊喜!)指数表示法。
  • 如何为read 写八进制数?我希望它不是以0 为前缀的数字。
  • @Rotsor 八进制 read 与字面 Haskell 语法中的八进制相同:0o32 = 26
猜你喜欢
  • 2023-03-21
  • 1970-01-01
  • 2021-09-15
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-02
相关资源
最近更新 更多