Skip to content

Commit 328b50a

Browse files
committed
fixup! O.C.Peras.Weight: add takeVolatileSuffix
1 parent ddb4119 commit 328b50a

File tree

3 files changed

+76
-24
lines changed

3 files changed

+76
-24
lines changed

ouroboros-consensus/bench/PerasCertDB-bench/Main.hs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
{-# LANGUAGE DataKinds #-}
12
{-# LANGUAGE ImportQualifiedPost #-}
23
{-# LANGUAGE LambdaCase #-}
4+
{-# LANGUAGE TypeApplications #-}
35

46
-- | This module contains benchmarks for Peras chain weight calculation as
5-
-- implemented by the by the
6-
-- 'Ouroboros.Consensus.Peras.Weight.weightBoostOfFragment' function.
7+
-- implemented in the 'Ouroboros.Consensus.Peras.Weight' module.
78
--
89
-- We benchmark the calculation on a static sequence of chain fragments of
910
-- increasing length, ranging from 0 to 'fragmentMaxLength', with a step size
@@ -12,13 +13,16 @@
1213
-- with weight 'boostWeight'. All parameters are set in 'benchmarkParams'.
1314
module Main (main) where
1415

16+
import Cardano.Ledger.BaseTypes.NonZero (knownNonZeroBounded)
1517
import Data.List (iterate')
1618
import Data.Word (Word64)
1719
import Numeric.Natural (Natural)
1820
import Ouroboros.Consensus.Block (PerasWeight (PerasWeight), SlotNo (..))
21+
import Ouroboros.Consensus.Config.SecurityParam
1922
import Ouroboros.Consensus.Peras.Weight
2023
( PerasWeightSnapshot
2124
, mkPerasWeightSnapshot
25+
, takeVolatileSuffix
2226
, weightBoostOfFragment
2327
)
2428
import Ouroboros.Network.AnchoredFragment qualified as AF
@@ -65,7 +69,11 @@ benchmarkParams =
6569

6670
main :: IO ()
6771
main =
68-
Test.Tasty.Bench.defaultMain $ map benchWeightBoostOfFragment inputs
72+
Test.Tasty.Bench.defaultMain $
73+
concat
74+
[ map benchWeightBoostOfFragment inputs
75+
, map benchTakeVolatileSuffix inputs
76+
]
6977
where
7078
-- NOTE: we do not use the 'env' combinator to set up the test data since
7179
-- it requires 'NFData' for 'AF.AnchoredFragment'. While the necessary
@@ -84,6 +92,14 @@ benchWeightBoostOfFragment (i, (weightSnapshot, fragment)) =
8492
bench ("weightBoostOfFragment of length " <> show i) $
8593
whnf (weightBoostOfFragment weightSnapshot) fragment
8694

95+
benchTakeVolatileSuffix ::
96+
(Natural, (PerasWeightSnapshot TestBlock, AF.AnchoredFragment TestBlock)) -> Benchmark
97+
benchTakeVolatileSuffix (i, (weightSnapshot, fragment)) =
98+
bench ("takeVolatileSuffix of length " <> show i) $
99+
whnf (takeVolatileSuffix weightSnapshot k) fragment
100+
where
101+
k = SecurityParam $ knownNonZeroBounded @2160
102+
87103
-- | An infinite list of chain fragments
88104
fragments :: [AF.AnchoredFragment TestBlock]
89105
fragments = iterate' addSuccessorBlock genesisFragment

ouroboros-consensus/ouroboros-consensus.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,7 @@ benchmark PerasCertDB-bench
906906
other-modules:
907907
build-depends:
908908
base,
909+
cardano-ledger-core,
909910
ouroboros-consensus,
910911
ouroboros-network-api,
911912
tasty-bench,

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Peras/Weight.hs

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@ module Ouroboros.Consensus.Peras.Weight
3434
import Data.Foldable as Foldable (foldl')
3535
import Data.Map.Strict (Map)
3636
import qualified Data.Map.Strict as Map
37-
import Data.Word (Word64)
37+
import Data.Maybe (fromJust)
3838
import GHC.Generics (Generic)
3939
import NoThunks.Class
4040
import Ouroboros.Consensus.Block
4141
import Ouroboros.Consensus.Config.SecurityParam
4242
import Ouroboros.Network.AnchoredFragment (AnchoredFragment)
4343
import qualified Ouroboros.Network.AnchoredFragment as AF
44+
import Ouroboros.Network.AnchoredSeq (AnchoredSeq)
45+
import qualified Ouroboros.Network.AnchoredSeq as AS
4446

4547
-- | Data structure for tracking the weight of blocks due to Peras boosts.
4648
newtype PerasWeightSnapshot blk = PerasWeightSnapshot
@@ -367,34 +369,67 @@ takeVolatileSuffix ::
367369
SecurityParam ->
368370
AnchoredFragment h ->
369371
AnchoredFragment h
370-
takeVolatileSuffix snap secParam frag
372+
takeVolatileSuffix snap secParam
371373
| Map.null $ getPerasWeightSnapshot snap =
372374
-- Optimize the case where Peras is disabled.
373-
AF.anchorNewest (unPerasWeight k) frag
374-
| hasAtMostWeightK frag = frag
375-
| otherwise = go 0 lenFrag (AF.Empty $ AF.headAnchor frag)
375+
AF.anchorNewest (unPerasWeight k)
376+
| otherwise =
377+
takeLongestSuffix (totalWeightOfFragment snap) (<= k)
376378
where
377379
k :: PerasWeight
378380
k = maxRollbackWeight secParam
379381

380-
hasAtMostWeightK :: AnchoredFragment h -> Bool
381-
hasAtMostWeightK f = totalWeightOfFragment snap f <= k
382-
383-
lenFrag = fromIntegral $ AF.length frag
384-
385-
-- Binary search for the longest suffix of @frag@ which 'hasAtMostWeightK'.
382+
-- | Take the longest suffix of an 'AnchoredSeq' @as@ satisfying the given
383+
-- predicate @p@ on the monoidal summary given by @f@.
384+
--
385+
-- TODO: upstream this function
386+
--
387+
-- === PRECONDITIONS:
388+
--
389+
-- For @as0, as1@ such that @AS.join as0 as1 = Just as2@, we must have the
390+
-- following homomorphism property:
391+
--
392+
-- > f as0 <> f as1 ≡ f as2
393+
--
394+
-- For empty @ase@, we must have @f ase ≡ mempty@.
395+
--
396+
-- The predicate must be monotonic, ie when @suf0@ is a suffix of @as@ and
397+
-- @suf1@ is a suffix of @suf0@, then @p (f suf0)@ must imply @p (f suf1)@.
398+
-- Furthermore, we must have @p mempty@.
399+
takeLongestSuffix ::
400+
forall s v a b.
401+
(Monoid s, AS.Anchorable v a b) =>
402+
-- | @f@: Compute a monoidal summary of a fragment.
403+
(AnchoredSeq v a b -> s) ->
404+
-- | @p@: Predicate on the summary of a fragment.
405+
(s -> Bool) ->
406+
-- | Input sequence @as@.
407+
AnchoredSeq v a b ->
408+
-- | A suffix of the input sequence.
409+
AnchoredSeq v a b
410+
takeLongestSuffix f p as =
411+
go (AS.Empty $ AS.headAnchor as) mempty as
412+
where
386413
go ::
387-
Word64 -> -- lb. The length lb suffix satisfies 'hasAtMostWeightK'.
388-
Word64 -> -- ub. The length ub suffix does not satisfy 'hasAtMostWeightK'.
389-
AnchoredFragment h -> -- The length lb suffix.
390-
AnchoredFragment h
391-
go lb ub lbFrag
392-
| lb + 1 == ub = lbFrag
393-
| hasAtMostWeightK midFrag = go mid ub midFrag
394-
| otherwise = go lb mid lbFrag
414+
-- @suf@: the longest suffix of @as@ for which we currently know that @p (f
415+
-- suf)@.
416+
AnchoredSeq v a b ->
417+
-- Equal to @f suf@.
418+
s ->
419+
-- @pre@: longest infix of @as@ ending just before @suf@ such that we don't
420+
-- know whether @p (f (AS.join pre suf))@.
421+
AnchoredSeq v a b ->
422+
-- Longest suffix of @as@ satisfying @p . f@.
423+
AnchoredSeq v a b
424+
go suf sufS pre
425+
| AS.null pre = suf
426+
| p suf'S = go suf' suf'S pre0
427+
| AS.null pre0 = suf
428+
| otherwise = go suf sufS pre1
395429
where
396-
mid = (lb + ub) `div` 2
397-
midFrag = AF.anchorNewest mid frag
430+
(pre0, pre1) = AS.splitAt (AS.length pre `div` 2) pre
431+
suf' = fromJust (AS.join (\_ _ -> True) pre1 suf)
432+
suf'S = f pre1 <> sufS
398433

399434
-- $setup
400435
-- >>> import Cardano.Ledger.BaseTypes

0 commit comments

Comments
 (0)