2012-05-15 11 views
5

State Monad'da çalışan bir simülasyonda bellek kullanımını ve GC zamanını nasıl azaltacağımı bulmakta biraz sorun yaşıyorum. Şu anda derlenmiş alanı taşmasını önlemek için derlenmiş kodu +RTS -K100M ile çalıştırmam gerekiyor ve GC istatistikleri oldukça iğrenç (aşağıya bakınız).Bir simülasyonda bellek ayırma/GC kontrol ediliyor mu?

İşte kodun ilgili kod parçacıklarıdır. Komple, çalışan (GHC 7.4.1) kodu http://hpaste.org/68527 adresinde bulunabilir. yığın açısından

-- Lone algebraic data type holding the simulation configuration. 
data SimConfig = SimConfig { 
     numDimensions :: !Int   -- strict 
    , numWalkers :: !Int   -- strict 
    , simArray  :: IntMap [Double] -- strict spine 
    , logP   :: Seq Double  -- strict spine 
    , logL   :: Seq Double  -- strict spine 
    , pairStream :: [(Int, Int)] -- lazy (infinite) list of random vals 
    , doubleStream :: [Double]  -- lazy (infinite) list of random vals 
    } deriving Show 

-- The transition kernel for the simulation. 
simKernel :: State SimConfig() 
simKernel = do 
    config <- get 
    let arr = simArray  config 
    let n  = numWalkers config 
    let d  = numDimensions config 
    let rstm0 = pairStream config 
    let rstm1 = doubleStream config 
    let lp = logP   config 
    let ll = logL   config 

    let (a, b) = head rstm0       -- uses random stream  
    let z0 = head . map affineTransform $ take 1 rstm1 -- uses random stream 
      where affineTransform a = 0.5 * (a + 1)^2 


    let proposal = zipWith (+) r1 r2 
      where r1 = map (*z0)  $ fromJust (IntMap.lookup a arr) 
        r2 = map (*(1-z0)) $ fromJust (IntMap.lookup b arr) 

    let logA = if val > 0 then 0 else val 
      where val = logP_proposal + logL_proposal - (lp `index` (a - 1)) - (ll `index` (a - 1)) + ((fromIntegral n - 1) * log z0) 
        logP_proposal = logPrior proposal 
        logL_proposal = logLikelihood proposal 

    let cVal  = (rstm1 !! 1) <= exp logA   -- uses random stream 

    let newConfig = SimConfig { simArray = if cVal 
              then IntMap.update (\_ -> Just proposal) a arr 
              else arr 
           , numWalkers = n 
           , numDimensions = d 
           , pairStream = drop 1 rstm0 
           , doubleStream = drop 2 rstm1 
           , logP = if cVal 
             then Seq.update (a - 1) (logPrior proposal) lp 
             else lp 
           , logL = if cVal 
             then Seq.update (a - 1) (logLikelihood proposal) ll 
             else ll 
           } 

    put newConfig 

main = do 
    -- (some stuff omitted) 
    let sim = logL $ (`execState` initConfig) . replicateM 100000 $ simKernel 
    print sim 

, bir profil System.Random fonksiyonları, (,) ek olarak, bellek suçluların olduklarını sufle gibi görünüyor. Doğrudan bir resim ekleyemiyorum, ancak burada bir yığın profili görebilirsiniz: http://i.imgur.com/5LKxX.png.

Bu şeylerin varlığını nasıl azaltacağımız konusunda hiçbir fikrim yok. Rastgele değişkenler State monad dışında üretilir (jeneratörü her yinelemede ayırmaktan kaçınmak için) ve simKernel'un içinde bulunan (,)'un tek örneğinin, simülasyon konfigürasyonunda yer alan tembel listeden (pairStream) bir çift kopyalarken ortaya çıktığına inanıyorum. aşağıdaki gibi GC içeren

istatistikler, aşağıdaki gibidir:

1,220,911,360 bytes allocated in the heap 
    787,192,920 bytes copied during GC 
    186,821,752 bytes maximum residency (10 sample(s)) 
     1,030,400 bytes maximum slop 
      449 MB total memory in use (0 MB lost due to fragmentation) 

            Tot time (elapsed) Avg pause Max pause 
    Gen 0  2159 colls,  0 par 0.80s 0.81s  0.0004s 0.0283s 
    Gen 1  10 colls,  0 par 0.96s 1.09s  0.1094s 0.4354s 

    INIT time 0.00s ( 0.00s elapsed) 
    MUT  time 0.95s ( 0.97s elapsed) 
    GC  time 1.76s ( 1.91s elapsed) 
    EXIT time 0.00s ( 0.00s elapsed) 
    Total time 2.72s ( 2.88s elapsed) 

    %GC  time  64.9% (66.2% elapsed) 

    Alloc rate 1,278,074,521 bytes per MUT second 

    Productivity 35.1% of total user, 33.1% of total elapsed 

Ve yine, daha simülasyonu çalıştırmak için maksimum yığın boyutu yükseltmek gerekir. Biliyorum, bir yerde büyük bir bina olmalı ... ama nerede olduğunu anlayamıyorum?

Yığın/yığın ayırma ve GC'yi bu gibi bir sorunu nasıl geliştirebilirim? Bir thunk'un nerede olabileceğini nasıl tanımlayabilirim? State monad'ın kullanımı yanlış mı?

-

GÜNCELLEME:

Ben -fprof-auto ile derlerken Profilcinin çıkışı üzerinde bakmayı ihmal. Tam olarak bu yorumlamak nasıl emin değilim

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

