Especialização com Restrições

156

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 plusFastCycin 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.hsarquivo possui dois drivers vtTest:, que é executado em ~ 3 segundos e fcTest, que é executado em ~ 83 segundos quando compilado com -O3 usando a forallespecialização 'd.

O núcleo mostra que, para o vtTestteste, o código de adição está sendo especializado em Unboxedvetores sobre Ints, 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. ( main6chama iterate main8 y, assim main8é onde plusFastCycdeve ser especializado.)

Meu objetivo é fazer o fcTestmais rápido possível, vtTestespecializando-me plusFastCyc. Eu encontrei duas maneiras de fazer isso:

  1. Chamada de explícita inlinede GHC.Extsdentro fcTest.
  2. Remova a Factored m Intrestrição ativada plusFastCyc.

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, INLINABLEe SPECIALIZE, mas nada parece funcionar. ( EDIT : eu posso ter despojado demais plusFastCycpara tornar meu exemplo pequeno, portanto, INLINEpode 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 casesou RULE: LHS too complicated to desugar(e aqui ) avisos, embora eu estivesse recebendo muitos match_coavisos antes de minimizar o exemplo. Presumivelmente, o "problema" é a Factored m Intrestrição na regra; se eu fizer alterações nessa restrição, será fcTestexecutado 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 plusFastCyce como posso fazê-lo?

ATUALIZAR

O problema persiste no GHC 7.8.2, então essa questão ainda é relevante.

crockeea
fonte
3
Eu apenas tentei me especializar para um específico m , a saber M. Isso fez o trabalho, mas não posso me especializar para tipos fantasmas específicos no programa real, pois eles são reificados.
crockeea
Também enviei um relatório de bug do GHC ghc.haskell.org/trac/ghc/ticket/8668, mas o problema ainda está aberto. O processo de relatório de erros me ajudou a limpar um pouco a pergunta, por isso espero que seja mais fácil descobrir o que está acontecendo.
precisa saber é o seguinte
@monojohnny Lamento ouvir isso, acredito que você pode sinalizá-lo como tal. Acho que estou pedindo ao GHC que faça algo razoavelmente razoável, e não fará. Não tenho certeza se estou fazendo algo errado ou se isso é uma idiossincrasia com o compilador que pode ter uma solução alternativa. Eu já vi soluções alternativas para especialização e regras em alguma biblioteca específica sobre hackers que me escapam no momento, então espero que alguém da comunidade com mais experiência em GHC do que eu saiba como obter a especialização.
crockeea
1
Peço desculpas pelo meu tom de comentário - não é a minha melhor contribuição para este local - não há realmente nada de errado com o seu post (é a minha falta de entendimento de que era a fonte de meu aborrecimento eu acho!)
monojohnny
@monojohnny Desculpas aceitas, mas é uma pena que downvote está bloqueado agora ;-)
crockeea

Respostas:

5

O GHC também oferece uma opção para SPECIALIZEuma declaração de instância de classe de tipo. Eu tentei isso com o código (expandido) de Foo.hs, colocando o seguinte:

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

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 Intcom as mesmas definições de função, como a seguir:

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

Isto requer a adição OverlappingInstancese FlexibleInstancesno LANGUAGE.

Curiosamente, no programa de exemplo, a aceleração obtida com a instância sobreposta permanece mesmo se você remover every SPECIALIZEe INLINABLEpragma.

Diego E. Alonso-Blas
fonte
Definitivamente não é ideal, mas é a primeira solução que realmente cumpre o objetivo, então eu acho que eu vou levá-la para agora ...
crockeea