moonlight-delta-0.1.0.0: bench/core/CoreBench.hs
{-# LANGUAGE BangPatterns #-}
module CoreBench
( coreBenchmarks,
)
where
import Data.IntSet
( IntSet,
)
import Data.IntSet qualified as IntSet
import Data.Map.Strict
( Map,
)
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import BenchSupport
( benchFailure,
caseLabel,
deltaSizes,
frontierDominatedSizes,
frontierSizes,
keys,
naturalWeight,
repeatedDeltaKeys,
)
import Moonlight.Delta.Frontier
( frontierContains,
frontierPoints,
mkFrontier,
mkProductFrontier2,
productFrontier2Contains,
productFrontier2Points,
)
import Moonlight.Delta.Normalize
( normalizeDelta,
)
import Moonlight.Delta.Scope
( Scope,
dirtyScope,
restrictScope,
scopeKeys,
unionScope,
)
import Moonlight.Delta.Signed
( Multiplicity (..),
SignedApplyError,
Signed,
applySignedToMap,
combineSigned,
signedFromList,
signedNull,
support,
)
import Test.Tasty.Bench
( Benchmark,
bench,
bgroup,
nf,
whnf,
)
coreBenchmarks :: Benchmark
coreBenchmarks =
bgroup
"core"
[ signedDeltaBenchmarks,
frontierBenchmarks,
scopeBenchmarks
]
signedDeltaBenchmarks :: Benchmark
signedDeltaBenchmarks =
bgroup
"signed-delta"
(deltaSizes >>= signedDeltaBenchmarksForSize)
signedDeltaBenchmarksForSize :: Int -> [Benchmark]
signedDeltaBenchmarksForSize size =
[ bench (caseLabel "prepared normalize" size) (whnf normalizeDelta preparedDelta),
bench (caseLabel "prepared null" size) (whnf signedNull preparedDelta),
bench (caseLabel "prepared support" size) (nf (Set.size . support) preparedDelta),
bench (caseLabel "fromList/normalize" size) (nf signedBuildWeight size),
bench (caseLabel "build/combine" size) (nf signedComposeSizeWeight size),
bench (caseLabel "build/apply to map" size) (nf signedApplySizeWeight size),
bench (caseLabel "apply first underflow" size) (nf signedFirstUnderflowWeight (signedFirstUnderflowDelta size)),
bench (caseLabel "repeated sparse apply stream" size) (nf signedRepeatedSparseApplyWeight size)
]
where
preparedDelta =
signedDelta size
signedFirstUnderflowDelta :: Int -> Signed Int
signedFirstUnderflowDelta size =
signedFromList ((0, -1) : fmap (\key -> (key, 1)) [1 .. max 0 (size - 1)])
signedFirstUnderflowWeight :: Signed Int -> Int
signedFirstUnderflowWeight deltaValue =
case applySignedToMap deltaValue Map.empty of
Left _underflow -> 1
Right unexpectedState -> Map.size unexpectedState
signedBuildWeight :: Int -> Int
signedBuildWeight =
Set.size . support . signedDelta
signedComposeWeight :: (Signed Int, Signed Int) -> Int
signedComposeWeight (newer, older) =
Set.size (support (combineSigned newer older))
signedComposeSizeWeight :: Int -> Int
signedComposeSizeWeight size =
signedComposeWeight (signedDelta size, shiftedSigned size)
signedApplySizeWeight :: Int -> Int
signedApplySizeWeight size =
signedApplyWeight (initialSignedState size, signedDelta size)
signedApplyWeight :: (Map Int Multiplicity, Signed Int) -> Int
signedApplyWeight (stateValue, deltaValue) =
case applySignedToMap deltaValue stateValue of
Left err -> benchFailure "signed apply" err
Right updatedState -> Map.size updatedState
signedRepeatedSparseApplyWeight :: Int -> Int
signedRepeatedSparseApplyWeight size =
signedRepeatedApplyWeight (repeatedSignedInitialState, repeatedSignedStream size)
signedRepeatedApplyWeight :: (Map Int Multiplicity, [Signed Int]) -> Int
signedRepeatedApplyWeight (initialState, deltas) =
case foldl' applySignedStreamStep (Right initialState) deltas of
Left err -> benchFailure "signed repeated apply" err
Right finalState -> Map.foldl' (\total (Multiplicity value) -> total + naturalWeight value) 0 finalState
applySignedStreamStep :: Either (SignedApplyError Int) (Map Int Multiplicity) -> Signed Int -> Either (SignedApplyError Int) (Map Int Multiplicity)
applySignedStreamStep state deltaValue =
state >>= applySignedToMap deltaValue
repeatedSignedInitialState :: Map Int Multiplicity
repeatedSignedInitialState =
Map.fromAscList (fmap (\key -> (key, Multiplicity 1)) repeatedDeltaKeys)
repeatedSignedStream :: Int -> [Signed Int]
repeatedSignedStream size =
fmap
(\step ->
signedFromList
( fmap
(\key -> (key, if even (key + step) then 1 else -1))
repeatedDeltaKeys
)
)
(keys size)
signedDelta :: Int -> Signed Int
signedDelta =
signedFromList . signedEntries
shiftedSigned :: Int -> Signed Int
shiftedSigned size =
signedFromList
[ (key + 1, signedAmount key)
| key <- keys size
]
signedEntries :: Int -> [(Int, Int)]
signedEntries size =
[ (key, signedAmount key)
| key <- keys size
]
signedAmount :: Int -> Int
signedAmount key
| even key = 1
| otherwise = -1
initialSignedState :: Int -> Map Int Multiplicity
initialSignedState size =
Map.fromAscList
[ (key, Multiplicity 1)
| key <- keys (size + 1)
]
frontierBenchmarks :: Benchmark
frontierBenchmarks =
bgroup
"frontier-antichain"
( (frontierSizes >>= frontierDiagonalBenchmarksForSize)
<> (frontierDominatedSizes >>= frontierDominatedBenchmarksForSize)
)
frontierDiagonalBenchmarksForSize :: Int -> [Benchmark]
frontierDiagonalBenchmarksForSize size =
[ bench (caseLabel "generic build diagonal" size) (nf genericFrontierBuildWeight size),
bench (caseLabel "product2 build diagonal" size) (nf productFrontierBuildWeight size),
bench (caseLabel "generic build/contains sweep" size) (nf genericFrontierContainsSizeWeight size),
bench (caseLabel "product2 build/contains sweep" size) (nf productFrontierContainsSizeWeight size)
]
frontierDominatedBenchmarksForSize :: Int -> [Benchmark]
frontierDominatedBenchmarksForSize size =
[ bench (caseLabel "generic dominated-chain build/contains" size) (nf genericFrontierDominatedContainsWeight size),
bench (caseLabel "product2 dominated-chain build/contains" size) (nf productFrontierDominatedContainsWeight size)
]
genericFrontierBuildWeight :: Int -> Int
genericFrontierBuildWeight =
length . frontierPoints . mkFrontier . frontierDiagonal
productFrontierBuildWeight :: Int -> Int
productFrontierBuildWeight =
length . productFrontier2Points . mkProductFrontier2 . frontierDiagonal
genericFrontierContainsSizeWeight :: Int -> Int
genericFrontierContainsSizeWeight size =
let frontier =
mkFrontier (frontierDiagonal size)
in length
( filter
(\point -> frontierContains point frontier)
(frontierProbePoints size)
)
productFrontierContainsSizeWeight :: Int -> Int
productFrontierContainsSizeWeight size =
let frontier =
mkProductFrontier2 (frontierDiagonal size)
in length
( filter
(\point -> productFrontier2Contains point frontier)
(frontierProbePoints size)
)
genericFrontierDominatedContainsWeight :: Int -> Int
genericFrontierDominatedContainsWeight size =
let frontier =
mkFrontier (frontierDominatedChain size)
in length
( filter
(\point -> frontierContains point frontier)
(frontierProbePoints size)
)
productFrontierDominatedContainsWeight :: Int -> Int
productFrontierDominatedContainsWeight size =
let frontier =
mkProductFrontier2 (frontierDominatedChain size)
in length
( filter
(\point -> productFrontier2Contains point frontier)
(frontierProbePoints size)
)
frontierDiagonal :: Int -> [(Int, Int)]
frontierDiagonal size =
[ (key, size - key)
| key <- keys size
]
frontierProbePoints :: Int -> [(Int, Int)]
frontierProbePoints size =
fmap (\key -> (key, key)) (keys size)
frontierDominatedChain :: Int -> [(Int, Int)]
frontierDominatedChain size =
fmap (\key -> (key, key)) (keys size)
scopeBenchmarks :: Benchmark
scopeBenchmarks =
bgroup
"scope"
(deltaSizes >>= scopeBenchmarksForSize)
scopeBenchmarksForSize :: Int -> [Benchmark]
scopeBenchmarksForSize size =
[ bench (caseLabel "Scope union/restrict" size) (nf scopeUnionRestrictWeight size)
]
scopeUnionRestrictWeight :: Int -> Int
scopeUnionRestrictWeight size =
scopeWeight
( restrictScope
(IntSet.fromAscList (sampleKeys size))
(unionScope (dirtyScope (evenKeySet size)) (dirtyScope (thirdKeySet size)))
)
scopeWeight :: Scope IntSet -> Int
scopeWeight =
maybe 0 IntSet.size . scopeKeys
evenKeySet :: Int -> IntSet
evenKeySet size =
IntSet.fromAscList
[ key
| key <- keys size,
even key
]
thirdKeySet :: Int -> IntSet
thirdKeySet size =
IntSet.fromAscList
[ key
| key <- keys size,
key `mod` 3 == 0
]
sampleKeys :: Int -> [Int]
sampleKeys size =
keys size