2015-09-11 26 views
5

Aşağıdaki kısa Haskell programını yeterince büyük bir veri kümesinde çalıştırırken Heap exhausted mesajını alıyorum. Örneğin, program yaklaşık 900k satırlı 20 Mb giriş dosyasında (yığın taşmasıyla) başarısız olur. Yığın büyüklüğü (-with-rtsopts aracılığıyla) 1 Gb'ye ayarlandı. longestCommonSubstrB'un daha basit bir şey olarak tanımlanması durumunda, commonPrefix. Dosyaları 100 Mb sırada işlemem gerekiyor.Haskell'de yığın taşması

Aşağıdaki komut satırında (GHC 7.8.3) ile program derlenmiş

: girdi sırasına göre (I alan makul bir çalıştırmak bu şeyi yapımında herhangi bir yardım takdir ediyorum

ghc -Wall -O2 -prof -fprof-auto "-with-rtsopts=-M512M -p -s -h -i0.1" SampleB.hs 

Dosya boyutu), ama özellikle darboğazın nerede olduğunu ve nerede ve nasıl katılaşmaya zorlanacağını bulma sürecini takdir ediyorum.

Tahminimce, bir şekilde longestCommonSubstrB işlevini zorlamak, bu sorunu kesinlikle çözecektir, ancak bunu nasıl yapacağımı bilmiyorum.

yığın profili, zaman içinde:

