2012-04-05 94 views
4

我在Haskell中編寫了一個程序,它必須以UTF8加載和解析大文本文件。該文件表示每行上具有鍵值對的字典。在我的程序中,我希望有一個Data.Map容器​​來進行快速字典搜索。我的文件大約是40MB,但在將其加載到我的程序後,使用了1.5 GB的RAM,並且從未釋放。我做錯了什麼?預計內存使用情況如何?爲什麼Haskell在處理字符串時會分配大量內存?

這裏是從我的程序代碼示例:

模塊主要其中

import Engine 

import Codec.Archive.Zip 
import Data.IORef 
import System.IO 
import System.Directory 
import qualified System.IO.UTF8 as UTF8 
import qualified Data.ByteString.Lazy as B 
import qualified Data.ByteString.UTF8 as BsUtf 
import qualified Data.Map as Map 

import Graphics.UI.Gtk 
import Graphics.UI.Gtk.Glade 

maybeRead :: Read a => BsUtf.ByteString -> Maybe a 
maybeRead s = case reads $ BsUtf.toString s of 
    [(x, "")] -> Just x 
    _   -> Nothing  

parseToEntries :: [BsUtf.ByteString] -> [(BsUtf.ByteString, Int)] 
parseToEntries [] = [] 
parseToEntries (x:xs) = let (key, svalue) = BsUtf.break (==':') x 
          value = maybeRead svalue 
         in case value of 
          Just x -> [(key, x)] ++ parseToEntries xs 
          Nothing -> parseToEntries xs 

createDict :: BsUtf.ByteString -> IO (Map.Map BsUtf.ByteString Int) 
createDict str = do 
    let entries = parseToEntries $ BsUtf.lines str 
     dict = Map.fromList entries 
    return (dict) 

main :: IO() 
main = do 

    currFileName <- newIORef "" 

    dictZipFile <- B.readFile "data.db"  
    extractFilesFromArchive [] $ toArchive dictZipFile 
    dictFile <- UTF8.readFile "dict.txt" 
    dict <- createDict $ BsUtf.fromString dictFile 

... 

searchAccent :: Map.Map BsUtf.ByteString Int -> String -> Int 
searchAccent dict word = let sword = BsUtf.fromString $ map toLower word 
          entry = Map.lookup sword dict 
         in case entry of 
          Nothing -> -1 
          Just match -> 0      
+0

我在哈斯克爾有點生疏,但IIRC的''++語法是內存價格昂貴,其中的利弊操作符(':')便宜。是否有可能使用像'(key,x):parseToEntries xs'?再次。 。 。我的Haskell非常生鏽,所以這可能會失敗。 – jpm 2012-04-05 21:16:13

+0

@jpm,它的內存昂貴取決於'++'的第一個參數的長度。在這種情況下,它不相關。 – 2012-04-05 21:21:42

+0

@maxtaldykin啊,這很有道理。感謝您的澄清。 – jpm 2012-04-05 21:38:27

回答

7

快速的答案。
主要問題是System.IO.UTF8.readFile讀取文件到String

應該瓶頸是在這裏:

dictFile <- UTF8.readFile "dict.txt" 
dict <- createDict $ BsUtf.fromString dictFile 

當使用UTF-8文本處理,最好是用Data.Text而不是ByteString。 嘗試是這樣的:

import qualified Data.Text.Lazy as LT 
import qualified Data.Text.Lazy.Encoding as LT 

... 
dictFile <- B.readFile "dict.txt" 
dict <- createDict $ LT.decodeUtf8 dictFile 

另一個瓶頸是解析數:要轉換ByteStringString,然後read它。 這是更好地使用Data.Text.Lazy.Read

import qualified Data.Text.Lazy.Read as LT 

maybeRead :: LT.Text -> Maybe Int 
maybeRead s = case LT.decimal s of 
    Left _ -> Nothing 
    Right i -> Just i 
+0

謝謝,爲答案。 Data.Text確實佔用了更少的RAM內存。但我使用了嚴格的版本,我認爲它更經濟。 – KolKir 2012-04-06 09:34:47

4

Haskell的String類型是間接連接的(因爲懶惰的)字符的列表;它是非常浪費太空明智。您不妨試試Data.Text(來自http://hackage.haskell.org/package/text),而不是大量的文字。

編輯現在源了,我看到了字符串是懶惰ByteString而不是String,所以這是不相關的。剖析是下一個步驟。)

+0

該程序僅在'maybeRead'中使用'String',其參數可能相對較小。 – ehird 2012-04-05 21:34:45

+0

好吧,我看到你現在有部分源碼,主要格式是UTF8惰性'ByteString'(這不是理想的,但比'String'好得多)。你可能太懶惰了,並且有可能造成沉重的打擊;下一步是使用分析來查看空間的進展情況。 – geekosaur 2012-04-05 21:40:00

+0

我不是提問者,但這個問題從來沒有被編輯過,所以如果源代碼被添加進來,它一定是在前五分鐘。(實際上,我不確定寬限期是否適用於問題......)但是,我之前的評論是錯誤的,正如max的答案所示。 – ehird 2012-04-05 21:43:22

相關問題