2012-04-05 9 views
4

私はUTF8で大きなテキストファイルをロードして解析しなければならなかったHaskellのプログラムを書いています。このファイルは、各行にkey:valueのペアを持つ辞書を表します。私のプログラムでは、辞書検索を高速化するためのData.Mapコンテナが必要です。私のファイルは約40MBですが、私のプログラムにロードした後、1.5GBの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

私はHaskellの上で少しさびだけど、短所のオペレータがどことしてIIRC、 '++'構文は、メモリ・高価です( ':')安いです。 '(key、x):parseToEntries xs'のようなものを使うことは可能ですか?もう一度。 。 。私のハスケルは非常に錆びているので、これは邪魔になるかもしれません。 – jpm

+0

@jpmの場合、メモリの使用量は '++'の最初の引数の長さに依存します。この場合、関係はありません。 –

+0

@maxtaldykinああ、意味がある。説明をありがとう。 – jpm

答えて

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

4

ハスケルStringタイプが(理由は怠惰の)リンクされた文字のリスト間接的である

モジュールメイン。それは非常に無駄なスペースです。大量のテキストの場合は、代わりにData.Texthttp://hackage.haskell.org/package/text)を試してください。

編集は今、そのソースは、私は、文字列は怠け者ByteStringの代わりStringある参照アップしているので、これは関係ありません。プロファイリングは、次のステップです。)

+0

プログラムは 'maybeRead'に' String'しか使用しません。引数はおそらく比較的小さいでしょう。 – ehird

+0

さて、部分ソースがあり、主なフォーマットはUTF8遅延 'ByteString'です(これは理想的ではありませんが、' String'よりはるかに優れています)。あなたはあまりにも怠け者で、塊が築かれている可能性があります。次のステップは、プロファイリングを使用してスペースがどこにあるかを確認することです。 – geekosaur

+0

私は尋問者ではありませんが、質問は一度も編集されていないので、ソースが追加されていれば、最初の5分間でなければなりません。(実際には、猶予期間が質問に当てはまるかどうかはわかりませんが...)しかし、私の以前のコメントは間違っていました。 – ehird

関連する問題