Skip to content

Commit f1958b0

Browse files
authored
Builtin Value: efficient retrieval of total size and max inner map size (#7319)
1 parent 354c1ea commit f1958b0

File tree

4 files changed

+89
-22
lines changed

4 files changed

+89
-22
lines changed

plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/ExMemoryUsage.hs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ module PlutusCore.Evaluation.Machine.ExMemoryUsage
1212
, flattenCostRose
1313
, NumBytesCostedAsNumWords(..)
1414
, IntegerCostedLiterally(..)
15+
, ValueTotalSize(..)
16+
, ValueOuterOrMaxInner(..)
1517
) where
1618

1719
import PlutusCore.Crypto.BLS12_381.G1 as BLS12_381.G1
@@ -20,7 +22,8 @@ import PlutusCore.Crypto.BLS12_381.Pairing as BLS12_381.Pairing
2022
import PlutusCore.Data
2123
import PlutusCore.Evaluation.Machine.CostStream
2224
import PlutusCore.Evaluation.Machine.ExMemory
23-
import PlutusCore.Value
25+
import PlutusCore.Value (Value)
26+
import PlutusCore.Value qualified as Value
2427

2528
import Data.ByteString qualified as BS
2629
import Data.Functor
@@ -371,11 +374,22 @@ instance ExMemoryUsage Data where
371374
B b -> memoryUsage b
372375

373376
instance ExMemoryUsage Value where
374-
memoryUsage (Value v) = case Map.toList v of
375-
[] -> CostRose 0 []
376-
x : xs -> CostRose (f x) (flip CostRose [] . f <$> xs)
377+
memoryUsage = singletonRose . fromIntegral . Value.totalSize
378+
379+
-- | Measure the size of a `Value` by its `Value.totalSize`.
380+
newtype ValueTotalSize = ValueTotalSize { unValueTotalSize :: Value }
381+
382+
instance ExMemoryUsage ValueTotalSize where
383+
memoryUsage = singletonRose . fromIntegral . Value.totalSize . unValueTotalSize
384+
385+
-- | Measure the size of a `Value` by taking the max of
386+
-- (size of the outer map, size of the largest inner map).
387+
newtype ValueOuterOrMaxInner = ValueOuterOrMaxInner { unValueOuterOrMaxInner :: Value }
388+
389+
instance ExMemoryUsage ValueOuterOrMaxInner where
390+
memoryUsage (ValueOuterOrMaxInner v) = singletonRose (fromIntegral size)
377391
where
378-
f = fromIntegral . Map.size . snd
392+
size = Map.size (Value.unpack v) `max` Value.maxInnerSize v
379393

