จะทราบได้อย่างไรว่าเหตุใดวิธีนี้จึงช้ามาก มีคำสั่งใดบ้างที่บอกฉันว่าเวลาส่วนใหญ่ในการคำนวณใช้เวลาส่วนไหนดังนั้นฉันจึงรู้ว่าโปรแกรม haskell ของฉันทำงานช้าหรือไม่
แม่นยำ! GHC มีเครื่องมือที่ยอดเยี่ยมมากมาย ได้แก่ :
กวดวิชาเกี่ยวกับการใช้เวลาและพื้นที่ profiling เป็นส่วนหนึ่งของโลกแห่งความจริง Haskell
สถิติ GC
ประการแรกตรวจสอบให้แน่ใจว่าคุณกำลังคอมไพล์ด้วย ghc -O2 และคุณอาจแน่ใจว่าเป็น GHC สมัยใหม่ (เช่น GHC 6.12.x)
สิ่งแรกที่เราทำได้คือตรวจสอบว่าการเก็บขยะไม่ใช่ปัญหา รันโปรแกรมของคุณด้วย + RTS -s
$ time ./A +RTS -s
./A +RTS -s
749700
9,961,432,992 bytes allocated in the heap
2,463,072 bytes copied during GC
29,200 bytes maximum residency (1 sample(s))
187,336 bytes maximum slop
**2 MB** total memory in use (0 MB lost due to fragmentation)
Generation 0: 19002 collections, 0 parallel, 0.11s, 0.15s elapsed
Generation 1: 1 collections, 0 parallel, 0.00s, 0.00s elapsed
INIT time 0.00s ( 0.00s elapsed)
MUT time 13.15s ( 13.32s elapsed)
GC time 0.11s ( 0.15s elapsed)
RP time 0.00s ( 0.00s elapsed)
PROF time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 13.26s ( 13.47s elapsed)
%GC time **0.8%** (1.1% elapsed)
Alloc rate 757,764,753 bytes per MUT second
Productivity 99.2% of total user, 97.6% of total elapsed
./A +RTS -s 13.26s user 0.05s system 98% cpu 13.479 total
ซึ่งให้ข้อมูลมากมายกับเราอยู่แล้ว: คุณมีฮีปเพียง 2M เท่านั้นและ GC ใช้เวลาถึง 0.8% จึงไม่ต้องกังวลว่าการจัดสรรจะเป็นปัญหา
โปรไฟล์เวลา
การรับโปรไฟล์เวลาสำหรับโปรแกรมของคุณนั้นตรงไปตรงมา: คอมไพล์ด้วย -prof -auto-all
$ ghc -O2 --make A.hs -prof -auto-all
[1 of 1] Compiling Main ( A.hs, A.o )
Linking A ...
และสำหรับ N = 200:
$ time ./A +RTS -p
749700
./A +RTS -p 13.23s user 0.06s system 98% cpu 13.547 total
ซึ่งสร้างไฟล์ A.prof ที่มี:
Sun Jul 18 10:08 2010 Time and Allocation Profiling Report (Final)
A +RTS -p -RTS
total time = 13.18 secs (659 ticks @ 20 ms)
total alloc = 4,904,116,696 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
numDivs Main 100.0 100.0
บ่งชี้ว่าเวลาทั้งหมดของคุณใช้ไปใน numDivs และยังเป็นที่มาของการจัดสรรทั้งหมดของคุณ
Heap Profiles
นอกจากนี้คุณยังสามารถแบ่งการจัดสรรเหล่านั้นได้ด้วยการรันด้วย + RTS -p -hy ซึ่งจะสร้าง A.hp ซึ่งคุณสามารถดูได้โดยการแปลงเป็นไฟล์ Postscript (hp2ps -c A.hp) โดยสร้าง:
ซึ่งบอกเราว่าไม่มีอะไรผิดปกติกับการใช้หน่วยความจำของคุณ: มันกำลังจัดสรรในพื้นที่คงที่
ดังนั้นปัญหาของคุณคือความซับซ้อนของอัลกอริทึมของ numDivs:
toInteger $ length [ x | x<-[2.. ((n `quot` 2)+1)], n `rem` x == 0] + 2
แก้ไขสิ่งนั้นซึ่งเป็นเวลาวิ่ง 100% ของคุณและทุกอย่างก็ทำได้ง่าย
การเพิ่มประสิทธิภาพ
นิพจน์นี้เป็นตัวเลือกที่ดีสำหรับการเพิ่มประสิทธิภาพสตรีมฟิวชั่นดังนั้นฉันจะเขียนใหม่เพื่อใช้Data.Vectorดังนี้:
numDivs n = fromIntegral $
2 + (U.length $
U.filter (\x -> fromIntegral n `rem` x == 0) $
(U.enumFromN 2 ((fromIntegral n `div` 2) + 1) :: U.Vector Int))
ซึ่งควรหลอมรวมเป็นลูปเดียวโดยไม่มีการจัดสรรฮีปที่ไม่จำเป็น นั่นคือมันจะมีความซับซ้อน (ตามปัจจัยคงที่) ดีกว่าเวอร์ชันรายการ คุณสามารถใช้เครื่องมือ ghc-core (สำหรับผู้ใช้ขั้นสูง) เพื่อตรวจสอบโค้ดกลางหลังการเพิ่มประสิทธิภาพ
การทดสอบนี้ ghc -O2 - สร้าง Z.hs
$ time ./Z
749700
./Z 3.73s user 0.01s system 99% cpu 3.753 total
ดังนั้นมันจึงลดเวลาในการทำงานสำหรับ N = 150 ลง 3.5x โดยไม่ต้องเปลี่ยนอัลกอริทึมเอง
สรุป
ปัญหาของคุณคือ numDivs เป็นเวลาวิ่งของคุณ 100% และมีความซับซ้อนมาก ลองนึกถึง numDivs และตัวอย่างเช่นสำหรับแต่ละ N ที่คุณสร้าง [2 .. n div
2 + 1] N ครั้ง ลองจดบันทึกเนื่องจากค่าต่างๆไม่เปลี่ยนแปลง
ในการวัดว่าฟังก์ชันใดของคุณเร็วกว่าให้พิจารณาใช้เกณฑ์ซึ่งจะให้ข้อมูลที่มีประสิทธิภาพทางสถิติเกี่ยวกับการปรับปรุงเวลาทำงานย่อยในระดับไมโครวินาที
ภาคผนวก
เนื่องจาก numDivs คือ 100% ของเวลาทำงานของคุณการสัมผัสส่วนอื่น ๆ ของโปรแกรมจะไม่สร้างความแตกต่างมากนักอย่างไรก็ตามเพื่อวัตถุประสงค์ในการเรียนการสอนเรายังสามารถเขียนซ้ำโดยใช้การหลอมรวมของสตรีม
นอกจากนี้เรายังสามารถเขียน trialList ใหม่และพึ่งพาฟิวชั่นเพื่อเปลี่ยนเป็นลูปที่คุณเขียนด้วยมือใน trialList2 ซึ่งเป็นฟังก์ชัน "สแกนคำนำหน้า" (aka scanl):
triaList = U.scanl (+) 0 (U.enumFrom 1 top)
where
top = 10^6
ในทำนองเดียวกันสำหรับโซล:
sol :: Int -> Int
sol n = U.head $ U.filter (\x -> numDivs x > n) triaList
ด้วยเวลาทำงานโดยรวมที่เท่ากัน แต่รหัสที่สะอาดกว่าเล็กน้อย
time
ยูทิลิตี้ที่ Don กล่าวถึงใน Time Profiles เป็นเพียงtime
โปรแกรมLinux ไม่มีใน Windows ดังนั้นสำหรับเวลาโปรไฟล์บน Windows (ทุกที่จริง) เห็นนี้คำถาม