ฉันมีปัญหาในการทำให้ GHC เชี่ยวชาญฟังก์ชั่นที่มีข้อ จำกัด ด้านชั้นเรียน ผมมีตัวอย่างที่น้อยที่สุดของปัญหาของฉันที่นี่: Foo.hsและ Main.hs รวบรวมไฟล์สองไฟล์ (GHC 7.6.2, ghc -O3 Main
) และเรียกใช้
หมายเหตุ:
Foo.hs
ถูกปล้นจริง ๆ ถ้าคุณต้องการที่จะเห็นว่าทำไมข้อ จำกัด เป็นสิ่งจำเป็นที่คุณสามารถดูรหัสเล็ก ๆ น้อย ๆที่นี่ ถ้าฉันใส่รหัสในไฟล์เดียวหรือทำการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ อื่น ๆ อีกมากมายเพียงแค่ GHC inlines plusFastCyc
การเรียกร้องให้ สิ่งนี้จะไม่เกิดขึ้นในรหัสจริงเนื่องจากplusFastCyc
มีขนาดใหญ่เกินไปที่ GHC จะอินไลน์แม้ว่าจะถูกทำเครื่องหมายไว้INLINE
ก็ตาม ประเด็นก็คือจะต้องชำนาญการโทรplusFastCyc
ไม่ใช่แบบอินไลน์ plusFastCyc
ถูกเรียกในหลาย ๆ ที่ในรหัสจริงดังนั้นการทำซ้ำฟังก์ชันที่มีขนาดใหญ่จะไม่เป็นที่ต้องการแม้ว่าฉันจะบังคับให้ GHC ทำเช่นนั้น
รหัสที่น่าสนใจคือplusFastCyc
ในFoo.hs
ทำซ้ำที่นี่:
{-# INLINEABLE plusFastCyc #-}
{-# SPECIALIZE plusFastCyc ::
forall m . (Factored m Int) =>
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) #-}
-- Although the next specialization makes `fcTest` fast,
-- it isn't useful to me in my real program because the phantom type M is reified
-- {-# SPECIALIZE plusFastCyc ::
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int ->
-- FastCyc (VT U.Vector M) Int #-}
plusFastCyc :: (Num (t r)) => (FastCyc t r) -> (FastCyc t r) -> (FastCyc t r)
plusFastCyc (PowBasis v1) (PowBasis v2) = PowBasis $ v1 + v2
Main.hs
ไฟล์มีสองไดรเวอร์: vtTest
ซึ่งทำงานใน ~ 3 วินาทีและfcTest
ที่ทำงานใน ~ 83 วินาทีเมื่อรวบรวมกับ -O3 ใช้forall
'd เชี่ยวชาญ
แสดงหลักว่าสำหรับvtTest
การทดสอบรหัสนอกจากจะถูกผู้เชี่ยวชาญเพื่อUnboxed
เวกเตอร์บนInt
s ฯลฯ fcTest
ในขณะที่รหัสเวกเตอร์ทั่วไปที่ใช้สำหรับ ในบรรทัดที่ 10 คุณจะเห็นว่า GHC เขียนรุ่นพิเศษของplusFastCyc
เมื่อเทียบกับรุ่นทั่วไปในบรรทัดที่ 167 กฎสำหรับความเชี่ยวชาญนั้นอยู่ที่บรรทัด 225 ฉันเชื่อว่ากฎนี้ควรเริ่มทำงานในสาย 270 (การmain6
โทรiterate main8 y
ดังนั้นmain8
ก็คือ ที่plusFastCyc
ควรได้รับความเชี่ยวชาญ.)
เป้าหมายของผมคือการทำให้fcTest
เร็วที่สุดเท่าโดยมีความเชี่ยวชาญvtTest
plusFastCyc
ฉันพบสองวิธีในการทำสิ่งนี้:
- โทรอย่างชัดเจน
inline
จากในGHC.Exts
fcTest
- ลบ
Factored m Int
ข้อ จำกัดplusFastCyc
ใน
ตัวเลือกที่ 1 ไม่เป็นที่น่าพอใจเนื่องจากในส่วนของรหัสที่แท้จริงplusFastCyc
นั้นเป็นการดำเนินการที่ใช้บ่อยและฟังก์ชั่นที่มีขนาดใหญ่มากดังนั้นจึงไม่ควรมีการ inline ทุกครั้งที่ใช้งาน แต่ GHC plusFastCyc
ควรจะเรียกรุ่นเฉพาะของ ตัวเลือกที่ 2 ไม่ใช่ตัวเลือกจริงๆเพราะฉันต้องการข้อ จำกัด ในรหัสจริง
ฉันได้พยายามหลากหลายของตัวเลือกใช้ (และไม่ได้ใช้) INLINE
, INLINABLE
และSPECIALIZE
แต่ไม่มีอะไรดูเหมือนว่าจะทำงาน ( แก้ไข : ฉันอาจจะลอกออกมากเกินไปที่plusFastCyc
จะทำให้ตัวอย่างของฉันเล็กดังนั้นINLINE
อาจทำให้ฟังก์ชั่นที่จะ inline นี้ไม่ได้เกิดขึ้นในรหัสที่แท้จริงของฉันเพราะplusFastCyc
มีขนาดใหญ่มาก) ในตัวอย่างนี้ฉันไม่รับคำเตือนใด ๆmatch_co: needs more cases
หรือRULE: LHS too complicated to desugar
(และที่นี่ ) แม้ว่าฉันจะได้match_co
รับคำเตือนมากมายก่อนที่จะย่อขนาดตัวอย่าง สันนิษฐานว่า "ปัญหา" เป็นFactored m Int
ข้อ จำกัด ในกฎ; ถ้าฉันเป็นผู้เปลี่ยนแปลงข้อ จำกัด ที่วิ่งเร็วที่สุดเท่าที่fcTest
vtTest
ฉันกำลังทำสิ่งที่ GHC ไม่ชอบหรือไม่ เหตุใด GHC จึงไม่เชี่ยวชาญplusFastCyc
และฉันจะสร้างมันได้อย่างไร
UPDATE
ปัญหายังคงอยู่ใน GHC 7.8.2 ดังนั้นคำถามนี้ยังเกี่ยวข้อง
m
M
นี่เป็นงานที่ทำเสร็จแล้ว แต่ฉันไม่สามารถชำนาญสำหรับประเภท phantom ที่เฉพาะเจาะจงในโปรแกรมจริงตามที่ได้รับการแก้ไข