ความไวของความเชี่ยวชาญอัตโนมัติใน GHC


392

จากเอกสารสำหรับ GHC 7.6:

[Y] คุณไม่ต้องการแม้แต่ความเชี่ยวชาญเป็นพิเศษในตอนแรก เมื่อทำการรวบรวมโมดูล M เครื่องมือเพิ่มประสิทธิภาพของ GHC (พร้อม -O) จะพิจารณาฟังก์ชั่นโอเวอร์โหลดระดับสูงสุดแต่ละรายการที่ประกาศใน M โดยอัตโนมัติและเชี่ยวชาญสำหรับประเภทต่าง ๆ ที่เรียกว่าเป็น M เครื่องมือเพิ่มประสิทธิภาพจะพิจารณาฟังก์ชั่นโอเวอร์โหลด และเชี่ยวชาญสำหรับประเภทต่าง ๆ ที่มันถูกเรียกว่าใน M

และ

ยิ่งไปกว่านั้นเมื่อได้รับ SPECIALIZE pragma สำหรับฟังก์ชั่น f GHC จะสร้างความเชี่ยวชาญเฉพาะสำหรับฟังก์ชั่นประเภทเกินพิกัดใด ๆ ที่เรียกโดย f หากอยู่ในโมดูลเดียวกันกับ SPECAGIZE pragma หรือหากไม่มีการเชื่อมโยง; และอื่น ๆ ต่อเนื่อง

ดังนั้น GHC ควรใช้ฟังก์ชันบางส่วน / มากที่สุด / ทั้งหมด (?) ที่ทำเครื่องหมายINLINABLE โดยไม่มี pragma โดยอัตโนมัติและถ้าฉันใช้ pragma อย่างชัดเจน คำถามของฉันคือ: การเปลี่ยนคำจำกัดความอัตโนมัติโดยอัตโนมัติหรือไม่

นี่เป็นตัวอย่างเล็ก ๆ :

Main.hs:

import Data.Vector.Unboxed as U
import Foo

main =
    let y = Bar $ Qux $ U.replicate 11221184 0 :: Foo (Qux Int)
        (Bar (Qux ans)) = iterate (plus y) y !! 100
    in putStr $ show $ foldl1' (*) ans

Foo.hs:

module Foo (Qux(..), Foo(..), plus) where

import Data.Vector.Unboxed as U

newtype Qux r = Qux (Vector r)
-- GHC inlines `plus` if I remove the bangs or the Baz constructor
data Foo t = Bar !t
           | Baz !t

instance (Num r, Unbox r) => Num (Qux r) where
    {-# INLINABLE (+) #-}
    (Qux x) + (Qux y) = Qux $ U.zipWith (+) x y

{-# INLINABLE plus #-}
plus :: (Num t) => (Foo t) -> (Foo t) -> (Foo t)
plus (Bar v1) (Bar v2) = Bar $ v1 + v2

GHC เชี่ยวชาญการโทรplusแต่ไม่ชำนาญ(+)ในQux Numอินสแตนซ์ที่ฆ่าประสิทธิภาพ

อย่างไรก็ตาม pragma ชัดเจน

{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}

ส่งผลให้มีความเชี่ยวชาญด้านสกรรมกริยาตามที่เอกสารระบุดังนั้นจึง(+)มีความเชี่ยวชาญและรหัสคือ 30x เร็วขึ้น (ทั้งคู่รวบรวมด้วย-O2) เป็นพฤติกรรมที่คาดหวังหรือไม่ ฉันควรคาดหวังว่า(+)จะมีความเชี่ยวชาญด้านการขนส่งโดยเฉพาะด้วยการปฏิบัติที่ชัดเจน?


UPDATE

เอกสารสำหรับ 7.8.2 ยังไม่ได้เปลี่ยนแปลงและพฤติกรรมเหมือนกันดังนั้นคำถามนี้ยังคงเกี่ยวข้อง


33
ฉันไม่ทราบคำตอบ แต่ดูเหมือนว่ามันอาจเกี่ยวข้องกับ: ghc.haskell.org/trac/ghc/ticket/5928น่าจะเป็นมูลค่าการเปิดตั๋วใหม่หรือเพิ่มข้อมูลของคุณถ้าคุณคิดว่าน่าจะเกี่ยวข้องกับ 5928
jberryman

6
@jberryman ดูเหมือนจะมีความแตกต่างระหว่างสองตั๋วที่และคำถามของฉัน: 1) ในตั๋วเทียบเท่าของplusถูกไม่ได้ทำเครื่องหมายเป็น INLINABLE และ 2) simonpj ชี้ให้เห็นว่ามีบาง inlining ที่เกิดขึ้นกับรหัสตั๋ว แต่หลักจาก ตัวอย่างของฉันแสดงให้เห็นว่าไม่มีฟังก์ชั่นใดที่ inline (โดยเฉพาะฉันไม่สามารถกำจัดFooนวกรรมิกตัวที่สองได้มิฉะนั้น GHC inline stuff)
crockeea

5
อ่าโอเค. จะเกิดอะไรขึ้นเมื่อคุณกำหนดplus (Bar v1) = \(Bar v2)-> Bar $ v1 + v2เพื่อให้ LHS ถูกนำไปใช้อย่างเต็มที่ในไซต์การโทร มันได้รับการ inline และจากนั้นจะเชี่ยวชาญในการเตะ?
jberryman

