Estou tendo problemas para que o GHC especialize uma função com uma restrição de classe. Eu tenho um exemplo mínimo do meu problema aqui: Foo.hs e Main.hs . Os dois arquivos são compilados (GHC 7.6.2 ghc -O3 Main
) e executados.
NOTA:
Foo.hs
está realmente despojado. Se você quiser ver por que a restrição é necessária, poderá ver um pouco mais de código aqui . Se eu colocar o código em um único arquivo ou fazer muitas outras pequenas alterações, o GHC simplesmente encaminha a chamada para plusFastCyc
. Isso não acontecerá no código real, porque plusFastCyc
é muito grande para o GHC se alinhar, mesmo quando marcado INLINE
. O objetivo é especializar a chamada plusFastCyc
, e não incorporá-la. plusFastCyc
é chamado em muitos lugares no código real, portanto, duplicar uma função tão grande não seria desejável, mesmo se eu pudesse forçar o GHC a fazê-lo.
O código de interesse é o plusFastCyc
in Foo.hs
, reproduzido aqui:
{-# 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
O Main.hs
arquivo possui dois drivers vtTest
:, que é executado em ~ 3 segundos e fcTest
, que é executado em ~ 83 segundos quando compilado com -O3 usando a forall
especialização 'd.
O núcleo mostra que, para o vtTest
teste, o código de adição está sendo especializado em Unboxed
vetores sobre Int
s, etc., enquanto o código vetorial genérico é usado fcTest
. Na linha 10, você pode ver que o GHC escreve uma versão especializada plusFastCyc
, em comparação com a versão genérica na linha 167. A regra para a especialização está na linha 225. Acredito que essa regra deve ser acionada na linha 270. ( main6
chama iterate main8 y
, assim main8
é onde plusFastCyc
deve ser especializado.)
Meu objetivo é fazer o fcTest
mais rápido possível, vtTest
especializando-me plusFastCyc
. Eu encontrei duas maneiras de fazer isso:
- Chamada de explícita
inline
deGHC.Exts
dentrofcTest
. - Remova a
Factored m Int
restrição ativadaplusFastCyc
.
A opção 1 não é satisfatória porque, na base de código real, plusFastCyc
é uma operação frequentemente usada e uma função muito grande, portanto, não deve ser incorporada a cada uso. Em vez disso, o GHC deve chamar uma versão especializada do plusFastCyc
. A opção 2 não é realmente uma opção, porque eu preciso da restrição no código real.
Eu tentei uma variedade de opções usando (e não usando) INLINE
, INLINABLE
e SPECIALIZE
, mas nada parece funcionar. ( EDIT : eu posso ter despojado demais plusFastCyc
para tornar meu exemplo pequeno, portanto, INLINE
pode fazer com que a função seja incorporada. Isso não acontece no meu código real porque plusFastCyc
é muito grande.) Neste exemplo em particular, não sou recebendo algum match_co: needs more cases
ou RULE: LHS too complicated to desugar
(e aqui ) avisos, embora eu estivesse recebendo muitos match_co
avisos antes de minimizar o exemplo. Presumivelmente, o "problema" é a Factored m Int
restrição na regra; se eu fizer alterações nessa restrição, será fcTest
executado o mais rápido possível vtTest
.
Estou fazendo algo que o GHC simplesmente não gosta? Por que o GHC não se especializou plusFastCyc
e como posso fazê-lo?
ATUALIZAR
O problema persiste no GHC 7.8.2, então essa questão ainda é relevante.
m
, a saberM
. Isso fez o trabalho, mas não posso me especializar para tipos fantasmas específicos no programa real, pois eles são reificados.Respostas:
O GHC também oferece uma opção para
SPECIALIZE
uma declaração de instância de classe de tipo. Eu tentei isso com o código (expandido) deFoo.hs
, colocando o seguinte:Essa mudança, no entanto, não atingiu a aceleração desejada. O que conseguiu essa melhoria de desempenho foi adicionar manualmente uma instância especializada para o tipo
VT U.Vector m Int
com as mesmas definições de função, como a seguir:Isto requer a adição
OverlappingInstances
eFlexibleInstances
noLANGUAGE
.Curiosamente, no programa de exemplo, a aceleração obtida com a instância sobreposta permanece mesmo se você remover every
SPECIALIZE
eINLINABLE
pragma.fonte