horde-ad-0.1.0.0: bench/common/BenchProdTools.hs
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wno-missing-export-lists #-}
-- {-# OPTIONS_GHC -ddump-stranal #-}
-- | A contrived benchmark: a product of a list of scalars.
module BenchProdTools where
import Prelude
import Control.DeepSeq (NFData (..))
import Criterion.Main
import Data.Default qualified as Default
import Data.Foldable qualified as Foldable
import Data.List (foldl1')
import Data.Proxy (Proxy (Proxy))
import GHC.Exts (IsList (..), WithDict)
import GHC.TypeLits (KnownNat)
import Test.Inspection
import Type.Reflection (Typeable)
import Data.Array.Nested qualified as Nested
import Data.Array.Nested.Ranked.Shape
import HordeAd
import HordeAd.Core.Adaptor
import HordeAd.Core.Ops
bgroup100, bgroup1000, bgroup1e4, bgroup1e5, bgroup1e6, bgroup1e7, bgroup5e7 :: [Double] -> Benchmark
bgroup100 = envProd 100 $ \args -> bgroup "100" $ benchProd args
bgroup1000 = envProd 1000 $ \args -> bgroup "1000" $ benchProd args
bgroup1e4 = envProd 1e4 $ \args -> bgroup "1e4" $ benchProd args
bgroup1e5 = envProd 1e5 $ \args -> bgroup "1e5" $ benchProd args
bgroup1e6 = envProd 1e6 $ \args -> bgroup "1e6" $ benchProd args
bgroup1e7 = envProd 1e7 $ \args -> bgroup "1e7" $ benchProd args
bgroup5e7 = envProd 5e7 $ \args -> bgroup "5e7" $ benchProd args
-- 5e7 == 5 * 10^7 == 0.5 * 10^8 == 0.5e8
envProd :: r ~ Double
=> Rational
-> (forall n.
( SNat n
, [Concrete (TKScalar r)]
, ListR n (Concrete (TKScalar r))
, ListR n (Concrete (TKS '[] r))
, Concrete (TKS '[n] r) )
-> Benchmark)
-> [r]
-> Benchmark
envProd rat f allxs =
let k = round rat
in withSNat k $ \(SNat @k) ->
env (return $!
let l = take k allxs
lt = map sscalar l
in ( SNat @k
, map Concrete l
, fromList (map Concrete l)
, fromList lt
, sfromList . fromList $ lt) )
(f @k)
benchProd :: r ~ Double
=> ( SNat n
, [Concrete (TKScalar r)]
, ListR n (Concrete (TKScalar r))
, ListR n (Concrete (TKS '[] r))
, Concrete (TKS '[n] r) )
-> [Benchmark]
benchProd ~(snat, list, l, lt, t) = case snat of
SNat ->
[ bench "cgrad s MapAccum" $ nf (crevSMapAccum snat) t
, bench "grad s MapAccum" $ nf (revSMapAccum snat) t
, bench "cgrad scalar MapAccum" $ nf (crevScalarMapAccum snat) t
, bench "grad scalar MapAccum" $ nf (revScalarMapAccum snat) t
, bench "cgrad scalar list" $ nf crevScalarList list
, bench "grad scalar list" $ nf revScalarList list
, bench "cgrad scalar L" $ nf (crevScalarL snat) l
, bench "grad scalar L" $ nf (revScalarL snat) l
, bench "cgrad scalar R" $ nf (crevScalarR snat) l
, bench "grad scalar R" $ nf (revScalarR snat) l
, bench "cgrad scalar NotShared" $ nf (crevScalarNotShared snat) l
, bench "cgrad s L" $ nf (crevSL snat) lt
, bench "grad s L" $ nf (revSL snat) lt
, bench "cgrad s R" $ nf (crevSR snat) lt
, bench "grad s R" $ nf (revSR snat) lt
, bench "cgrad s NotShared" $ nf (crevSNotShared snat) lt
]
-- Another variant, with foldl1' and indexing, would be a disaster.
-- We can define sproduct if this benchmark ends up used anywhere,
-- because the current codomain of gradientFromDelta rules out
-- low-level hacky pipeline tricks that could avoid indexing.
multSMapAccum :: (BaseTensor target, LetTensor target, GoodScalar r)
=> SNat n -> target (TKS '[n] r) -> target (TKS '[] r)
multSMapAccum SNat = sfold (*) (sscalar 1)
{-# SPECIALIZE multSMapAccum :: SNat n -> ADVal Concrete (TKS '[n] Double) -> ADVal Concrete (TKS '[] Double) #-}
{-# SPECIALIZE multSMapAccum :: SNat n -> AstTensor AstMethodLet FullSpan (TKS '[n] Double) -> AstTensor AstMethodLet FullSpan (TKS '[] Double) #-}
crevSMapAccum
:: SNat n -> Concrete (TKS '[n] Double) -> Concrete (TKS '[n] Double)
crevSMapAccum snat@SNat = cgrad (kfromS . multSMapAccum snat)
revSMapAccum
:: SNat n -> Concrete (TKS '[n] Double) -> Concrete (TKS '[n] Double)
revSMapAccum snat@SNat = grad (kfromS . multSMapAccum snat)
multScalarMapAccum :: forall target n r.
(BaseTensor target, GoodScalar r)
=> SNat n -> target (TKS '[n] r) -> target (TKScalar r)
multScalarMapAccum snat@SNat =
tproject1
. tmapAccumL (Proxy @target)
snat
(FTKScalar @r)
(FTKScalar @Z1)
(FTKScalar @r)
(let g :: forall f. ADReady f
=> f (TKScalar r) -> f (TKScalar r)
-> f (TKProduct (TKScalar r) TKUnit)
g !acc !e = tpair (acc * e) tunit
in g)
1
{-# SPECIALIZE multScalarMapAccum :: SNat n -> ADVal Concrete (TKS '[n] Double) -> ADVal Concrete (TKScalar Double) #-}
{-# SPECIALIZE multScalarMapAccum :: SNat n -> AstTensor AstMethodLet FullSpan (TKS '[n] Double) -> AstTensor AstMethodLet FullSpan (TKScalar Double) #-}
crevScalarMapAccum
:: SNat n -> Concrete (TKS '[n] Double) -> Concrete (TKS '[n] Double)
crevScalarMapAccum snat@SNat = cgrad (multScalarMapAccum snat)
revScalarMapAccum
:: SNat n -> Concrete (TKS '[n] Double) -> Concrete (TKS '[n] Double)
revScalarMapAccum snat@SNat = grad (multScalarMapAccum snat)
multScalarList :: (BaseTensor target, GoodScalar r)
=> [target (TKScalar r)] -> target (TKScalar r)
multScalarList = foldl1' (*)
crevScalarList
:: [Concrete (TKScalar Double)] -> [Concrete (TKScalar Double)]
crevScalarList =
cgrad multScalarList
revScalarList
:: [Concrete (TKScalar Double)] -> [Concrete (TKScalar Double)]
revScalarList =
grad multScalarList
multScalarL :: (BaseTensor target, GoodScalar r)
=> ListR n (target (TKScalar r)) -> target (TKScalar r)
multScalarL = foldl1' (*) . Foldable.toList
crevScalarL
:: SNat n -> ListR n (Concrete (TKScalar Double))
-> ListR n (Concrete (TKScalar Double))
crevScalarL snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKScalar Double)) snat) $
cgrad multScalarL
revScalarL
:: SNat n -> ListR n (Concrete (TKScalar Double))
-> ListR n (Concrete (TKScalar Double))
revScalarL snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKScalar Double)) snat) $
grad multScalarL
multScalarR :: (BaseTensor target, GoodScalar r)
=> ListR n (target (TKScalar r)) -> target (TKScalar r)
multScalarR = foldr1 (*)
crevScalarR
:: SNat n -> ListR n (Concrete (TKScalar Double))
-> ListR n (Concrete (TKScalar Double))
crevScalarR snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKScalar Double)) snat) $
cgrad multScalarR
revScalarR
:: SNat n -> ListR n (Concrete (TKScalar Double))
-> ListR n (Concrete (TKScalar Double))
revScalarR snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKScalar Double)) snat) $
grad multScalarR
multScalarNotShared :: (BaseTensor target, GoodScalar r)
=> ListR n (ADVal target (TKScalar r))
-> ADVal target (TKScalar r)
multScalarNotShared = foldr1 multNotShared
crevScalarNotShared
:: SNat n -> ListR n (Concrete (TKScalar Double))
-> ListR n (Concrete (TKScalar Double))
crevScalarNotShared snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKScalar Double)) snat) $
cgrad multScalarNotShared
multSL :: (BaseTensor target, GoodScalar r)
=> ListR n (target (TKS '[] r)) -> target (TKS '[] r)
multSL = foldl1' (*) . Foldable.toList
crevSL
:: SNat n -> ListR n (Concrete (TKS '[] Double))
-> ListR n (Concrete (TKS '[] Double))
crevSL snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKS '[] Double)) snat) $
cgrad (kfromS . multSL)
revSL
:: SNat n -> ListR n (Concrete (TKS '[] Double))
-> ListR n (Concrete (TKS '[] Double))
revSL snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKS '[] Double)) snat) $
grad (kfromS . multSL)
multSR :: (BaseTensor target, GoodScalar r)
=> ListR n (target (TKS '[] r)) -> target (TKS '[] r)
multSR = foldr1 (*)
crevSR
:: SNat n -> ListR n (Concrete (TKS '[] Double))
-> ListR n (Concrete (TKS '[] Double))
crevSR snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKS '[] Double)) snat) $
cgrad (kfromS . multSR)
revSR
:: SNat n -> ListR n (Concrete (TKS '[] Double))
-> ListR n (Concrete (TKS '[] Double))
revSR snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKS '[] Double)) snat) $
grad (kfromS . multSR)
multSNotShared :: (BaseTensor target, GoodScalar r)
=> ListR n (ADVal target (TKS '[] r))
-> ADVal target (TKS '[] r)
multSNotShared = foldr1 multNotShared
crevSNotShared
:: SNat n -> ListR n (Concrete (TKS '[] Double))
-> ListR n (Concrete (TKS '[] Double))
crevSNotShared snat@SNat =
withKnownSTK (stkOfListR (knownSTK @(TKS '[] Double)) snat) $
cgrad (kfromS . multSNotShared)
{- TODO: re-enable once -fpolymorphic-specialisation works
-- KnownNat and AstSpan are tag types, so it's fine not to specialize
-- for them. Some of the other classes come from existential types,
-- some of which it's not advantageous to specialize.
--
-- This is expected to fail with -O0 and to pass with -O1
-- and -fpolymorphic-specialisation.
-- This prevents running benchmarks without optimization, which is a good thing.
inspect $ hasNoTypeClassesExcept 'crevScalarL [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt]
inspect $ hasNoTypeClassesExcept 'revScalarL [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt, ''AstSpan, ''RealFloatH, ''Nested.FloatElt, ''Fractional, ''Floating, ''IntegralH, ''RealFrac, ''Real, ''Nested.Storable, ''Integral]
inspect $ hasNoTypeClassesExcept 'crevScalarNotShared [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt]
inspect $ hasNoTypeClassesExcept 'crevSL [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt, ''Nested.Storable, ''ShareTensor]
inspect $ hasNoTypeClassesExcept 'revSL [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt, ''AstSpan, ''RealFloatH, ''Nested.FloatElt, ''Fractional, ''Floating, ''IntegralH, ''RealFrac, ''Real, ''Nested.Storable, ''Integral]
inspect $ hasNoTypeClassesExcept 'crevSMapAccum [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt, ''LetTensor, ''BaseTensor, ''ConvertTensor, ''Boolean, ''CommonTargetEqOrd, ''AllTargetShow, ''ShareTensor]
inspect $ hasNoTypeClassesExcept 'revSMapAccum [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt, ''LetTensor, ''BaseTensor, ''ConvertTensor, ''Boolean, ''CommonTargetEqOrd, ''AllTargetShow, ''ShareTensor, ''AstSpan, ''RealFloatH, ''Nested.FloatElt, ''Fractional, ''Floating, ''IntegralH, ''RealFrac, ''Real, ''Nested.Storable, ''Integral]
inspect $ hasNoTypeClassesExcept 'crevScalarMapAccum [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt, ''LetTensor, ''BaseTensor, ''ConvertTensor, ''Boolean, ''CommonTargetEqOrd, ''AllTargetShow, ''ShareTensor]
inspect $ hasNoTypeClassesExcept 'revScalarMapAccum [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''GoodScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''IfDifferentiable, ''NFData, ''Default.Default, ''Nested.Elt, ''LetTensor, ''BaseTensor, ''ConvertTensor, ''Boolean, ''CommonTargetEqOrd, ''AllTargetShow, ''ShareTensor, ''AstSpan, ''RealFloatH, ''Nested.FloatElt, ''Fractional, ''Floating, ''IntegralH, ''RealFrac, ''Real, ''Nested.Storable, ''Integral]
-- inspect $ coreOf 'revScalarL
-}