Специализация с ограничениями

156

У меня проблемы с получением 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. Я нашел два способа сделать это:

  1. Звоните по простоте inlineиз GHC.Extsв fcTest.
  2. Удалить 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, поэтому этот вопрос по-прежнему актуален.

crockeea
источник
3
Я просто пытался специализироваться на конкретном m , а именно M. Это сделало свою работу, но я не могу специализироваться на конкретных фантомных типах в реальной программе, поскольку они ограничены.
crockeea
Я также представил отчет об ошибке GHC ghc.haskell.org/trac/ghc/ticket/8668, но проблема все еще остается открытой. Процесс сообщения об ошибках помог мне немного прояснить вопрос, так что, надеюсь, будет легче выяснить, что происходит.
Crockeea
@monojohnny Очень жаль это слышать, я думаю, вы можете пометить это как таковое. Я думаю, что я прошу GHC сделать что-то довольно разумное, и это не будет сделано. Я не уверен, что делаю это неправильно, или это особенность компилятора, который может иметь обходной путь. Я видел обходные пути для специализации и правил в какой-то конкретной библиотеке по взлому, которая ускользает от меня на данный момент, поэтому я надеюсь, что кто-то в сообществе с большим опытом работы с GHC, чем я, может знать, как получить специализацию.
crockeea
1
Я прошу прощения за тон моего комментария - это не мой лучший вклад в этот сайт - в вашем посте действительно нет ничего плохого (я полагаю, это мое непонимание было источником моего раздражения!)
monojohnny
@monojohnny Извинение принято, но очень жаль, что downvote теперь заблокирован ;-)
crockeea

Ответы:

5

GHC также дает возможность SPECIALIZEобъявления экземпляра класса типа. Я попробовал это с (расширенным) кодом Foo.hs, поставив следующее:

instance (Num r, V.Vector v r, Factored m r) => Num (VT v m r) where 
    {-# SPECIALIZE instance ( Factored m Int => Num (VT U.Vector m Int)) #-}
    VT x + VT y = VT $ V.zipWith (+) x y

Это изменение, однако, не достигло желаемого ускорения. Достигнутое улучшение производительности было вручную добавлено специализированный экземпляр для типа VT U.Vector m Intс теми же определениями функций, как показано ниже:

instance (Factored m Int) => Num (VT U.Vector m Int) where 
    VT x + VT y = VT $ V.zipWith (+) x y

Это требует добавления OverlappingInstancesи FlexibleInstancesв LANGUAGE.

Интересно, что в примере программы ускорение, полученное с перекрывающимся экземпляром, сохраняется, даже если вы удалите Every SPECIALIZEи INLINABLEPragma.

Диего Э. Алонсо-Блас
источник
Определенно не оптимально, но это первое решение, которое на самом деле достигает цели, так что, я думаю, я возьму это сейчас ...
crockeea