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