У мене виникають проблеми з отриманням GHC для спеціалізації функції з обмеженням класу. У мене є мінімальний приклад моєї проблеми тут: Foo.hs і Main.hs . Два файли компілюються (GHC 7.6.2, ghc -O3 Main
) і запускаються.
ПРИМІТКА:
Foo.hs
дійсно позбавлений. Якщо ви хочете зрозуміти, для чого потрібне обмеження, ви можете побачити трохи більше коду тут . Якщо я поміщую код в один файл або вношу багато інших незначних змін, GHC просто вказує дзвінок на 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
с, і т.д., в той час як загальний вектором код використовується для 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
є часто використовувана операція і дуже велика функція, тому її не слід вказувати при кожному використанні. Швидше за все, GHC має викликати спеціалізовану версію plusFastCyc
. Варіант 2 насправді не є варіантом, тому що мені потрібно обмеження в реальному коді.
Я пробував різні варіанти використання (і не використовувати) INLINE
, INLINABLE
і SPECIALIZE
, але нічого не схоже на роботу. ( EDIT : я, можливо, позбавив себе занадто багато, plusFastCyc
щоб зробити мій приклад малим, тому це INLINE
може призвести до того, що функція буде вписана. Це не відбувається в моєму реальному коді, оскільки plusFastCyc
такий великий.) У цьому конкретному прикладі я не отримувати будь-які match_co: needs more cases
або RULE: LHS too complicated to desugar
(і тут ) попередження, хоча я отримував багато match_co
попереджень, перш ніж мінімізувати приклад. Імовірно, "проблема" є Factored m Int
обмеженням у праві; якщо я вношу зміни до цього обмеження, fcTest
працює так само швидко vtTest
.
Мені щось робити GHC просто не подобається? Чому GHC не спеціалізується plusFastCyc
, і як це зробити?
ОНОВЛЕННЯ
Проблема зберігається в GHC 7.8.2, тому це питання все ще є актуальним.
m
, а самеM
. З цим було виконано завдання, але я не можу спеціалізуватися на конкретних фантомних типах у реальній програмі, оскільки вони переробляються.