Haskell iş parçacıkları yalnızca 22Mb toplam bellek kullanımına rağmen yığın taşıyor mu?


114

Bir ışın izleyiciyi paralelleştirmeye çalışıyorum. Bu, çok uzun bir küçük hesaplama listesine sahip olduğum anlamına geliyor. Vanilya programı belirli bir sahnede 67,98 saniye ve 13 MB toplam bellek kullanımı ve% 99,2 verimlilikle çalışır.

İlk denememde parBufferarabellek boyutu 50 olan paralel stratejiyi kullandım. parBufferListede yalnızca kıvılcımlar tüketildiği kadar hızlı yürüdüğü ve listenin omurgasını parListçok fazla bellek kullanan gibi zorlamadığı için seçtim. liste çok uzun olduğu için. İle -N2100,46 saniye ve 14 MB toplam bellek kullanımı ve% 97,8 verimlilikle çalıştı. Kıvılcım bilgileri:SPARKS: 480000 (476469 converted, 0 overflowed, 0 dud, 161 GC'd, 3370 fizzled)

Kabarık kıvılcımların büyük oranı, kıvılcımların tanecikliğinin çok küçük olduğunu gösteriyor, bu yüzden daha parListChunksonra listeyi parçalara bölen ve her parça için bir kıvılcım oluşturan stratejiyi kullanmayı denedim . Bir yığın boyutu ile en iyi sonuçları aldım 0.25 * imageWidth. Program 93,43 saniye ve 236 MB toplam bellek kullanımı ve% 97,3 verimlilikle çalıştı. Kıvılcım bilgiler şunlardır: SPARKS: 2400 (2400 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled). Çok daha fazla hafıza kullanımının parListChunklistenin omurgasını zorladığına inanıyorum .

Sonra, listeyi tembel bir şekilde parçalara ayıran ve ardından parçalara parBufferaktarıp sonuçları birleştiren kendi stratejimi yazmaya çalıştım .

 concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map colorPixel pixels))

Bu, 95,99 saniye ve 22 MB toplam bellek kullanımı ve% 98,8 üretkenlikle çalıştı. Bu, tüm kıvılcımların dönüştürüldüğü ve bellek kullanımının çok daha düşük olduğu, ancak hızın iyileştirilmediği anlamında başarılı oldu. İşte olay günlüğü profilinin bir kısmının görüntüsü.Olay günlüğü profili

Gördüğünüz gibi yığın taşmaları nedeniyle iş parçacıkları durduruluyor. +RTS -M1GVarsayılan yığın boyutunu 1 Gb'ye kadar artıran eklemeyi denedim . Sonuçlar değişmedi. Haskell ana iş parçacığının, yığın taşması durumunda yığından bellek kullanacağını okudum, bu yüzden varsayılan yığın boyutunu da artırmayı denedim, +RTS -M1G -K1Gancak bunun da bir etkisi olmadı.

Deneyebileceğim başka bir şey var mı? Gerekirse bellek kullanımı veya olay günlüğü için daha ayrıntılı profil oluşturma bilgileri gönderebilirim, hepsini dahil etmedim çünkü çok fazla bilgi var ve hepsinin dahil edilmesi gerektiğini düşünmedim.

DÜZENLEME: Haskell RTS çok çekirdekli desteği hakkında okuyordum ve her çekirdek için bir HEC (Haskell Yürütme Bağlamı) olduğundan bahsediyor. Her HEC, diğer şeylerin yanı sıra, bir Tahsis Alanını (tek bir paylaşılan yığının parçası olan) içerir. Herhangi bir HEC'nin Tahsis Alanı tükendiğinde, bir çöp toplama işlemi gerçekleştirilmelidir. Görünüşe göre onu kontrol etmek için bir RTS seçeneği , -A. -A32M'yi denedim ama fark görmedim.

DÜZENLEME2: İşte bu soruya adanmış bir github deposuna bağlantı . Profil oluşturma sonuçlarını profil oluşturma klasörüne ekledim.

DÜZENLEME3: İşte ilgili kod biti:

render :: [([(Float,Float)],[(Float,Float)])] -> World -> [Color]
render grids world = cs where 
  ps = [ (i,j) | j <- reverse [0..wImgHt world - 1] , i <- [0..wImgWd world - 1] ]
  cs = map (colorPixel world) (zip ps grids)
  --cs = withStrategy (parListChunk (round (wImgWd world)) rdeepseq) (map (colorPixel world) (zip ps grids))
  --cs = withStrategy (parBuffer 16 rdeepseq) (map (colorPixel world) (zip ps grids))
  --cs = concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map (colorPixel world) (zip ps grids)))

Izgaraları, önceden hesaplanabilir ve colorPixel.The türü tarafından kullanılan rasgele mantarlar olan colorPixelgeçerli:

 colorPixel :: World -> ((Float,Float),([(Float,Float)],[(Float,Float)])) -> Color

2
Denediğiniz kesin taahhüdü sağlayabilir misiniz concat $ withStrategy …? 6008010Düzenlemenize en yakın işlem olan bu davranışı içinde yeniden oluşturamıyorum.
Zeta

3
Özel bir repo yaptım, böylece kazara karıştırmam. Tüm profil bilgilerini de ekledim.
Justin Raymond

@dfeuer kendi stratejimi tanımla dediğimde demek istemedim Strategy. Daha iyi bir kelime seçmeliydin. Ayrıca, yığın taşması sorunu parListChunkve ile birlikte ortaya çıkar parBuffer.
Justin Raymond

Yanıtlar:


2

Sorununuzun çözümü değil, nedenine bir ipucu:

Haskell, belleğin yeniden kullanımında oldukça muhafazakar görünüyor ve yorumlayıcı bir bellek bloğunu geri kazanma potansiyelini gördüğünde, onun yerine geçer. Sorun açıklamanız, https://wiki.haskell.org/GHC/Memory_Management burada (altta) açıklanan küçük GC davranışına uyar .

Yeni veriler 512kb "fidanlıkta" tahsis edilir. Bittiğinde, "küçük GC" oluşur - çocuk odasını tarar ve kullanılmayan değerleri serbest bırakır.

Dolayısıyla, verileri daha küçük parçalara bölerseniz, motorun temizliği daha erken yapmasını sağlarsınız - GC devreye girer.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.