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ì plusFastCyc
GHC 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à plusFastCyc
trong 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.hs
tậ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 vtTest
kiểm tra, mã bổ sung đang được chuyên để Unboxed
vectơ qua Int
s, 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. ( main6
các cuộc gọi iterate main8 y
, vì vậy main8
là nơi nào plusFastCyc
nên chuyên ngành.)
Mục tiêu của tôi là làm fcTest
nhanh như vtTest
chuyê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
inline
từGHC.Exts
trongfcTest
. - Loại bỏ các
Factored m Int
ràng buộc trênplusFastCyc
.
Tùy chọn 1 là không thỏa đáng vì trong cơ sở mã thực tế plusFastCyc
là 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
, INLINABLE
và 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 đó INLINE
có 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ì plusFastCyc
quá lớn.) Trong ví dụ cụ thể này, tôi không nhận được bất kỳ match_co: needs more cases
hoặc RULE: LHS too complicated to desugar
(và ở đây ) cảnh báo, mặc dù tôi đã nhận được nhiều match_co
cảnh báo trước khi giảm thiểu ví dụ. Có lẽ, "vấn đề" là sự Factored m Int
ràng buộc trong quy tắc; nếu tôi thay đổi ràng buộc đó, hãy fcTest
chạ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.