380394
{- Note [Costing constant-size types]
381395
The memory usage of each of the BLS12-381 types is constant, so we may be able

plutus-core/plutus-core/src/PlutusCore/Pretty/PrettyConst.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module PlutusCore.Pretty.PrettyConst where
1515

1616
import PlutusCore.Data
1717
import PlutusCore.Pretty.Readable
18-
import PlutusCore.Value (Value (..))
18+
import PlutusCore.Value (Value)
1919
import PlutusCore.Value qualified as Value
2020

2121
import Control.Lens hiding (List)
@@ -29,7 +29,7 @@ import Data.Typeable
2929
import Data.Vector.Strict (Vector)
3030
import Data.Word (Word8)
3131
import Numeric (showHex)
32-
import Prettyprinter as Prettyprinter
32+
import Prettyprinter
3333
import Prettyprinter.Internal (Doc (Text))
3434
import Text.PrettyBy
3535
import Text.PrettyBy.Internal (DefaultPrettyBy (..))

plutus-core/plutus-core/src/PlutusCore/Value.hs

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
{-# LANGUAGE DeriveAnyClass #-}
12
{-# LANGUAGE FlexibleInstances #-}
3+
{-# LANGUAGE ViewPatterns #-}
24

35
module PlutusCore.Value (
4-
Value (..),
6+
Value, -- Do not expose data constructor
7+
NestedMap,
8+
unpack,
9+
pack,
510
empty,
611
fromList,
712
toList,
813
totalSize,
14+
maxInnerSize,
915
) where
1016

1117
import Codec.Serialise (Serialise)
@@ -14,33 +20,77 @@ import Data.Bifunctor
1420
import Data.ByteString (ByteString)
1521
import Data.ByteString.Base64 qualified as Base64
1622
import Data.Hashable (Hashable)
23+
import Data.IntMap.Strict (IntMap)
24+
import Data.IntMap.Strict qualified as IntMap
1725
import Data.Map.Strict (Map)
1826
import Data.Map.Strict qualified as Map
1927
import Data.Text.Encoding qualified as Text
28+
import GHC.Generics
2029

2130
import PlutusPrelude (Pretty (..))
2231

23-
newtype Value = Value {unValue :: Map ByteString (Map ByteString Integer)}
24-
deriving newtype (Eq, Show, Hashable, Serialise, NFData)
32+
type NestedMap = Map ByteString (Map ByteString Integer)
33+
34+
-- | The underlying type of the UPLC built-in type @Value@.
35+
data Value
36+
= Value
37+
!NestedMap
38+
{- ^ Map from (currency symbol, token name) to amount.
39+
40+
Invariants: no empty inner map, and no zero amount.
41+
-}
42+
!(IntMap Int)
43+
{- ^ Map from size to the number of inner maps that have that size,
44+
useful for efficient retrieval of the size of the largest inner map.
45+
46+
Invariant: all values are positive.
47+
-}
48+
{-# UNPACK #-} !Int
49+
-- ^ Total size, i.e., sum total of inner map sizes
50+
deriving stock (Eq, Show, Generic)
51+
deriving anyclass (Hashable, Serialise, NFData)
52+
53+
{-| Unpack a `Value` into a map from (currency symbol, token name) to amount.
54+
55+
The map is guaranteed to not contain empty inner map or zero amount.
56+
-}
57+
unpack :: Value -> NestedMap
58+
unpack (Value v _ _) = v
59+
60+
{-| Pack a map from (currency symbol, token name) to amount into a `Value`.
61+
62+
The map will be filtered so that it does not contain empty inner map or zero amount.
63+
-}
64+
pack :: NestedMap -> Value
65+
pack (normalize -> v) = Value v sizes size
66+
where
67+
sizes = Map.foldr' (IntMap.alter (maybe (Just 1) (Just . (+ 1))) . Map.size) mempty v
68+
size = Map.foldr' ((+) . Map.size) 0 v
69+
70+
{-| Total size, i.e., the number of distinct `(currency symbol, token name)` pairs
71+
contained in the `Value`.
72+
-}
73+
totalSize :: Value -> Int
74+
totalSize (Value _ _ size) = size
75+
76+
-- | Size of the largest inner map.
77+
maxInnerSize :: Value -> Int
78+
maxInnerSize (Value _ sizes _) = maybe 0 fst (IntMap.lookupMax sizes)
2579

2680
empty :: Value
27-
empty = Value mempty
81+
empty = Value mempty mempty 0
2882

2983
toList :: Value -> [(ByteString, [(ByteString, Integer)])]
30-
toList = Map.toList . Map.map Map.toList . unValue
84+
toList = Map.toList . Map.map Map.toList . unpack
3185

3286
fromList :: [(ByteString, [(ByteString, Integer)])] -> Value
3387
fromList =
34-
normalize
35-
. Value
88+
pack
3689
. Map.fromListWith (Map.unionWith (+))
3790
. fmap (second (Map.fromListWith (+)))
3891

39-
normalize :: Value -> Value
40-
normalize = Value . Map.filter (not . Map.null) . Map.map (Map.filter (/= 0)) . unValue
41-
42-
totalSize :: Value -> Int
43-
totalSize = Map.foldl' (\n -> (n +) . Map.size) 0 . unValue
92+
normalize :: NestedMap -> NestedMap
93+
normalize = Map.filter (not . Map.null) . Map.map (Map.filter (/= 0))
4494

4595
instance Pretty Value where
4696
pretty = pretty . fmap (bimap toText (fmap (first toText))) . toList

plutus-core/testlib/PlutusCore/Generators/QuickCheck/Builtin.hs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import PlutusCore.Crypto.BLS12_381.Pairing qualified as BLS12_381.Pairing
2020
import PlutusCore.Data
2121
import PlutusCore.Generators.QuickCheck.GenerateKinds ()
2222
import PlutusCore.Generators.QuickCheck.Split (multiSplit0, multiSplit1, multiSplit1In)
23-
import PlutusCore.Value
23+
import PlutusCore.Value (Value)
24+
import PlutusCore.Value qualified as Value
2425

2526
import Data.ByteString (ByteString, empty)
2627
import Data.Int
2728
import Data.Kind qualified as GHC
28-
import Data.Map (Map)
2929
import Data.Maybe
3030
import Data.Proxy
3131
import Data.Text (Text)
@@ -244,7 +244,9 @@ instance Arbitrary Data where
244244
arbitrary = arbitraryBuiltin
245245
shrink = shrinkBuiltin
246246

247-
deriving via Map ByteString (Map ByteString Integer) instance Arbitrary Value
247+
instance Arbitrary Value where
248+
arbitrary = Value.pack <$> arbitrary
249+
shrink = fmap Value.pack . shrink . Value.unpack
248250

249251
instance ArbitraryBuiltin Value
250252

@@ -422,6 +424,7 @@ instance KnownKind k => Arbitrary (MaybeSomeTypeOf k) where
422424
, JustSomeType DefaultUniBLS12_381_G1_Element
423425
, JustSomeType DefaultUniBLS12_381_G2_Element
424426
, JustSomeType DefaultUniBLS12_381_MlResult
427+
, JustSomeType DefaultUniValue
425428
]
426429
SingType `SingKindArrow` SingType ->
427430
[ genDefaultUniApply | size > 10 ]

0 commit comments

Comments
 (0)