horde-ad-0.3.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 $ bgroup "100" . benchProd
bgroup1000 = envProd 1000 $ bgroup "1000" . benchProdShort
bgroup1e4 = envProd 1e4 $ bgroup "1e4" . benchProdShort
bgroup1e5 = envProd 1e5 $ bgroup "1e5" . benchProdShortest
bgroup1e6 = envProd 1e6 $ bgroup "1e6" . benchProdShortest
bgroup1e7 = envProd 1e7 $ bgroup "1e7" . benchProdShortest
bgroup5e7 = envProd 5e7 $ bgroup "5e7" . benchProdShortest
-- 5e7 == 5 * 10^7 == 0.5 * 10^8 == 0.5e8
envProd :: r ~ Double
=> Rational
-> (forall n.
( SNat n
, [Concrete (TKScalar r)]
, IxR n (Concrete (TKScalar r))
, IxR 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)]
, IxR n (Concrete (TKScalar r))
, IxR 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 k MapAccum" $ nf (crevScalarMapAccum snat) t
, bench "grad k MapAccum" $ nf (revScalarMapAccum snat) t
, bench "cgrad k list" $ nf crevScalarList list
, bench "grad k list" $ nf revScalarList list
, bench "cgrad k L" $ nf (crevScalarL snat) l
, bench "grad k L" $ nf (revScalarL snat) l
, bench "cgrad k R" $ nf (crevScalarR snat) l
, bench "grad k R" $ nf (revScalarR snat) l
, bench "cgrad k 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
]
benchProdShort
:: r ~ Double
=> ( SNat n
, [Concrete (TKScalar r)]
, IxR n (Concrete (TKScalar r))
, IxR n (Concrete (TKS '[] r))
, Concrete (TKS '[n] r) )
-> [Benchmark]
benchProdShort ~(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 k MapAccum" $ nf (crevScalarMapAccum snat) t
, bench "grad k MapAccum" $ nf (revScalarMapAccum snat) t
-- , bench "cgrad k list" $ nf crevScalarList list
-- , bench "grad k list" $ nf revScalarList list
, bench "cgrad k L" $ nf (crevScalarL snat) l
, bench "grad k L" $ nf (revScalarL snat) l
-- , bench "cgrad k R" $ nf (crevScalarR snat) l
-- , bench "grad k R" $ nf (revScalarR snat) l
, bench "cgrad k 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
]
benchProdShortest
:: r ~ Double
=> ( SNat n
, [Concrete (TKScalar r)]
, IxR n (Concrete (TKScalar r))
, IxR n (Concrete (TKS '[] r))
, Concrete (TKS '[n] r) )
-> [Benchmark]
benchProdShortest ~(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 k MapAccum" $ nf (crevScalarMapAccum snat) t
, bench "grad k MapAccum" $ nf (revScalarMapAccum snat) t
-- , bench "cgrad k list" $ nf crevScalarList list
-- , bench "grad k list" $ nf revScalarList list
-- , bench "cgrad k L" $ nf (crevScalarL snat) l
-- , bench "grad k L" $ nf (revScalarL snat) l
-- , bench "cgrad k R" $ nf (crevScalarR snat) l
-- , bench "grad k R" $ nf (revScalarR snat) l
-- , bench "cgrad k 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, NumScalar 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 @_ @_ @_ @Concrete(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. (ADReady target, NumScalar r)
=> SNat n -> target (TKS '[n] r) -> target (TKScalar r)
multScalarMapAccum snat = tfold snat STKScalar STKScalar (*) 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 @_ @_ @_ @Concrete (multScalarMapAccum snat)
revScalarMapAccum
:: SNat n -> Concrete (TKS '[n] Double) -> Concrete (TKS '[n] Double)
revScalarMapAccum snat@SNat = grad (multScalarMapAccum snat)
multScalarList :: (BaseTensor target, NumScalar r)
=> [target (TKScalar r)] -> target (TKScalar r)
multScalarList = foldl1' (*)
crevScalarList
:: [Concrete (TKScalar Double)] -> [Concrete (TKScalar Double)]
crevScalarList =
cgrad @_ @_ @_ @Concrete multScalarList
revScalarList
:: [Concrete (TKScalar Double)] -> [Concrete (TKScalar Double)]
revScalarList =
grad multScalarList
multScalarL :: (BaseTensor target, NumScalar r)
=> IxR n (target (TKScalar r)) -> target (TKScalar r)
multScalarL = foldl1' (*) . Foldable.toList
crevScalarL
:: SNat n -> IxR n (Concrete (TKScalar Double))
-> IxR n (Concrete (TKScalar Double))
crevScalarL snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKScalar Double)) snat) $
cgrad @_ @_ @_ @Concrete multScalarL
revScalarL
:: SNat n -> IxR n (Concrete (TKScalar Double))
-> IxR n (Concrete (TKScalar Double))
revScalarL snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKScalar Double)) snat) $
grad multScalarL
multScalarR :: (BaseTensor target, NumScalar r)
=> IxR n (target (TKScalar r)) -> target (TKScalar r)
multScalarR = foldr1 (*)
crevScalarR
:: SNat n -> IxR n (Concrete (TKScalar Double))
-> IxR n (Concrete (TKScalar Double))
crevScalarR snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKScalar Double)) snat) $
cgrad @_ @_ @_ @Concrete multScalarR
revScalarR
:: SNat n -> IxR n (Concrete (TKScalar Double))
-> IxR n (Concrete (TKScalar Double))
revScalarR snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKScalar Double)) snat) $
grad multScalarR
multScalarNotShared :: (BaseTensor target, NumScalar r)
=> IxR n (ADVal target (TKScalar r))
-> ADVal target (TKScalar r)
multScalarNotShared = foldr1 multNotShared
crevScalarNotShared
:: SNat n -> IxR n (Concrete (TKScalar Double))
-> IxR n (Concrete (TKScalar Double))
crevScalarNotShared snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKScalar Double)) snat) $
cgrad @_ @_ @_ @Concrete multScalarNotShared
multSL :: (BaseTensor target, NumScalar r)
=> IxR n (target (TKS '[] r)) -> target (TKS '[] r)
multSL = foldl1' (*) . Foldable.toList
crevSL
:: SNat n -> IxR n (Concrete (TKS '[] Double))
-> IxR n (Concrete (TKS '[] Double))
crevSL snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKS '[] Double)) snat) $
cgrad @_ @_ @_ @Concrete (kfromS . multSL)
revSL
:: SNat n -> IxR n (Concrete (TKS '[] Double))
-> IxR n (Concrete (TKS '[] Double))
revSL snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKS '[] Double)) snat) $
grad (kfromS . multSL)
multSR :: (BaseTensor target, NumScalar r)
=> IxR n (target (TKS '[] r)) -> target (TKS '[] r)
multSR = foldr1 (*)
crevSR
:: SNat n -> IxR n (Concrete (TKS '[] Double))
-> IxR n (Concrete (TKS '[] Double))
crevSR snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKS '[] Double)) snat) $
cgrad @_ @_ @_ @Concrete (kfromS . multSR)
revSR
:: SNat n -> IxR n (Concrete (TKS '[] Double))
-> IxR n (Concrete (TKS '[] Double))
revSR snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKS '[] Double)) snat) $
grad (kfromS . multSR)
multSNotShared :: (BaseTensor target, NumScalar r)
=> IxR n (ADVal target (TKS '[] r))
-> ADVal target (TKS '[] r)
multSNotShared = foldr1 multNotShared
crevSNotShared
:: SNat n -> IxR n (Concrete (TKS '[] Double))
-> IxR n (Concrete (TKS '[] Double))
crevSNotShared snat@SNat =
withKnownSTK (stkOfIxR (knownSTK @(TKS '[] Double)) snat) $
cgrad @_ @_ @_ @Concrete (kfromS . multSNotShared)
{- TODO: re-enable once -fpolymorphic-specialisation works
-- KnownNat and KnownSpan 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, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt]
inspect $ hasNoTypeClassesExcept 'revScalarL [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt, ''KnownSpan, ''RealFloatH, ''Nested.FloatElt, ''Fractional, ''Floating, ''IntegralH, ''RealFrac, ''Real, ''Nested.Storable, ''Integral]
inspect $ hasNoTypeClassesExcept 'crevScalarNotShared [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt]
inspect $ hasNoTypeClassesExcept 'crevSL [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt, ''Nested.Storable, ''ShareTensor]
inspect $ hasNoTypeClassesExcept 'revSL [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt, ''KnownSpan, ''RealFloatH, ''Nested.FloatElt, ''Fractional, ''Floating, ''IntegralH, ''RealFrac, ''Real, ''Nested.Storable, ''Integral]
inspect $ hasNoTypeClassesExcept 'crevSMapAccum [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt, ''LetTensor, ''BaseTensor, ''ConvertTensor, ''Boolean, ''CommonTargetEqOrd, ''AllTargetShow, ''ShareTensor]
inspect $ hasNoTypeClassesExcept 'revSMapAccum [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt, ''LetTensor, ''BaseTensor, ''ConvertTensor, ''Boolean, ''CommonTargetEqOrd, ''AllTargetShow, ''ShareTensor, ''KnownSpan, ''RealFloatH, ''Nested.FloatElt, ''Fractional, ''Floating, ''IntegralH, ''RealFrac, ''Real, ''Nested.Storable, ''Integral]
inspect $ hasNoTypeClassesExcept 'crevScalarMapAccum [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt, ''LetTensor, ''BaseTensor, ''ConvertTensor, ''Boolean, ''CommonTargetEqOrd, ''AllTargetShow, ''ShareTensor]
inspect $ hasNoTypeClassesExcept 'revScalarMapAccum [''(~), ''KnownNat, ''WithDict, ''KnownShS, ''AdaptableTarget, ''RandomValue, ''KnownSTK, ''NumScalar, ''Num, ''Show, ''Ord, ''Eq, ''Nested.PrimElt, ''Nested.KnownElt, ''Nested.NumElt, ''Typeable, ''NFData, ''Default.Default, ''Nested.Elt, ''LetTensor, ''BaseTensor, ''ConvertTensor, ''Boolean, ''CommonTargetEqOrd, ''AllTargetShow, ''ShareTensor, ''KnownSpan, ''RealFloatH, ''Nested.FloatElt, ''Fractional, ''Floating, ''IntegralH, ''RealFrac, ''Real, ''Nested.Storable, ''Integral]
-- inspect $ coreOf 'revScalarL
-}