Haskell threads heap overflow meskipun total penggunaan memori hanya 22Mb?

114

Saya mencoba untuk memparalelkan pelacak sinar. Ini berarti saya memiliki daftar penghitungan kecil yang sangat panjang. Program vanilla berjalan pada adegan tertentu dalam 67,98 detik dan penggunaan memori total 13 MB dan produktivitas 99,2%.

Dalam upaya pertama saya, saya menggunakan strategi paralel parBufferdengan ukuran buffer 50. Saya memilih parBufferkarena ini berjalan melalui daftar hanya secepat bunga api dikonsumsi, dan tidak memaksa tulang belakang daftar seperti parList, yang akan menggunakan banyak memori karena daftarnya sangat panjang. Dengan -N2, itu berjalan dalam waktu 100,46 detik dan 14 MB penggunaan memori total dan produktivitas 97,8%. Informasi percikannya adalah:SPARKS: 480000 (476469 converted, 0 overflowed, 0 dud, 161 GC'd, 3370 fizzled)

Proporsi percikan api yang mendesis menunjukkan bahwa butiran bunga api terlalu kecil, jadi selanjutnya saya mencoba menggunakan strategi parListChunk, yang membagi daftar menjadi beberapa bagian dan menciptakan percikan untuk setiap bagian. Saya mendapatkan hasil terbaik dengan ukuran sebagian 0.25 * imageWidth. Program ini berjalan dalam 93,43 detik dan total penggunaan memori 236 MB dan produktivitas 97,3%. Informasi spark adalah: SPARKS: 2400 (2400 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled). Saya percaya penggunaan memori yang jauh lebih besar adalah karena parListChunkmemaksa tulang punggung daftar.

Kemudian saya mencoba menulis strategi saya sendiri yang dengan malas membagi daftar menjadi beberapa bagian dan kemudian meneruskan potongan tersebut parBufferdan menggabungkan hasilnya.

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

Ini berjalan dalam 95,99 detik dan 22MB dari total penggunaan memori dan produktivitas 98,8%. Ini berhasil dalam arti bahwa semua percikan api diubah dan penggunaan memori jauh lebih rendah, namun kecepatannya tidak ditingkatkan. Berikut adalah gambar bagian dari profil eventlog.Profil log peristiwa

Seperti yang Anda lihat, utas dihentikan karena heap overflow. Saya mencoba menambahkan +RTS -M1Gyang meningkatkan ukuran heap default hingga 1Gb. Hasilnya tidak berubah. Saya membaca bahwa utas utama Haskell akan menggunakan memori dari heap jika tumpukannya meluap, jadi saya juga mencoba meningkatkan ukuran tumpukan default juga +RTS -M1G -K1Gtetapi ini juga tidak berdampak.

Apakah ada hal lain yang bisa saya coba? Saya dapat memposting info profil yang lebih rinci untuk penggunaan memori atau eventlog jika diperlukan, saya tidak memasukkan semuanya karena ini adalah informasi yang banyak dan saya tidak berpikir semua itu perlu disertakan.

EDIT: Saya membaca tentang dukungan multicore Haskell RTS , dan ini berbicara tentang adanya HEC (Haskell Execution Context) untuk setiap inti. Setiap HEC berisi, antara lain, Area Alokasi (yang merupakan bagian dari satu heap bersama). Setiap kali Area Alokasi HEC habis, pengumpulan sampah harus dilakukan. Tampaknya opsi RTS untuk mengontrolnya, -A. Saya mencoba -A32M tetapi tidak melihat perbedaan.

EDIT2: Ini adalah tautan ke repo github yang didedikasikan untuk pertanyaan ini . Saya telah menyertakan hasil pembuatan profil di folder profiling.

EDIT3: Ini sedikit kode yang relevan:

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)))

Kisi adalah pelampung acak yang dihitung sebelumnya dan digunakan oleh colorPixel. Jenisnya colorPixeladalah:

 colorPixel :: World -> ((Float,Float),([(Float,Float)],[(Float,Float)])) -> Color
Justin Raymond
sumber
2
Bisakah Anda memberikan komit yang tepat di mana Anda mencoba concat $ withStrategy …? Saya tidak bisa mereproduksi perilaku ini 6008010, yang merupakan komitmen terdekat untuk hasil edit Anda.
Zeta
3
Saya membuat repo khusus jadi saya tidak sengaja mengacaukannya. Saya juga menyertakan semua informasi profil.
Justin Raymond
@dfeuer ketika saya mengatakan mendefinisikan strategi saya sendiri, saya tidak bermaksud Strategy. Seharusnya memilih kata yang lebih baik. Selain itu, masalah heap overflow terjadi dengan parListChunkdan parBufferjuga.
Justin Raymond

Jawaban:

2

Bukan solusi untuk masalah Anda, tapi petunjuk penyebabnya:

Haskell tampaknya sangat konservatif dalam penggunaan kembali memori dan ketika penerjemah melihat potensi untuk merebut kembali blok memori, itu berlaku untuk itu. Deskripsi masalah Anda sesuai dengan perilaku GC minor yang dijelaskan di sini (bawah) https://wiki.haskell.org/GHC/Memory_Management .

Data baru dialokasikan di 512kb "pembibitan". Setelah habis, "GC minor" terjadi - ia memindai kamar bayi dan membebaskan nilai yang tidak digunakan.

Jadi, jika Anda memotong data menjadi bagian yang lebih kecil, Anda mengaktifkan mesin untuk melakukan pembersihan lebih awal - GC dimulai.

Thinkeye
sumber