Tôi đang gặp vấn đề khi GHC chuyên môn hóa một chức năng với ràng buộc lớp. Tôi có một ví dụ rất nhỏ của vấn đề của tôi ở đây: Foo.hs và Main.hs . Hai tệp biên dịch (GHC 7.6.2, ghc -O3 Main) và chạy.
LƯU Ý:
Foo.hs thực sự bị tước bỏ. Nếu bạn muốn xem tại sao cần phải có ràng buộc, bạn có thể xem thêm một chút mã ở đây . Nếu tôi đặt mã trong một tệp duy nhất hoặc thực hiện nhiều thay đổi nhỏ khác, GHC chỉ cần thực hiện cuộc gọi đến plusFastCyc. Điều này sẽ không xảy ra trong mã thực bởi vì plusFastCycGHC quá lớn để nội tuyến, ngay cả khi được đánh dấu INLINE. Vấn đề là chuyên môn hóa cuộc gọi đến plusFastCyc, không phải nội tuyến. plusFastCycđược gọi ở nhiều nơi trong mã thực, do đó, việc sao chép một hàm lớn như vậy sẽ không được mong muốn ngay cả khi tôi có thể buộc GHC thực hiện.
Mã quan tâm là plusFastCyctrong Foo.hs, được sao chép ở đây:
{-# 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
Các Main.hstập tin có hai tài xế: vtTest, chạy trong ~ 3 giây, và fcTest, chạy trong ~ 83 giây khi biên soạn với O3 bằng cách sử dụng forall'd chuyên môn hóa.
Các chương trình cốt lõi rằng đối với vtTestkiểm tra, mã bổ sung đang được chuyên để Unboxedvectơ qua Ints, vv, trong khi mã vector chung được sử dụng cho fcTest. Trên dòng 10, bạn có thể thấy GHC viết một phiên bản chuyên biệt plusFastCyc, so với phiên bản chung trên dòng 167. Quy tắc dành cho chuyên môn hóa là trên dòng 225. Tôi tin rằng quy tắc này sẽ kích hoạt trên dòng 270. ( main6các cuộc gọi iterate main8 y, vì vậy main8là nơi nào plusFastCycnên chuyên ngành.)
Mục tiêu của tôi là làm fcTestnhanh như vtTestchuyên plusFastCyc. Tôi đã tìm thấy hai cách để làm điều này:
- Cuộc gọi giải thích
inlinetừGHC.ExtstrongfcTest. - Loại bỏ các
Factored m Intràng buộc trênplusFastCyc.
Tùy chọn 1 là không thỏa đáng vì trong cơ sở mã thực tế plusFastCyclà một hoạt động được sử dụng thường xuyên và một chức năng rất lớn, do đó không nên nội tuyến trong mỗi lần sử dụng. Thay vào đó, GHC nên gọi một phiên bản chuyên biệt của plusFastCyc. Tùy chọn 2 không thực sự là một tùy chọn vì tôi cần sự ràng buộc trong mã thực.
Tôi đã thử một loạt các lựa chọn sử dụng (và không sử dụng) INLINE, INLINABLEvà SPECIALIZE, nhưng không có vẻ làm việc. ( EDIT : Tôi có thể đã loại bỏ quá nhiều plusFastCycđể làm cho ví dụ của mình trở nên nhỏ, do đó INLINEcó thể khiến hàm bị nội tuyến. Điều này không xảy ra trong mã thực của tôi vì plusFastCycquá lớn.) Trong ví dụ cụ thể này, tôi không nhận được bất kỳ match_co: needs more caseshoặc RULE: LHS too complicated to desugar(và ở đây ) cảnh báo, mặc dù tôi đã nhận được nhiều match_cocảnh báo trước khi giảm thiểu ví dụ. Có lẽ, "vấn đề" là sự Factored m Intràng buộc trong quy tắc; nếu tôi thay đổi ràng buộc đó, hãy fcTestchạy nhanh như vtTest.
Tôi có đang làm điều gì đó mà GHC không thích? Tại sao GHC không chuyên plusFastCyc, và làm cách nào để tạo ra nó?
CẬP NHẬT
Vấn đề vẫn tồn tại trong GHC 7.8.2, vì vậy câu hỏi này vẫn có liên quan.
m, cụ thể làM. Điều này đã hoàn thành công việc, nhưng tôi không thể chuyên cho các loại bóng cụ thể trong chương trình thực khi chúng được thống nhất.