2012-04-02 31 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