{-# LANGUAGE BangPatterns #-} 
module Main where 
import System.Environment (getArgs) 
import qualified Data.ByteString.Lazy.Char8 as B 
import Data.List (maximumBy, sort) 
import Data.Function (on) 
import Data.Char (isSpace) 

-- | Returns a list of lexicon items, i.e. [[w1,w2,w3]] 
readLexicon :: FilePath -> IO [[B.ByteString]] 
readLexicon filename = do 
    text <- B.readFile filename 
    return $ map (B.split '\t' . stripR) . B.lines $ text 
    where 
     stripR = B.reverse . B.dropWhile isSpace . B.reverse 

transformOne :: [B.ByteString] -> B.ByteString 
transformOne (w1:w2:w3:[]) = 
    B.intercalate (B.pack "|") [w1, longestCommonSubstrB w2 w1, w3] 
transformOne a = error $ "transformOne: unexpected tuple " ++ show a 

longestCommonSubstrB :: B.ByteString -> B.ByteString -> B.ByteString 
longestCommonSubstrB xs ys = maximumBy (compare `on` B.length) . concat $ 
    [f xs' ys | xs' <- B.tails xs] ++ 
    [f xs ys' | ys' <- tail $ B.tails ys] 
    where f xs' ys' = scanl g B.empty $ B.zip xs' ys' 
     g z (x, y) = if x == y 
       then z `B.snoc` x 
       else B.empty 

main :: IO() 
main = do 
    (input:output:_) <- getArgs 
    lexicon <- readLexicon input 
    let flattened = B.unlines . sort . map transformOne $ lexicon 
    B.writeFile output flattened 

Bu test veri kümesi için profil çıkışına olan (100k hatları, yığın boyutu yani generateSample.exe 100000 1 GB, ayarlanmış, elde edilen dosya boyutu 2.38 MB)

Memory usage over time

Yürütme istatistikleri:

3,505,737,588 bytes allocated in the heap 
    785,283,180 bytes copied during GC 
     62,390,372 bytes maximum residency (44 sample(s)) 
     216,592 bytes maximum slop 
       96 MB total memory in use (0 MB lost due to fragmentation) 

            Tot time (elapsed) Avg pause Max pause 
    Gen 0  6697 colls,  0 par 1.05s 1.03s  0.0002s 0.0013s 
    Gen 1  44 colls,  0 par 4.14s 3.99s  0.0906s 0.1935s 

    INIT time 0.00s ( 0.00s elapsed) 
    MUT  time 7.80s ( 9.17s elapsed) 
    GC  time 3.75s ( 3.67s elapsed) 
    RP  time 0.00s ( 0.00s elapsed) 
    PROF time 1.44s ( 1.35s elapsed) 
    EXIT time 0.02s ( 0.00s elapsed) 
    Total time 13.02s (12.85s elapsed) 

    %GC  time  28.8% (28.6% elapsed) 

    Alloc rate 449,633,678 bytes per MUT second 

    Productivity 60.1% of total user, 60.9% of total elapsed 

Zaman ve Tahsis profil oluşturma Raporu:

 SampleB.exe +RTS -M1G -p -s -h -i0.1 -RTS sample.txt sample_out.txt 

    total time =  3.97 secs (3967 ticks @ 1000 us, 1 processor) 
    total alloc = 2,321,595,564 bytes (excludes profiling overheads) 

COST CENTRE   MODULE %time %alloc 

longestCommonSubstrB Main  43.3 33.1 
longestCommonSubstrB.f Main  21.5 43.6 
main.flattened   Main  17.5 5.1 
main     Main  6.6 5.8 
longestCommonSubstrB.g Main  5.0 5.8 
readLexicon   Main  2.5 2.8 
transformOne   Main  1.8 1.7 
readLexicon.stripR  Main  1.8 1.9 


                      individual  inherited 
COST CENTRE     MODULE      no.  entries %time %alloc %time %alloc 

MAIN       MAIN       45   0 0.1 0.0 100.0 100.0 
main      Main       91   0 6.6 5.8 99.9 100.0 
    main.flattened    Main       93   1 17.5 5.1 89.1 89.4 
    transformOne    Main       95  100000 1.8 1.7 71.6 84.3 
    longestCommonSubstrB  Main       100  100000 43.3 33.1 69.8 82.5 
    longestCommonSubstrB.f Main       101  1400000 21.5 43.6 26.5 49.5 
     longestCommonSubstrB.g Main       104  4200000 5.0 5.8  5.0 5.8 
    readLexicon    Main       92   1 2.5 2.8  4.2 4.8 
    readLexicon.stripR  Main       98   0 1.8 1.9  1.8 1.9 
CAF       GHC.IO.Encoding.CodePage  80   0 0.0 0.0  0.0 0.0 
CAF       GHC.IO.Encoding    74   0 0.0 0.0  0.0 0.0 
CAF       GHC.IO.FD      70   0 0.0 0.0  0.0 0.0 
CAF       GHC.IO.Handle.FD    66   0 0.0 0.0  0.0 0.0 
CAF       System.Environment   65   0 0.0 0.0  0.0 0.0 
CAF       Data.ByteString.Lazy.Char8 54   0 0.0 0.0  0.0 0.0 
CAF       Main       52   0 0.0 0.0  0.0 0.0 
    transformOne    Main       99   0 0.0 0.0  0.0 0.0 
    readLexicon    Main       96   0 0.0 0.0  0.0 0.0 
    readLexicon.stripR  Main       97   1 0.0 0.0  0.0 0.0 
    main      Main       90   1 0.0 0.0  0.0 0.0 

GÜNCELLEME: aşağıdaki program örnek verileri oluşturmak için kullanılabilir. Bir argüman, oluşturulan veri kümesindeki satır sayısını bekler. Oluşturulan veriler sample.txt dosyasına kaydedilir. Onunla 900k satırlık veri kümesi oluşturduğumda (generateSample.exe 900000 çalıştırarak), oluşturulan veri kümesi, yığın taşmasıyla yukarıdaki programın başarısız olmasını sağlar (yığın boyutu 1 GB'ye ayarlandı). Ortaya çıkan veri kümesi yaklaşık 20 MB'dir.

module Main where 
import System.Environment (getArgs) 
import Data.List (intercalate, permutations) 

generate :: Int -> [(String,String,String)] 
generate n = take n $ zip3 (f "banana") (f "ruanaba") (f "kikiriki") 
    where 
     f = cycle . permutations 

main :: IO() 
main = do 
    (n:_) <- getArgs 
    let flattened = unlines . map f $ generate (read n :: Int) 
    writeFile "sample.txt" flattened 
    where 
     f (w1,w2,w3) = intercalate "\t" [w1, w2, w3] 
+4

Eh, 'sort' sabit aralıkta çalışamaz: herhangi bir çıktı üretmeden önce tüm girdisini tüketmesi (ve saklaması) gerekir. –

+1

GHC'nin bu sefer problemle ilgili olduğunu düşünmüyorum, ancak soru raporunda GHC sürümünü her zaman profiler raporuna eklemelisiniz. – dfeuer

+0

@dfeuer GHC sürümü 7.8.3 – Glaukon

cevap

0

Bana öyle geliyor size korkunç uzay karmaşıklığı ile, bir naif en uzun ortak alt dize uyguladık (en azından O (n^2)). Katılığın onunla hiçbir ilgisi yoktur.

Dinamik bir programlama algoritması uygulamak isteyeceksiniz. string-similarity paketinde veya Diff paketinin bağırsaklarındaki lcs işlevinde ilham bulabilirsiniz.

İlgili konular