3
@ jberryman ตลกที่คุณควรถาม ฉันลงไปที่ถนนด้วยคำถามนี้ซึ่งนำไปสู่รายงาน tracนี้ เดิมฉันมีการโทรเพื่อplusนำไปใช้อย่างเต็มที่โดยเฉพาะเนื่องจากการเชื่อมโยงเหล่านั้น แต่ในความเป็นจริงฉันมีความเชี่ยวชาญน้อยลง : การโทรไปplusยังไม่เฉพาะเจาะจง ฉันไม่มีคำอธิบายสำหรับเรื่องนี้ แต่ตั้งใจที่จะทิ้งไว้เพื่อคำถามอื่นหรือหวังว่ามันจะได้รับการแก้ไขในคำตอบของคำถามนี้
crockeea

11
จากghc.haskell.org/trac/ghc/wiki/ReportABug : "หากมีข้อสงสัยให้รายงานบั๊กของคุณ" คุณไม่ควรรู้สึกแย่โดยเฉพาะอย่างยิ่งเนื่องจากมีผู้มีประสบการณ์ที่เพียงพอจำนวนมากที่นี่ยังไม่รู้วิธีตอบคำถามของคุณ กรณีทดสอบเช่นนี้น่าจะมีค่ามากสำหรับ GHC devs ขอให้โชคดี! อัปเดตคำถามถ้าคุณยื่นตั๋ว
jberryman

คำตอบ:


4

คำตอบสั้น ๆ:

ประเด็นสำคัญของคำถามดังที่ฉันเข้าใจพวกเขามีดังต่อไปนี้:

  • "การถ่ายทอดความเชี่ยวชาญอัตโนมัติเป็นอย่างไร"
  • ฉันควรคาดหวังว่า (+) จะเป็นผู้เชี่ยวชาญด้านการขนส่งโดยเฉพาะอย่างยิ่งกับการปฏิบัติที่ชัดเจน
  • (ตั้งใจชัดเจน) นี่เป็นข้อบกพร่องของ GHC หรือไม่? มันไม่สอดคล้องกับเอกสารหรือไม่

AFAIK คำตอบคือไม่ส่วนใหญ่ใช่ แต่มีวิธีอื่นและไม่

การอินไลน์โค้ดและความเชี่ยวชาญแอปพลิเคชันประเภทคือการแลกเปลี่ยนระหว่างความเร็ว (เวลาดำเนินการ) และขนาดรหัส ระดับเริ่มต้นจะได้รับการเร่งความเร็วโดยไม่ต้อง bloating รหัส การเลือกระดับที่ครบถ้วนสมบูรณ์ยิ่งขึ้นนั้นขึ้นอยู่กับดุลยพินิจของโปรแกรมเมอร์ผ่านทาง SPECIALISEpragma

คำอธิบาย:

เครื่องมือเพิ่มประสิทธิภาพยังพิจารณาฟังก์ชั่นโอเวอร์โหลดที่นำเข้าแต่ละ INLINABLE และเชี่ยวชาญสำหรับประเภทต่าง ๆ ที่เรียกว่าเป็น M

สมมติว่าfเป็นฟังก์ชั่นที่มีประเภทรวมถึงตัวแปรประเภทaจำกัด C aโดยระดับประเภท GHC โดยค่าเริ่มต้นมีความเชี่ยวชาญfเกี่ยวกับการเป็นโปรแกรมประเภท (แทนaสำหรับt) ถ้าfเรียกว่ามีแอพลิเคชันประเภทนั้นในรหัสแหล่งที่มาของ (ก) ฟังก์ชั่นใด ๆ ในโมดูลเดียวกันหรือ (ข) ถ้าfมีการทำเครื่องหมายINLINABLEแล้วโมดูลอื่น ๆ ที่ นำเข้า จากf Bดังนั้นอัตโนมัติเชี่ยวชาญไม่ได้เป็นสกรรมกริยาก็เพียงสัมผัสINLINABLEฟังก์ชั่นที่นำเข้ามาและเรียกร้องให้ในรหัสที่มาAของ

ในตัวอย่างของคุณหากคุณเขียนอินสแตนซ์ใหม่Numดังนี้:

instance (Num r, Unbox r) => Num (Qux r) where
    (+) = quxAdd

quxAdd (Qux x) (Qux y) = Qux $ U.zipWith (+) x y
  • quxAddMainจะไม่นำเข้าโดยเฉพาะ Mainนำเข้าพจนานุกรมอินสแตนซ์Num (Qux Int)และพจนานุกรมนี้มีในบันทึกquxAdd (+)อย่างไรก็ตามแม้ว่าจะมีการนำเข้าพจนานุกรมเนื้อหาที่ใช้ในพจนานุกรมจะไม่
  • plusไม่เรียกquxAddจะใช้ฟังก์ชั่นที่เก็บไว้สำหรับบันทึกในพจนานุกรมตัวอย่างของ(+) Num tพจนานุกรมนี้ถูกตั้งค่าที่ไซต์การโทร (เป็นMain) โดยคอมไพเลอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.