GHC ไม่บันทึกฟังก์ชัน
อย่างไรก็ตามจะคำนวณนิพจน์ใด ๆ ที่กำหนดในโค้ดได้มากที่สุดครั้งละครั้งต่อครั้งที่มีการป้อน lambda-expression โดยรอบหรือมากที่สุดครั้งเดียวหากอยู่ในระดับบนสุด การพิจารณาว่านิพจน์แลมบ์ดาอยู่ที่ใดอาจเป็นเรื่องยุ่งยากเล็กน้อยเมื่อคุณใช้น้ำตาลวากยสัมพันธ์เช่นในตัวอย่างของคุณดังนั้นเรามาแปลงสิ่งเหล่านี้เป็นไวยากรณ์ที่ได้รับการ desugared ที่เทียบเท่ากัน:
m1' = (!!) (filter odd [1..]) -- NB: See below!
m2' = \n -> (!!) (filter odd [1..]) n
(หมายเหตุ: จริงๆแล้วรายงาน Haskell 98 อธิบายถึงส่วนของตัวดำเนินการด้านซ้ายเช่นเดียว(a %)
กับที่เทียบเท่า\b -> (%) a b
แต่ GHC จะแยก(%) a
ออกเป็นส่วนนี้มีความแตกต่างกันในทางเทคนิคเนื่องจากสามารถแยกแยะseq
ได้ฉันคิดว่าฉันอาจส่งตั๋ว GHC Trac เกี่ยวกับเรื่องนี้)
ให้นี้คุณสามารถเห็นในที่m1'
แสดงออกfilter odd [1..]
ไม่อยู่ในที่ใด ๆ แลมบ์ดาแสดงออกจึงจะได้รับการคำนวณครั้งเดียวต่อการทำงานของโปรแกรมของคุณในขณะที่ในm2'
, filter odd [1..]
จะได้รับการคำนวณในแต่ละครั้งที่แลมบ์ดาแสดงออกถูกป้อนคือ m2'
ในสายของแต่ละ ซึ่งอธิบายถึงความแตกต่างของเวลาที่คุณเห็น
จริงๆแล้ว GHC บางเวอร์ชันที่มีตัวเลือกการเพิ่มประสิทธิภาพบางอย่างจะแบ่งปันค่ามากกว่าที่คำอธิบายข้างต้นระบุไว้ ซึ่งอาจเป็นปัญหาได้ในบางสถานการณ์ ตัวอย่างเช่นพิจารณาฟังก์ชัน
f = \x -> let y = [1..30000000] in foldl' (+) 0 (y ++ [x])
GHC อาจสังเกตว่าy
ไม่ขึ้นอยู่กับx
และเขียนฟังก์ชันใหม่เป็น
f = let y = [1..30000000] in \x -> foldl' (+) 0 (y ++ [x])
ในกรณีนี้เวอร์ชันใหม่มีประสิทธิภาพน้อยกว่ามากเนื่องจากจะต้องอ่านข้อมูลประมาณ 1 GB จากหน่วยความจำที่y
จัดเก็บในขณะที่เวอร์ชันดั้งเดิมจะทำงานในพื้นที่คงที่และพอดีกับแคชของโปรเซสเซอร์ ในความเป็นจริงภายใต้ GHC 6.12.1, ฟังก์ชั่นf
เป็นเกือบสองเท่าอย่างรวดเร็วเมื่อรวบรวมได้โดยไม่ต้อง-O2
เพิ่มประสิทธิภาพกว่าที่มันจะรวบรวมกับ
seq
m1 10000000) มีความแตกต่างแม้ว่าเมื่อไม่มีการระบุแฟล็กการปรับให้เหมาะสม และตัวแปร "f" ทั้งสองของคุณมีที่อยู่อาศัยสูงสุด 5356 ไบต์โดยไม่คำนึงถึงการเพิ่มประสิทธิภาพอย่างไรก็ตาม (มีการจัดสรรรวมน้อยลงเมื่อใช้ -O2)