У меня проблемы с получением 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
, но ничего не похоже на работу. ( РЕДАКТИРОВАТЬ : я, возможно, лишился слишком много, 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
. Это сделало свою работу, но я не могу специализироваться на конкретных фантомных типах в реальной программе, поскольку они ограничены.Ответы:
GHC также дает возможность
SPECIALIZE
объявления экземпляра класса типа. Я попробовал это с (расширенным) кодомFoo.hs
, поставив следующее:Это изменение, однако, не достигло желаемого ускорения. Достигнутое улучшение производительности было вручную добавлено специализированный экземпляр для типа
VT U.Vector m Int
с теми же определениями функций, как показано ниже:Это требует добавления
OverlappingInstances
иFlexibleInstances
вLANGUAGE
.Интересно, что в примере программы ускорение, полученное с перекрывающимся экземпляром, сохраняется, даже если вы удалите Every
SPECIALIZE
иINLINABLE
Pragma.источник