Dos documentos para o GHC 7.6:
Em geral, você nem precisa do pragma SPECIALIZE. Ao compilar um módulo M, o otimizador do GHC (com -O) considera automaticamente cada função de sobrecarga de nível superior declarada em M e a especializa para os diferentes tipos em que é chamado em M. O otimizador também considera cada função de sobrecarga INLINABLE importada, e especializa-o para os diferentes tipos em que é chamado em M.
e
Além disso, dado um pragma SPECIALIZE para uma função f, o GHC criará automaticamente especializações para quaisquer funções sobrecarregadas de classe de tipo chamadas por f, se estiverem no mesmo módulo que o pragma SPECIALIZE ou se forem INLINABLE; e assim por diante, transitivamente.
Portanto, o GHC deve especializar automaticamente algumas / a maioria / todas as funções (?) Marcadas INLINABLE
sem um pragma e, se eu usar um pragma explícito, a especialização é transitiva. Minha pergunta é: a auto-especialização é transitiva?
Especificamente, aqui está um pequeno exemplo:
Main.hs:
import Data.Vector.Unboxed as U
import Foo
main =
let y = Bar $ Qux $ U.replicate 11221184 0 :: Foo (Qux Int)
(Bar (Qux ans)) = iterate (plus y) y !! 100
in putStr $ show $ foldl1' (*) ans
Foo.hs:
module Foo (Qux(..), Foo(..), plus) where
import Data.Vector.Unboxed as U
newtype Qux r = Qux (Vector r)
-- GHC inlines `plus` if I remove the bangs or the Baz constructor
data Foo t = Bar !t
| Baz !t
instance (Num r, Unbox r) => Num (Qux r) where
{-# INLINABLE (+) #-}
(Qux x) + (Qux y) = Qux $ U.zipWith (+) x y
{-# INLINABLE plus #-}
plus :: (Num t) => (Foo t) -> (Foo t) -> (Foo t)
plus (Bar v1) (Bar v2) = Bar $ v1 + v2
O GHC especializa a chamada plus
, mas não se especializa (+)
na Qux
Num
instância que mata o desempenho.
No entanto, um pragma explícito
{-# SPECIALIZE plus :: Foo (Qux Int) -> Foo (Qux Int) -> Foo (Qux Int) #-}
resulta em especialização transitiva, conforme indicado pelos documentos, por isso (+)
é especializado e o código é 30x mais rápido (ambos compilados -O2
). Esse comportamento é esperado? Devo apenas esperar (+)
ser especializado transitivamente com um pragma explícito?
ATUALIZAR
Os documentos para 7.8.2 não foram alterados e o comportamento é o mesmo, portanto, essa pergunta ainda é relevante.
plus
foi marcado como INLINABLE e 2) simonpj indicou que havia algo de errado com o código do ticket, mas o núcleo de meu exemplo mostra que nenhuma das funções estava embutida (em particular, eu não conseguia me livrar do segundo construtor, caso contrário, coisas embutidas no GHC). Foo
plus (Bar v1) = \(Bar v2)-> Bar $ v1 + v2
, para que o LHS seja totalmente aplicado no local da chamada? Ele é embutido e a especialização é ativada?
plus
totalmente aplicada devido a esses links, mas, na verdade, recebi menos especialização: a chamada para plus
também não era especializada. Não tenho explicação para isso, mas pretendia deixar para outra pergunta, ou espero que isso seja resolvido em resposta a esta.