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

String 为什么Haskell中基于[Char]的输入比基于[Char]的输出慢得多?,string,performance,haskell,io,String,Performance,Haskell,Io,众所周知,不使用[Char]读取Haskell中的大量数据。我们使用ByteStrings来完成这项工作。 通常的解释是Chars很大,列表增加了开销 但是,这似乎不会对输出造成任何问题 例如,以下程序: main = interact $ const $ unwords $ map show $ replicate 500000 38000000 在我的计算机上运行只需131毫秒,而以下程序: import Data.List sum' :: [Int] -> Int sum' =

众所周知,不使用
[Char]
读取Haskell中的大量数据。我们使用
ByteString
s来完成这项工作。 通常的解释是
Char
s很大,列表增加了开销

但是,这似乎不会对输出造成任何问题

例如,以下程序:

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秒


使用
String
s时,输入和输出性能之间出现这种差异的原因是什么?

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

首先,考虑下面的程序,该程序只处理一个懒惰的列表。在我的机器上需要4.1秒(用
-O2
编译):

读取
功能替换为
长度
会将时间降低到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.parsercompbinators.ReadP
模块,对于读取单个整数的简单情况来说,这可能不是最快的选择。

我的快速评测显示,输入程序分配的内存是输出程序的13倍。这肯定是造成差异的原因。哦,所以不使用
String
s的主要原因与
String
s无关。这太不公平了。公平地说,
read
做了一些
myread
没有做的事情:错误检查、空格跳过、负数、十六进制、八进制,甚至(令人惊讶的!)指数记数法。如何为
read
写八进制?我希望它不是以
0
@Rotsor八进制作为前缀,因为
read
的八进制与literal Haskell语法中的八进制相同:
0o32=26
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