ฉันกำลังพยายามขนานเรย์ - เทรเซอร์ ซึ่งหมายความว่าฉันมีรายการการคำนวณขนาดเล็กที่ยาวมาก โปรแกรมวานิลลาทำงานบนฉากเฉพาะใน 67.98 วินาทีและ 13 MB ของการใช้หน่วยความจำทั้งหมดและผลผลิต 99.2%
ในความพยายามครั้งแรกของฉันฉันใช้กลยุทธ์คู่ขนานที่parBuffer
มีขนาดบัฟเฟอร์ 50 ฉันเลือกparBuffer
เพราะมันเดินผ่านรายการได้เร็วที่สุดเท่าที่ประกายไฟจะหมดและไม่บังคับกระดูกสันหลังของรายการเช่นparList
ซึ่งจะใช้หน่วยความจำมาก เนื่องจากรายการยาวมาก ด้วย-N2
มันทำงานในเวลา 100.46 วินาทีและ 14 MB ของการใช้หน่วยความจำทั้งหมดและผลผลิต 97.8% ข้อมูลจุดประกายคือ:SPARKS: 480000 (476469 converted, 0 overflowed, 0 dud, 161 GC'd, 3370 fizzled)
ประกายไฟที่มอดลงในสัดส่วนที่มากบ่งบอกว่าความละเอียดของประกายไฟมีขนาดเล็กเกินไปดังนั้นต่อไปฉันจึงลองใช้กลยุทธ์parListChunk
ซึ่งแยกรายการออกเป็นชิ้น ๆ และสร้างประกายสำหรับแต่ละชิ้น ฉันได้ผลลัพธ์ที่ดีที่สุดด้วยขนาดของ0.25 * imageWidth
. โปรแกรมทำงานใน 93.43 วินาทีและ 236 MB ของการใช้หน่วยความจำทั้งหมดและประสิทธิภาพการผลิต 97.3% ข้อมูลจุดประกายคือ: SPARKS: 2400 (2400 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled)
. ฉันเชื่อว่าการใช้หน่วยความจำมากขึ้นเป็นเพราะparListChunk
บังคับกระดูกสันหลังของรายการ
จากนั้นฉันก็พยายามเขียนกลยุทธ์ของตัวเองที่แบ่งรายการออกเป็นชิ้น ๆ อย่างเกียจคร้านแล้วส่งผ่านชิ้นส่วนไปparBuffer
และเชื่อมต่อผลลัพธ์
concat $ withStrategy (parBuffer 40 rdeepseq) (chunksOf 100 (map colorPixel pixels))
สิ่งนี้ทำงานใน 95.99 วินาทีและ 22MB ของการใช้หน่วยความจำทั้งหมดและประสิทธิภาพการทำงาน 98.8% สิ่งนี้ประสบความสำเร็จในแง่ที่ว่าประกายไฟทั้งหมดกำลังถูกแปลงและการใช้หน่วยความจำต่ำกว่ามากอย่างไรก็ตามความเร็วไม่ได้รับการปรับปรุง นี่คือภาพส่วนหนึ่งของโปรไฟล์ eventlog
ดังที่คุณเห็นว่าเธรดกำลังหยุดทำงานเนื่องจากฮีปล้น ฉันพยายามเพิ่ม+RTS -M1G
ซึ่งจะเพิ่มขนาดฮีปเริ่มต้นจนถึง 1Gb ผลลัพธ์ไม่เปลี่ยนแปลง ฉันอ่านว่าเธรดหลักของ Haskell จะใช้หน่วยความจำจากฮีปหากสแต็กล้นดังนั้นฉันจึงพยายามเพิ่มขนาดสแต็กเริ่มต้นด้วย+RTS -M1G -K1G
แต่สิ่งนี้ก็ไม่มีผลกระทบเช่นกัน
มีอะไรให้ลองทำอีกไหม? ฉันสามารถโพสต์ข้อมูลการทำโปรไฟล์โดยละเอียดเพิ่มเติมสำหรับการใช้หน่วยความจำหรือบันทึกเหตุการณ์ได้หากจำเป็นฉันไม่ได้รวมไว้ทั้งหมดเนื่องจากเป็นข้อมูลจำนวนมากและฉันไม่คิดว่าจำเป็นต้องรวมไว้ทั้งหมด
แก้ไข: ฉันกำลังอ่านเกี่ยวกับการสนับสนุนมัลติคอร์ของHaskell RTSและพูดถึงการมี HEC (Haskell Execution Context) สำหรับแต่ละคอร์ HEC แต่ละตัวประกอบด้วยพื้นที่จัดสรร (ซึ่งเป็นส่วนหนึ่งของฮีปที่แบ่งใช้ร่วมกัน) เมื่อใดก็ตามที่พื้นที่จัดสรรของ HEC หมดลงจะต้องดำเนินการจัดเก็บขยะ ดูเหมือนจะเป็นตัวเลือก RTSเพื่อควบคุม -A ฉันลอง -A32M แต่ไม่เห็นความแตกต่าง
EDIT2: นี่คือการเชื่อมโยงไป GitHub Repo ทุ่มเทให้กับคำถามนี้ ฉันได้รวมผลลัพธ์การทำโปรไฟล์ไว้ในโฟลเดอร์การทำโปรไฟล์แล้ว
แก้ไข 3: นี่คือบิตโค้ดที่เกี่ยวข้อง:
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)))
กริดคือการลอยแบบสุ่มที่มีการคำนวณล่วงหน้าและใช้โดย colorPixel ประเภทของcolorPixel
คือ:
colorPixel :: World -> ((Float,Float),([(Float,Float)],[(Float,Float)])) -> Color
Strategy
เมื่อฉันกล่าวว่าการกำหนดกลยุทธ์ของฉันเองฉันไม่ได้หมายความว่า ควรเลือกคำที่ดีกว่านี้ นอกจากนี้ยังมีปัญหากองล้นเกิดขึ้นกับparListChunk
และparBuffer
มากเกินไป
concat $ withStrategy …
หรือไม่? ฉันไม่สามารถสร้างพฤติกรรมนี้ซ้ำได้6008010
ซึ่งเป็นการกระทำที่ใกล้เคียงที่สุดกับการแก้ไขของคุณ