2012-04-02 55 views
8

这是我的用于计数线和字码:这个简单的文本分析程序为什么这么慢?

import System.IO 
import Data.List 
main = do 
     hSetBinaryMode stdin True 
     interact $ (\(w,l)->"line:"++(show l)++"\nwords:"++(show w)++"\n") 
        . foldl' (\(w,l) r-> w `seq` l `seq` (w+length r ,succ l)) (0,0) 
        . lines 

这需要约10秒到约100兆字节的文件运行。我将它与Lua(9s),awk(20s)和wc -l -c(0.6s)中的类似程序进行了比较。

这段代码为什么这么慢?可能是什么问题呢?

+0

你用-O2编译过吗? – 2012-04-02 14:34:56

+0

是的,-O2实际上只是加速大约0.xx秒 – vzex 2012-04-02 14:41:42

+4

尝试使用ByteString:http://stackoverflow.com/questions/9746352/parsing-large-log-files-in-haskell – 2012-04-02 14:58:23

回答

15

使用String的I/O在Haskell中已知不及速度快。从句柄中读取的字节通常必须转换为Unicode代码点,然后从这些字符串构建链接列表。这导致了很多分配的很多工作。在这种情况下,转换为代码点会更简单一些,因为您将stdin设置为二进制模式,但构建链接的字符列表仍然需要很长时间。

另一个小的因素是您的行数使用Integer,但这很小,只在I/O达到速度时起作用。

如果您需要快速I/O,则必须使用更适合该类型的类型。一种可能性是使用ByteString,例如

import Data.List 
import qualified Data.ByteString.Lazy.Char8 as C 
main = do 
     txt <- C.getContents 
     putStrLn $ (\(w,l)->"line:"++(show l)++"\nwords:"++(show w)++"\n"). foldl' (\(w,l) r-> w `seq` l `seq` (w+C.length r ,succ l)) (0,0) . C.lines $ txt 

确实对我的盒子在0.12S一个94MB文件的作业(WC -l -c需要0.06S),而原来使用String了4.4s。它可以进一步优化,

{-# LANGUAGE BangPatterns #-} 
import Data.List 
import qualified Data.ByteString.Lazy.Char8 as C 
main = do 
     txt <- C.getContents 
     putStrLn $ (\(w,l)->"line:"++(show l)++"\nwords:"++(show w)++"\n"). loop 0 0 . C.lines $ txt 

loop :: Int -> Int -> [C.ByteString] -> (Int,Int) 
loop !w !l (ln:lns) = loop (w + fromIntegral (C.length ln)) (l+1) lns 
loop w l _ = (w,l) 

只需要0.08s,这是不够体面我停最佳那里(为String版本类似的变化所带来的时间缩短到3.6s为)。

+0

那么,这个结果是我的期望,非常感谢。 – vzex 2012-04-02 15:45:53

+4

如果丹尼尔的回答对你有帮助,你应该点击旁边的复选标记将其标记为已接受,以便你的问题被标记为已解决,其他人阅读此问题可以看到答案帮助你:) – ehird 2012-04-02 15:49:58

+0

好吧,我很新鲜,现在它被标记〜 – vzex 2012-04-02 16:35:12