为什么基于[Char]的输入比Haskell中基于[Char]的输出慢得多?

Rot*_*sor 7 string io performance haskell

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

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

例如以下程序:

main = interact $ const $ unwords $ map show $ replicate 500000 38000000
Run Code Online (Sandbox Code Playgroud)

在我的计算机上运行只需131毫秒,而以下一个:

import Data.List

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

main = interact $ show . sum' . map read . words
Run Code Online (Sandbox Code Playgroud)

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

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

Jud*_*son 10

我不认为这个问题必然与I/O有关.相反,它表明Read实例Int非常低效.

首先,考虑以下只处理惰性列表的程序.我的机器需要4.1s(编译-O2):

main = print $ sum' $ map read $ words
        $ unwords $ map show $ replicate 500000 38000000
Run Code Online (Sandbox Code Playgroud)

更换read功能可length将时间缩短至0.48秒:

main = print $ sum' $ map length $ words
        $ unwords $ map show $ replicate 500000 38000000
Run Code Online (Sandbox Code Playgroud)

此外,用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
Run Code Online (Sandbox Code Playgroud)

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

  • 公平地说,`read`做了一些'myread`没有做的事情:错误检查,空格跳过,负数,十六进制,八进制,甚至(惊讶!)指数表示法. (2认同)