У меня проблемы с GHC, чтобы специализировать функцию с ограничением класса. У меня есть минимальный пример моей проблемы: Foo.hs и Main.hs. Эти два файла компилируются (GHC 7.6.2, ghc -O3 Main
) и запускаются.
Примечание:
Foo.hs
действительно удаляется. Если вы хотите узнать, почему требуется ограничение, вы можете увидеть здесь немного больше кода здесь. Если я помещу код в один файл или сделаю много других незначительных изменений, GHC просто вставляет вызов plusFastCyc
. Это не произойдет в реальном коде, потому что plusFastCyc
слишком велико для GHC для встроенного, даже если отмечено INLINE
. Дело в том, чтобы специализировать вызов plusFastCyc
, а не встраивать его. plusFastCyc
вызывается во многих местах реального кода, поэтому дублирование такой большой функции было бы нежелательным, даже если бы я мог заставить GHC это сделать.
Код интереса - это plusFastCyc
in 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
s и т.д., в то время как для 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
, но ничего не работает. (РЕДАКТИРОВАТЬ: я мог бы удалить слишком много из plusFastCyc
, чтобы сделать мой пример маленьким, поэтому INLINE
может привести к тому, что функция будет встроена. Это не происходит в моем реальном коде, потому что plusFastCyc
настолько велико.) В этом конкретном примере я не получаю никаких match_co: needs more cases
или RULE: LHS too complicated to desugar
(и здесь) предупреждения, хотя я получал много предупреждений match_co
, прежде чем сводить к минимуму пример. Предположительно, "проблема" - это ограничение Factored m Int
в правиле; если я вношу изменения в это ограничение, fcTest
работает так же быстро, как vtTest
.
Я что-то делаю, что GHC просто не нравится? Почему GHC не специализируется на plusFastCyc
, и как я могу это сделать?
UPDATE
Проблема сохраняется в GHC 7.8.2, поэтому этот вопрос по-прежнему имеет значение.