MAIN        MAIN        58   0 0.0 0.0 100.0 100.0 
main        Main        117   0 0.0 0.0 100.0 100.0 
    main.randomList     Main        147   1 62.0 55.5 62.0 55.5 
    main.arr      Main        142   1 0.0 0.0  0.0 0.0 
    streamToAssocList    Main        143   1 0.0 0.0  0.0 0.0 
    streamToAssocList.go   Main        146   5 0.0 0.0  0.0 0.0 
    main.pairList     Main        137   1 0.0 0.0  9.5 16.5 
    consPairStream     Main        138   1 0.7 0.9  9.5 16.5 
    consPairStream.ys    Main        140   1 4.3 7.8  4.3 7.8 
    consPairStream.xs    Main        139   1 4.5 7.8  4.5 7.8 
    main.initConfig     Main        122   1 0.0 0.0  0.0 0.0 
    logLikelihood     Main        163   0 0.0 0.0  0.0 0.0 
    logPrior      Main        161   5 0.0 0.0  0.0 0.0 
    main.sim      Main        118   1 1.0 2.2 28.6 28.1 
    simKernel      Main        120   0 4.8 5.1 27.6 25.8 

ama rastgele çiftlerde tembel akışı, randomList ürkmek beni yapar: Bu çıktı başıdır. Bunun nasıl geliştirilebileceği hakkında hiçbir fikrim yok.

+0

çıkış sonuçlarının istediğiniz ise

Fikrim yok performansta anlık bir artış. Yine de, çalışma zamanında + RTS -K100M kullanmak zorundayım, bu yüzden büyük bir thunk hala bir yere inşa olduğunu tahmin ediyorum. Kodun güncelleştirilmiş bir anlık görüntüsü burada: http://hpaste.org/68532 ve geliştirilmiş bir yığın profili burada: http://i.imgur.com/YzoNE.png. – jtobin

+0

Ayrıca "ghc -O2" kullanıyor olduğunuzu farz ediyorum. –

+0

Sağ; ghc --make -O2 blah.hs -fllvm -funbox-strict-fields -rtsopts' ile derleniyor. – jtobin

cevap

3

Hpaste'yi çalışma örneğiyle güncelledim. Bu zanlılar benziyor: üç SimConfig alanda

  • Eksik katılık ek açıklamalar: simArray, logP ve logL

newConfig nedeniyle varlık State-simKernel döngü hiç değerlendirilmemiştir

  •  
        data SimConfig = SimConfig { 
          numDimensions :: !Int   -- strict 
         , numWalkers :: !Int   -- strict 
         , simArray  :: !(IntMap [Double]) -- strict spine 
         , logP   :: !(Seq Double)  -- strict spine 
         , logL   :: !(Seq Double)  -- strict spine 
         , pairStream :: [(Int, Int)] -- lazy 
         , doubleStream :: [Double]  -- lazy 
         } deriving Show 
    
      tembel. Başka bir alternatif yerine sıkı State monad kullanmak olacaktır.

      put $! newConfig 
      
    • execState ... replicateM da Thunks kurar.Başlangıçta bir foldl' ile bu yerini ve içine kat execState taşındım ama replicateM_ yılında takas düşünüyorum olacağını okumak eşdeğerdir ve kolaydır: replicateM ile yerini almış

      let sim = logL $ execState (replicateM_ epochs simKernel) initConfig 
      -- sim = logL $ foldl' (const . execState simKernel) initConfig [1..epochs] 
      

    Ve mapM .. replicate için birkaç görüşme . Özellikle bellek kullanımını oldukça azalttığı için consPairList'da dikkat çekicidir. İyileştirme için hala bir yer var ama en düşük asılı meyve güvensiz içmeyi önlüyor ... bu yüzden durdum. istatistikler

     
    fromList [-4.287033457733427,-1.8000404912760795,-5.581988678626085,-0.9362372340483293,-5.267791907985331] 
    

    Ama burada: Ben System.Random.MWC jeneratöre açık ve gözlenen ettik

     
        268,004,448 bytes allocated in the heap 
         70,753,952 bytes copied during GC 
         16,014,224 bytes maximum residency (7 sample(s)) 
         1,372,456 bytes maximum slop 
           40 MB total memory in use (0 MB lost due to fragmentation) 
    
                Tot time (elapsed) Avg pause Max pause 
        Gen 0  490 colls,  0 par 0.05s 0.05s  0.0001s 0.0012s 
        Gen 1   7 colls,  0 par 0.04s 0.05s  0.0076s 0.0209s 
    
        INIT time 0.00s ( 0.00s elapsed) 
        MUT  time 0.12s ( 0.12s elapsed) 
        GC  time 0.09s ( 0.10s elapsed) 
        EXIT time 0.00s ( 0.00s elapsed) 
        Total time 0.21s ( 0.22s elapsed) 
    
        %GC  time  42.2% (45.1% elapsed) 
    
        Alloc rate 2,241,514,569 bytes per MUT second 
    
        Productivity 57.8% of total user, 53.7% of total elapsed 
    
  • +0

    Vay. ADT kayıt türlerine sadece katılık ek açıklamaları eklemek hafıza kullanımını * plummet * yapar. Sanırım, 'katı-spined' veri yapısının anlamını yanlış anlamışım, çünkü ben orada katılık ek açıklamaları eklemeyi bile denemedim. Sanırım "execState" i katlamalıyım diye anlamanız beni epey bir zaman alacaktı. Harika şeyler, teşekkürler. – jtobin

    +1

    @jtobin Değerin, iç sıkılık garantileri ne olursa olsun, WHNF'ye göre değerlendirilmesi gerekir. Ayrıca 'execState' çağrısını daha net olmak için 'foldl'' yerine' replicateM_' kullanarak güncelledim. Eğer “runState” (replicateM 10 (return()))() 'deki sonuçlara bakarsanız, GHCi'deki“ replicateM_ ”değişkenine karşı, bunun neden gerekli olduğu açık olmalıdır. –