ความเชี่ยวชาญด้วยข้อ จำกัด


156

ฉันมีปัญหาในการทำให้ 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เวกเตอร์บนInts ฯลฯ fcTestในขณะที่รหัสเวกเตอร์ทั่วไปที่ใช้สำหรับ ในบรรทัดที่ 10 คุณจะเห็นว่า GHC เขียนรุ่นพิเศษของplusFastCycเมื่อเทียบกับรุ่นทั่วไปในบรรทัดที่ 167 กฎสำหรับความเชี่ยวชาญนั้นอยู่ที่บรรทัด 225 ฉันเชื่อว่ากฎนี้ควรเริ่มทำงานในสาย 270 (การmain6โทรiterate main8 yดังนั้นmain8ก็คือ ที่plusFastCycควรได้รับความเชี่ยวชาญ.)

เป้าหมายของผมคือการทำให้fcTestเร็วที่สุดเท่าโดยมีความเชี่ยวชาญvtTest plusFastCycฉันพบสองวิธีในการทำสิ่งนี้:

  1. โทรอย่างชัดเจนinlineจากในGHC.ExtsfcTest
  2. ลบ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ข้อ จำกัด ในกฎ; ถ้าฉันเป็นผู้เปลี่ยนแปลงข้อ จำกัด ที่วิ่งเร็วที่สุดเท่าที่fcTestvtTest

ฉันกำลังทำสิ่งที่ GHC ไม่ชอบหรือไม่ เหตุใด GHC จึงไม่เชี่ยวชาญplusFastCycและฉันจะสร้างมันได้อย่างไร

UPDATE

ปัญหายังคงอยู่ใน GHC 7.8.2 ดังนั้นคำถามนี้ยังเกี่ยวข้อง


3
ฉันพยายามที่เชี่ยวชาญเป็นที่เฉพาะเจาะจง คือm Mนี่เป็นงานที่ทำเสร็จแล้ว แต่ฉันไม่สามารถชำนาญสำหรับประเภท phantom ที่เฉพาะเจาะจงในโปรแกรมจริงตามที่ได้รับการแก้ไข
crockeea

ฉันยังส่งรายงานข้อผิดพลาด GHC ghc.haskell.org/trac/ghc/ticket/8668แต่ปัญหายังคงเปิดอยู่ กระบวนการรายงานข้อผิดพลาดช่วยให้ฉันทำความสะอาดคำถามได้เล็กน้อยดังนั้นหวังว่ามันจะง่ายขึ้นหากคิดว่าเกิดอะไรขึ้น
crockeea

@monojohnny เสียใจที่ได้ยินฉันเชื่อว่าคุณสามารถตั้งค่าสถานะนี้ได้ ฉันคิดว่าฉันขอให้ GHC ทำสิ่งที่สมเหตุสมผลพอสมควรและจะไม่ทำเช่นนั้น ฉันไม่แน่ใจว่าฉันทำผิดหรือว่านี่เป็นเรื่องแปลกสำหรับคอมไพเลอร์ที่อาจมีวิธีแก้ปัญหา ฉันเคยเห็นวิธีแก้ปัญหาสำหรับความเชี่ยวชาญและกฎระเบียบในห้องสมุดเฉพาะเกี่ยวกับการแฮ็กที่หนีฉันในขณะนี้ดังนั้นฉันหวังว่าใครบางคนในชุมชนที่มีประสบการณ์ GHC มากกว่าที่ฉันอาจรู้วิธีการบรรลุความเชี่ยวชาญ
crockeea

1
ฉันขอโทษสำหรับน้ำเสียงของความคิดเห็นของฉัน - มันไม่ใช่การมีส่วนร่วมที่ดีที่สุดของฉันในเว็บไซต์นี้ - ไม่มีอะไรผิดปกติกับโพสต์ของคุณ (มันขาดความเข้าใจของฉันที่เป็นแหล่งที่มาของความรำคาญฉันเดา!)
monojohnny

@monojohnny ขอโทษยอมรับ แต่มันแย่เกินไปที่ downvote ถูกล็อคในตอนนี้ ;-)
crockeea

คำตอบ:


5

GHC ยังมีตัวเลือกในSPECIALIZEการประกาศอินสแตนซ์ระดับคลาส ฉันลองสิ่งนี้ด้วยโค้ด (ขยาย) ของFoo.hsโดยใส่สิ่งต่อไปนี้:

instance (Num r, V.Vector v r, Factored m r) => Num (VT v m r) where 
    {-# SPECIALIZE instance ( Factored m Int => Num (VT U.Vector m Int)) #-}
    VT x + VT y = VT $ V.zipWith (+) x y

แม้ว่าการเปลี่ยนแปลงนี้จะไม่สามารถเร่งความเร็วที่ต้องการได้ สิ่งที่บรรลุผลสำเร็จได้ว่าการปรับปรุงประสิทธิภาพนั้นเพิ่มอินสแตนซ์พิเศษสำหรับประเภทด้วยคำจำกัดความของฟังก์ชั่นเดียวกันด้วยตนเองVT U.Vector m Intดังนี้:

instance (Factored m Int) => Num (VT U.Vector m Int) where 
    VT x + VT y = VT $ V.zipWith (+) x y

เรื่องนี้ต้องมีการเพิ่มOverlappingInstancesและในFlexibleInstances LANGUAGE

ที่น่าสนใจในโปรแกรมตัวอย่างการเพิ่มความเร็วที่ได้จากอินสแตนซ์ที่ทับซ้อนกันยังคงอยู่แม้ว่าคุณจะลบทุก ๆSPECIALIZEและINLINABLEpragma


ไม่ดีที่สุดอย่างแน่นอน แต่มันเป็นทางออกแรกที่บรรลุเป้าหมายจริง ๆ ดังนั้นฉันเดาว่าฉันจะเอามันไปใช้ในตอนนี้ ...
crockeea
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.