Mam problemy ze skonfigurowaniem GHC do specjalizacji funkcji z ograniczeniem klasy. Mam minimalny przykład mój problem tutaj: Foo.hs i Main.hs . Te dwa pliki są kompilowane (GHC 7.6.2 ghc -O3 Main
) i uruchamiane.
UWAGA:
Foo.hs
jest naprawdę okrojona. Jeśli chcesz zobaczyć, dlatego też konieczne jest ograniczenie, można zobaczyć trochę więcej kodu tutaj . Jeśli umieszczę kod w jednym pliku lub wprowadzę wiele innych drobnych zmian, GHC po prostu wstawia wywołanie do plusFastCyc
. To się nie stanie w prawdziwym kodzie, ponieważ plusFastCyc
jest zbyt duży, aby GHC mógł go wbudować, nawet gdy jest oznaczony INLINE
. Chodzi o to, aby specjalizować wywołanie plusFastCyc
, a nie je wbudować. plusFastCyc
jest wywoływana w wielu miejscach w rzeczywistym kodzie, więc powielanie tak dużej funkcji nie byłoby pożądane, nawet gdybym mógł do tego zmusić GHC.
Interesujący jest kod plusFastCyc
in Foo.hs
, odtworzony tutaj:
{-# 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
Plik ma dwóch kierowców: vtTest
, czyli w ~ 3 sekundy, a fcTest
, która biegnie w ~ 83 sekund, gdy skompilowany z użyciem -O3 forall
„d specjalizacji.
Do podstawowych pokazuje , że w vtTest
teście kod dodatek jest specjalistyczne, Unboxed
wektory ponad Int
s, itp, a rodzajowy kod wektor stosuje fcTest
. W linii 10. widać, że GHC pisze wyspecjalizowaną wersję programu plusFastCyc
, w porównaniu z wersją ogólną w linii 167. Reguła dla specjalizacji jest w linii 225. Uważam, że ta reguła powinna zostać uruchomiona w linii 270. ( main6
wywołania iterate main8 y
, więc main8
jest gdzie plusFastCyc
powinien być wyspecjalizowany.)
Moim celem jest robić fcTest
tak szybko, jak vtTest
specjalizując się plusFastCyc
. Znalazłem na to dwa sposoby:
- Wezwanie explicity
inline
odGHC.Exts
wfcTest
. - Usuń
Factored m Int
ograniczenie naplusFastCyc
.
Opcja 1 jest niezadowalająca, ponieważ w rzeczywistej bazie kodu plusFastCyc
jest często używaną operacją i bardzo dużą funkcją, więc nie powinna być wprowadzana przy każdym użyciu. Zamiast tego GHC powinien wywołać wyspecjalizowaną wersję plusFastCyc
. Opcja 2 nie jest tak naprawdę opcją, ponieważ potrzebuję ograniczenia w prawdziwym kodzie.
Próbowałem różnych opcji przy użyciu (a nie przy użyciu) INLINE
, INLINABLE
oraz SPECIALIZE
, ale nic nie wydaje się do pracy. ( EDYCJA : Być może usunąłem zbyt wiele, plusFastCyc
aby mój przykład był mały, więc INLINE
może to spowodować wstawienie funkcji. Nie dzieje się tak w moim prawdziwym kodzie, ponieważ plusFastCyc
jest tak duży). W tym konkretnym przykładzie nie jestem otrzymuję ostrzeżenia match_co: needs more cases
lub RULE: LHS too complicated to desugar
(i tutaj ), chociaż otrzymywałem wiele match_co
ostrzeżeń przed zminimalizowaniem przykładu. Przypuszczalnie „problemem” jest Factored m Int
ograniczenie reguły; jeśli wprowadzę zmiany w tym ograniczeniu, fcTest
działa tak szybko, jak vtTest
.
Czy robię coś, czego GHC po prostu nie lubi? Dlaczego GHC nie specjalizuje się w tym plusFastCyc
i jak mogę to zrobić?
AKTUALIZACJA
Problem występuje nadal w GHC 7.8.2, więc to pytanie jest nadal aktualne.
m
, a mianowicieM
. Wykonało to zadanie, ale nie mogę specjalizować się w konkretnych typach fantomów w prawdziwym programie, ponieważ są one reifikowane.Odpowiedzi:
GHC udostępnia również opcję
SPECIALIZE
deklaracji wystąpienia klasy typu. Wypróbowałem to z (rozszerzonym) kodemFoo.hs
, wstawiając:Ta zmiana nie przyniosła jednak pożądanego przyspieszenia. To, co pozwoliło osiągnąć tę poprawę wydajności, to ręczne dodanie wyspecjalizowanej instancji dla typu
VT U.Vector m Int
z tymi samymi definicjami funkcji w następujący sposób:Wymaga to dodawanie
OverlappingInstances
iFlexibleInstances
wLANGUAGE
.Co ciekawe, w przykładowym programie przyspieszenie uzyskane z nakładającą się instancją pozostaje, nawet jeśli usuniesz wszystkie
SPECIALIZE
iINLINABLE
pragma.źródło