จะอธิบายข้อมูลโปรไฟล์ของเครื่องกำเนิดสุ่ม Haskell สำหรับการใช้งานหน่วยความจำขนาดใหญ่และความเร็วต่ำได้อย่างไร

ฉันต้องการโปรไฟล์ความเร็วของเครื่องกำเนิดสุ่ม Haskell และกรณีทดสอบของฉันคือการสร้างตัวเลขสุ่มที่มีความแม่นยำสองเท่า 1000,000 ช่วงจากศูนย์ถึงหนึ่งและคำนวณผลรวม นี่คือรหัสของฉัน:

import System.Random
import System.Environment
import Control.Monad.State
import Data.Time.Clock
type Seed = Int
intDayTime :: IO Int
intDayTime = getCurrentTime >>= return.(floor.utctDayTime :: UTCTime->Int)
n = 1000000 :: Int
main :: IO ()
main = do
    calc <- getArgs >>= return . (read ::(String->Int)).head
    seed <- intDayTime 
    let calcit :: Int->Double 
        calcit 1 = calc1 n seed 
        calcit 2 = calc2 n (mkStdGen seed)
        calcit _ = error "error calculating" 
     in print $ calcit calc
calc1 ::Int->Seed->Double
calc1 n initSeed = 
  let next      :: Seed->(Double,Seed) -- my simple random number generator, just for test
      myRandGen :: State Seed Double 
      calc      :: Int->Int->Seed->Double->Double 
      next seed = let x = (1103515245 * seed + 12345) `mod` 1073741824 in (((fromIntegral x)/1073741824),x)
      myRandGen = state next 
      calc _ 0 _ r = r
      calc n c s r = calc n (c-1) ns (r + nv)
        where (nv,ns) = runState myRandGen s 
     in calc n n initSeed 0
calc2 ::Int->StdGen->Double
calc2 n initSeed = 
    let myRandGen :: State StdGen Double 
        calc      :: Int->Int->StdGen->Double->Double 
        next      :: StdGen->(Double,StdGen)
        next gen  = randomR (0,1) gen
        myRandGen = state next
        calc _ 0 _ r = r
        calc n c s r = calc n (c-1) ns (r + nv)
          where (nv,ns) = runState myRandGen s 
       in calc n n initSeed 0

และฉันก็รวบรวมโค้ดด้วย

ghc profRandGen.hs -O3 -prof -fprof-auto -rtsopts

วิ่งด้วย

./profRandGen.exe 1 +RTS -o # for calc1
./profRandGen.exe 2 +RTS -o # for calc2

และข้อมูลโปรไฟล์สำหรับ calc1 คือ

total time  =        0.10 secs   (105 ticks @ 1000 us, 1 processor)
total alloc = 128,121,344 bytes  (excludes profiling overheads)

ข้อมูลโปรไฟล์สำหรับ calc1 คือ

total time  =        1.48 secs   (1479 ticks @ 1000 us, 1 processor)
total alloc = 2,008,077,560 bytes  (excludes profiling overheads)

ฉันเข้าใจได้ว่าตัวสร้างแบบสุ่มใน System.Random จะช้าลง แต่ทำไมมันถึงช้าลงมากและทำไมมันถึงจัดสรรหน่วยความจำมากขึ้น

ฉันใช้ การเรียกซ้ำแบบหาง ในโค้ดของฉันและคอมไพล์ด้วยตัวเลือก -O2 -fforce-recomp เหตุใดฉันจึงไม่ได้รับการใช้งานหน่วยความจำอย่างต่อเนื่อง

มีอะไรผิดปกติในรหัสของฉันหรือเปล่า? ตัวอย่างเช่น เป็นเพราะการประเมินแบบขี้เกียจที่การเรียกซ้ำส่วนท้ายไม่ได้รับการปรับให้เหมาะสมและมีการจัดสรรหน่วยความจำจำนวนมากหรือไม่ หากเป็นเช่นนั้น โปรแกรมนี้จะได้รับการเพิ่มประสิทธิภาพเพิ่มเติมได้อย่างไร


person Alaya    schedule 31.01.2015    source แหล่งที่มา
comment
การใช้หน่วยความจำอย่างต่อเนื่อง? กับฮาสเคลเหรอ? ฉันไม่เคยได้ยินเกี่ยวกับเรื่องนั้นมาก่อนเลยตั้งแต่มีอะไรมากมายในการจัดสรรของ Haskell ฉันเดาว่านั่นเป็นส่วนหนึ่งของประเด็น จริงๆ แล้ว... ฉันคิดว่าไม่มี -O3 มีเพียง -O2 เท่านั้น นอกจากนี้ อย่าลืมมี -fforce-recomp   -  person MaiaVictor    schedule 31.01.2015
comment
เอ่อ... ฉันได้ยินมาว่าถ้าฉันคอมไพล์ด้วย -O2 การเรียกหางจะถูกปรับให้เหมาะสม ดังนั้นฉันเดาว่าฉันอาจได้รับการใช้งานหน่วยความจำอย่างต่อเนื่อง...   -  person Alaya    schedule 31.01.2015
comment
คุณสามารถไว้วางใจการเพิ่มประสิทธิภาพการโทรแบบหางได้อย่างปลอดภัย และฟังก์ชันจะไม่ขยายสแต็ก ด้วยวิธีนี้ หากคุณเขียนฟังก์ชันที่ไม่ทำอะไรเลยนอกจากบวกตัวเลขเข้าด้วยกัน ฉันเดาว่าฟังก์ชันนั้นจะต้องใช้หน่วยความจำอย่างต่อเนื่อง แต่ทันทีที่คุณทำอะไรก็ตามที่ต้องการการจัดสรร (ซึ่งเกือบจะเป็นอะไรก็ได้ใน Haskell) คุณจะต้องจ่ายเงินเพื่อสิ่งนั้น นั่นคือการเดาของฉัน - ฉันคิดว่ามันไม่น่าเป็นไปได้มากที่คุณจะได้รับโปรแกรม Haskell หน่วยความจำคงที่จริง แต่ฉันอาจผิด   -  person MaiaVictor    schedule 31.01.2015
comment
ทำให้การสะสมของคุณเข้มงวด   -  person Daniel Wagner    schedule 31.01.2015
comment
@Viclib จากประสบการณ์ของฉัน มันเกือบจะเป็นเรื่องธรรมดามากที่จะปรับลูปให้เหมาะสมเพื่อให้พวกมันทำงานในพื้นที่คงที่หากคุณขี้เกียจ ดู means ตัวอย่างใน Real World Haskell ที่นี่ เวอร์ชันแบบเรียกซ้ำแบบหางและแบบสะสมอย่างเคร่งครัดจะทำงานในพื้นที่คงที่   -  person MasterMastic    schedule 31.01.2015
comment
@Viclib พื้นที่คงที่ไม่ได้หมายความว่าไม่มีการจัดสรร หมายความว่าหน่วยความจำจะถูกปลดปล่อยอย่างรวดเร็วตามที่ได้รับการจัดสรร   -  person Carl    schedule 31.01.2015


คำตอบ (1)


ดังที่ Daniel Wagner กล่าวในความคิดเห็น ผู้สะสมสถานะของคุณไม่ได้เข้มงวด ก่อนอื่น ให้ลองนำเข้า Control.Monad.State.Strict นั่นอาจจะเพียงพอแล้ว! มิฉะนั้น คุณจะต้องแก้ไข myRandGen เพื่อให้บังคับตัวสร้างใหม่ได้ละเอียดยิ่งขึ้นก่อนที่จะเปลี่ยนในสถานะ

person sclv    schedule 22.02.2015