Skip to content

Commit 0d3e7e9

Browse files
geo2aamesgen
authored andcommitted
ouroboros-consensus: add Peras chain weight benchmark
1 parent ea8c83a commit 0d3e7e9

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

docs/website/contents/for-developers/Benchmarks.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,13 @@ To aid the refactoring of the ChainSync client, we added a benchmark for it in [
2020
```sh
2121
cabal new-run ouroboros-consensus:ChainSync-client-bench -- 10 10
2222
```
23+
24+
## PerasCertDB Benchmark
25+
26+
We have a microbenchmark for the boosted chain fragment weight calculation, which could be run as follows:
27+
28+
```sh
29+
cabal run ouroboros-consensus:PerasCertDB-bench -- +RTS -T -A32m -RTS
30+
```
31+
32+
We request GHC runtime system statistics with `-T` to get a memory usage estimate, and also request a large nursery with `-A32m` to minimise garbage collection. See `tasty-bench` [documentation](https://github.com/Bodigrim/tasty-bench?tab=readme-ov-file#troubleshooting) for more tips.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
{-# LANGUAGE ImportQualifiedPost #-}
2+
{-# LANGUAGE LambdaCase #-}
3+
4+
-- | This module contains benchmarks for Peras chain weight calculation as implemented by
5+
-- the by the 'Ouroboros.Consensus.Storage.PerasCertDB.API.boostedWeightForFragment'
6+
-- function.
7+
--
8+
-- We benchmark the calculation on a static sequence of chain fragments of increasing
9+
-- length, ranging from 0 to around 8640, with a sampling rate of 100. The chain fragments
10+
-- are instantiated with 'TestBlock', and every 5 blocks there is a booster block with
11+
-- weight 15. All parameters are set in 'benchmarkParams'.
12+
module Main (main) where
13+
14+
import Data.List (iterate')
15+
import Data.Map.Strict qualified as Map
16+
import Numeric.Natural (Natural)
17+
import Ouroboros.Consensus.Block (PerasWeight (PerasWeight), SlotNo (..))
18+
import Ouroboros.Consensus.Storage.PerasCertDB.API
19+
( PerasWeightSnapshot (..)
20+
, boostedWeightForFragment
21+
)
22+
import Ouroboros.Network.AnchoredFragment qualified as AF
23+
import Test.Ouroboros.Storage.TestBlock (TestBlock (..), TestBody (..), TestHeader (..))
24+
import Test.Ouroboros.Storage.TestBlock qualified as TestBlock
25+
import Test.Tasty.Bench
26+
27+
data BenchmarkParams = BenchmarkParams
28+
{ blockRate :: SlotNo
29+
-- ^ How often the fragments will contain blocks, in slots
30+
, fragmentLenghtSamplingRate :: Natural
31+
-- ^ The rate of length increase for generate chain fragments
32+
, fragmentMaxLenght :: Natural
33+
-- ^ the maximum length of a fragment
34+
, boostedBlockRate :: Natural
35+
-- ^ How often boosted blocks occur, in blocks
36+
, boostWeight :: PerasWeight
37+
-- ^ The weight of the boost
38+
}
39+
40+
benchmarkParams :: BenchmarkParams
41+
benchmarkParams =
42+
BenchmarkParams
43+
{ blockRate = 20
44+
, fragmentLenghtSamplingRate = 100
45+
, fragmentMaxLenght = 2160 + 3 * 2160
46+
, boostedBlockRate = 5
47+
, boostWeight = PerasWeight 15
48+
}
49+
50+
main :: IO ()
51+
main =
52+
Test.Tasty.Bench.defaultMain $ map benchBoostedWeightForFragment inputs
53+
where
54+
-- NOTE: we do not use the 'env' combinator to set up the test data since
55+
-- it requires 'NFData' for 'AF.AnchoredFragment'. While the necessary
56+
-- instances could be provided, we do not think is necessary for this
57+
-- benchmark, as the input data is rather small.
58+
inputs :: [(Natural, (PerasWeightSnapshot TestBlock, AF.AnchoredFragment TestBlock))]
59+
inputs =
60+
getEveryN (fragmentLenghtSamplingRate benchmarkParams) $
61+
take (fromIntegral $ fragmentMaxLenght benchmarkParams) $
62+
zip [0 ..] $
63+
zip (map uniformWeightSnapshot fragments) fragments
64+
65+
benchBoostedWeightForFragment ::
66+
(Natural, (PerasWeightSnapshot TestBlock, AF.AnchoredFragment TestBlock)) -> Benchmark
67+
benchBoostedWeightForFragment (i, (weightSnapshot, fragment)) =
68+
bench ("boostedWeightForFragment of length " <> show i) $
69+
whnf (boostedWeightForFragment weightSnapshot) fragment
70+
71+
-- | An infinite list of chain fragments
72+
fragments :: [AF.AnchoredFragment TestBlock]
73+
fragments = iterate' addSuccessorBlock genesisFragment
74+
where
75+
genesisFragment :: AF.AnchoredFragment TestBlock
76+
genesisFragment = AF.Empty AF.AnchorGenesis
77+
78+
addSuccessorBlock :: AF.AnchoredFragment TestBlock -> AF.AnchoredFragment TestBlock
79+
addSuccessorBlock = \case
80+
AF.Empty _ -> (AF.Empty AF.AnchorGenesis) AF.:> (TestBlock.firstBlock 0 dummyBody)
81+
(xs AF.:> x) ->
82+
let nextBlockSlot = blockRate benchmarkParams + (thSlotNo . testHeader $ x)
83+
in (xs AF.:> x) AF.:> TestBlock.mkNextBlock x nextBlockSlot dummyBody
84+
85+
dummyBody :: TestBody
86+
dummyBody = TestBody{tbForkNo = 0, tbIsValid = True}
87+
88+
-- | Given a chain fragment, construct a weight snapshot where there's a boosted block every 90 slots
89+
uniformWeightSnapshot :: AF.AnchoredFragment TestBlock -> PerasWeightSnapshot TestBlock
90+
uniformWeightSnapshot fragment =
91+
let pointsToBoost =
92+
map snd
93+
. getEveryN (boostedBlockRate benchmarkParams)
94+
. zip [0 ..]
95+
. map AF.blockPoint
96+
. AF.toOldestFirst
97+
$ fragment
98+
weights = repeat (boostWeight benchmarkParams)
99+
in PerasWeightSnapshot{getPerasWeightSnapshot = Map.fromList $ zip pointsToBoost weights}
100+
101+
getEveryN :: Natural -> [(Natural, a)] -> [(Natural, a)]
102+
getEveryN n = filter (\(i, _) -> (i `mod` n) == 0)

ouroboros-consensus/ouroboros-consensus.cabal

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,20 @@ benchmark ChainSync-client-bench
837837
unstable-consensus-testlib,
838838
with-utf8,
839839

840+
benchmark PerasCertDB-bench
841+
import: common-bench
842+
type: exitcode-stdio-1.0
843+
hs-source-dirs: bench/PerasCertDB-bench
844+
main-is: Main.hs
845+
other-modules:
846+
build-depends:
847+
base,
848+
containers,
849+
ouroboros-consensus,
850+
ouroboros-network-api,
851+
tasty-bench,
852+
unstable-consensus-testlib,
853+
840854
test-suite doctest
841855
import: common-test
842856
main-is: doctest.hs

0 commit comments

Comments
 (0)