Skip to content

Commit 9000633

Browse files
authored
Revert "Keep track of currency/token lengths in builtin Value (#7349)" (#7370)
1 parent ef1fa2e commit 9000633

File tree

3 files changed

+45
-112
lines changed

3 files changed

+45
-112
lines changed

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

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import PlutusCore.Evaluation.Machine.ExMemory
2525
import PlutusCore.Value (Value)
2626
import PlutusCore.Value qualified as Value
2727

28-
import Control.Arrow ((&&&))
2928
import Data.ByteString qualified as BS
3029
import Data.Functor
3130
import Data.Map.Strict qualified as Map
@@ -375,37 +374,22 @@ instance ExMemoryUsage Data where
375374
B b -> memoryUsage b
376375

377376
instance ExMemoryUsage Value where
378-
memoryUsage = memoryUsage . ValueTotalSize
377+
memoryUsage = singletonRose . fromIntegral . Value.totalSize
379378

380-
381-
{-| Measure the size of a `Value` by its `Value.totalSize`, multiplied by
382-
the length of the longest currency symbol or token name plus 1.
383-
-}
384-
newtype ValueTotalSize = ValueTotalSize {unValueTotalSize :: Value}
379+
-- | Measure the size of a `Value` by its `Value.totalSize`.
380+
newtype ValueTotalSize = ValueTotalSize { unValueTotalSize :: Value }
385381

386382
instance ExMemoryUsage ValueTotalSize where
387-
memoryUsage =
388-
singletonRose
389-
. fromInteger
390-
. uncurry (*)
391-
. (toInteger . Value.totalSize &&& toInteger . (+1) . Value.maxKeyLength)
392-
. unValueTotalSize
393-
394-
{-| Measure the size of a `Value` by taking the max of
395-
(size of the outer map, size of the largest inner map), multiplied by
396-
the length of the longest currency symbol or token name plus 1.
397-
-}
398-
newtype ValueOuterOrMaxInner = ValueOuterOrMaxInner {unValueOuterOrMaxInner :: Value}
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 }
399388

400389
instance ExMemoryUsage ValueOuterOrMaxInner where
401-
memoryUsage =
402-
singletonRose
403-
. fromInteger
404-
. uncurry (*)
405-
. ( toInteger . (+1) . Value.maxKeyLength
406-
&&& (toInteger . uncurry max . (Map.size . Value.unpack &&& Value.maxInnerSize))
407-
)
408-
. unValueOuterOrMaxInner
390+
memoryUsage (ValueOuterOrMaxInner v) = singletonRose (fromIntegral size)
391+
where
392+
size = Map.size (Value.unpack v) `max` Value.maxInnerSize v
409393

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

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

Lines changed: 30 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ module PlutusCore.Value (
1414
toFlatList,
1515
totalSize,
1616
maxInnerSize,
17-
maxKeyLength,
1817
insertCoin,
1918
deleteCoin,
2019
lookupCoin,
@@ -29,7 +28,6 @@ import Control.DeepSeq (NFData)
2928
import Data.Bifunctor
3029
import Data.Bitraversable
3130
import Data.ByteString (ByteString)
32-
import Data.ByteString qualified as B
3331
import Data.ByteString.Base64 qualified as Base64
3432
import Data.Functor
3533
import Data.Hashable (Hashable)
@@ -62,10 +60,6 @@ data Value
6260
6361
Invariant: all values are positive.
6462
-}
65-
!(IntMap Int)
66-
{- ^ Map from length to the number of `ByteString`s of that length
67-
(across both outer and inner maps).
68-
-}
6963
{-# UNPACK #-} !Int
7064
{-^ Total size, i.e., sum total of inner map sizes. This avoids recomputing
7165
the total size during the costing of operations like `unionValue`.
@@ -78,7 +72,7 @@ data Value
7872
The map is guaranteed to not contain empty inner map or zero amount.
7973
-}
8074
unpack :: Value -> NestedMap
81-
unpack (Value v _ _ _) = v
75+
unpack (Value v _ _) = v
8276
{-# INLINE unpack #-}
8377

8478
{-| Pack a map from (currency symbol, token name) to amount into a `Value`.
@@ -91,19 +85,11 @@ pack = pack' . normalize
9185

9286
-- | Like `pack` but does not normalize.
9387
pack' :: NestedMap -> Value
94-
pack' v = Value v sizes lens size
88+
pack' v = Value v sizes size
9589
where
96-
(sizes, lens, size) = Map.foldrWithKey' alg (mempty, mempty, 0) v
97-
alg currency inner (ss, ls, s) =
98-
( incCount (Map.size inner) ss
99-
, IntMap.unionWith
100-
(+)
101-
(incCount (B.length currency) ls)
102-
( Map.foldrWithKey'
103-
(\token _ -> incCount (B.length token))
104-
mempty
105-
inner
106-
)
90+
(sizes, size) = Map.foldl' alg (mempty, 0) v
91+
alg (ss, s) inner =
92+
( IntMap.alter (maybe (Just 1) (Just . (+ 1))) (Map.size inner) ss
10793
, s + Map.size inner
10894
)
10995
{-# INLINEABLE pack' #-}
@@ -112,20 +98,16 @@ pack' v = Value v sizes lens size
11298
contained in the `Value`.
11399
-}
114100
totalSize :: Value -> Int
115-
totalSize (Value _ _ _ size) = size
101+
totalSize (Value _ _ size) = size
116102
{-# INLINE totalSize #-}
117103

118104
-- | Size of the largest inner map.
119105
maxInnerSize :: Value -> Int
120-
maxInnerSize (Value _ sizes _ _) = maybe 0 fst (IntMap.lookupMax sizes)
106+
maxInnerSize (Value _ sizes _) = maybe 0 fst (IntMap.lookupMax sizes)
121107
{-# INLINE maxInnerSize #-}
122108

123-
-- | Maximum `ByteString` length, across both outer and inner maps.
124-
maxKeyLength :: Value -> Int
125-
maxKeyLength (Value _ _ lens _) = maybe 0 fst (IntMap.lookupMax lens)
126-
127109
empty :: Value
128-
empty = Value mempty mempty mempty 0
110+
empty = Value mempty mempty 0
129111
{-# INLINE empty #-}
130112

131113
toList :: Value -> [(ByteString, [(ByteString, Integer)])]
@@ -155,60 +137,47 @@ instance Pretty Value where
155137
the size of the largest inner map.
156138
-}
157139
insertCoin :: ByteString -> ByteString -> Integer -> Value -> Value
158-
insertCoin currency token amt v@(Value outer sizes lens size)
140+
insertCoin currency token amt v@(Value outer sizes size)
159141
| amt == 0 = deleteCoin currency token v
160142
| otherwise =
161-
let (r, outer') = Map.alterF f currency outer
162-
(sizes', lens', size') = case r of
163-
Just (old, currencyInserted) ->
164-
( updateSizes old (old + 1) sizes
165-
, (if currencyInserted then incCount (B.length currency) else id)
166-
(incCount (B.length token) lens)
167-
, size + 1
168-
)
169-
Nothing -> (sizes, lens, size)
170-
in Value outer' sizes' lens' size'
143+
let (mold, outer') = Map.alterF f currency outer
144+
(sizes', size') = case mold of
145+
Just old -> (updateSizes old (old + 1) sizes, size + 1)
146+
Nothing -> (sizes, size)
147+
in Value outer' sizes' size'
171148
where
172149
f
173150
:: Maybe (Map ByteString Integer)
174-
-> ( -- Just (old size of inner map, whether it is a new currency)
175-
-- if the total size grows by 1, otherwise Nothing
176-
Maybe (Int, Bool)
151+
-> ( -- Just (old size of inner map) if the total size grows by 1, otherwise Nothing
152+
Maybe Int
177153
, Maybe (Map ByteString Integer)
178154
)
179155
f = \case
180-
Nothing -> (Just (0, True), Just (Map.singleton token amt))
156+
Nothing -> (Just 0, Just (Map.singleton token amt))
181157
Just inner ->
182158
let (isJust -> exists, inner') = Map.insertLookupWithKey (\_ _ _ -> amt) token amt inner
183-
in (if exists then Nothing else Just (Map.size inner, False), Just inner')
159+
in (if exists then Nothing else Just (Map.size inner), Just inner')
184160
{-# INLINEABLE insertCoin #-}
185161

186162
-- | \(O(\log \max(m, k))\)
187163
deleteCoin :: ByteString -> ByteString -> Value -> Value
188-
deleteCoin currency token (Value outer sizes lens size) = Value outer' sizes' lens' size'
164+
deleteCoin currency token (Value outer sizes size) = Value outer' sizes' size'
189165
where
190-
(r, outer') = Map.alterF f currency outer
191-
(sizes', lens', size') = case r of
192-
Just (old, currencyDeleted) ->
193-
( updateSizes old (old - 1) sizes
194-
, (if currencyDeleted then decLen (B.length currency) else id) (decLen (B.length token) lens)
195-
, size - 1
196-
)
197-
Nothing -> (sizes, lens, size)
166+
(mold, outer') = Map.alterF f currency outer
167+
(sizes', size') = case mold of
168+
Just old -> (updateSizes old (old - 1) sizes, size - 1)
169+
Nothing -> (sizes, size)
198170
f
199171
:: Maybe (Map ByteString Integer)
200-
-> ( -- Just (old size of inner map, whether a currency is deleted)
201-
-- if the total size shrinks by 1, otherwise Nothing
202-
Maybe (Int, Bool)
172+
-> ( -- Just (old size of inner map) if the total size shrinks by 1, otherwise Nothing
173+
Maybe Int
203174
, Maybe (Map ByteString Integer)
204175
)
205176
f = \case
206177
Nothing -> (Nothing, Nothing)
207178
Just inner ->
208179
let (amt, inner') = Map.updateLookupWithKey (\_ _ -> Nothing) token inner
209-
in ( amt $> (Map.size inner, Map.null inner')
210-
, if Map.null inner' then Nothing else Just inner'
211-
)
180+
in (amt $> Map.size inner, if Map.null inner' then Nothing else Just inner')
212181

213182
-- | \(O(\log \max(m, k))\)
214183
lookupCoin :: ByteString -> ByteString -> Value -> Integer
@@ -238,8 +207,9 @@ valueContains v = Map.foldrWithKey' go True . unpack
238207
)
239208

240209
{-| The precise complexity is complicated, but an upper bound
241-
is \(O(n_{1} \log n_{2}) + O(n_{2})\), where \(n_{1}\) is the total size of the smaller
242-
value, and \(n_{2}\) is the total size of the bigger value.
210+
is \(O(n_{1} \log n_{2}) + O(m)\), where \(n_{1}\) is the total size of the smaller
211+
value, \(n_{2}\) is the total size of the bigger value, and \(m\) is the
212+
combined size of the outer maps.
243213
-}
244214
unionValue :: Value -> Value -> Value
245215
unionValue (unpack -> vA) (unpack -> vB) =
@@ -308,18 +278,9 @@ updateSizes old new = dec . inc
308278
inc =
309279
if new == 0
310280
then id
311-
else incCount new
281+
else IntMap.alter (maybe (Just 1) (Just . (+ 1))) new
312282
dec =
313283
if old == 0
314284
then id
315285
else IntMap.update (\n -> if n <= 1 then Nothing else Just (n - 1)) old
316286
{-# INLINEABLE updateSizes #-}
317-
318-
-- | Increment the count at the given key.
319-
incCount :: Int -> IntMap Int -> IntMap Int
320-
incCount = IntMap.alter (maybe (Just 1) (Just . (+ 1)))
321-
{-# INLINEABLE incCount #-}
322-
323-
decLen :: Int -> IntMap Int -> IntMap Int
324-
decLen = IntMap.update (\n -> if n <= 1 then Nothing else Just (n - 1))
325-
{-# INLINEABLE decLen #-}

plutus-core/plutus-core/test/Value/Spec.hs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
module Value.Spec (tests) where
66

7-
import Data.ByteString qualified as B
87
import Data.Foldable qualified as F
98
import Data.Map.Strict qualified as Map
109
import Data.Maybe
@@ -21,8 +20,7 @@ prop_packUnpackRoundtrip v = v === V.pack (V.unpack v)
2120

2221
-- | Verifies that @pack@ correctly updates the sizes
2322
prop_packBookkeeping :: V.NestedMap -> Property
24-
prop_packBookkeeping = checkBookKeeping . V.pack
25-
23+
prop_packBookkeeping = checkSizes . V.pack
2624

2725
{-| Verifies that @pack@ preserves @Value@ invariants, i.e.,
2826
no empty inner map or zero amount.
@@ -36,7 +34,7 @@ prop_insertCoinBookkeeping v (ValueAmount amt) =
3634
forAll (genShortHex (V.totalSize v)) $ \currency ->
3735
forAll (genShortHex (V.totalSize v)) $ \token ->
3836
let v' = V.insertCoin currency token amt v
39-
in checkBookKeeping v'
37+
in checkSizes v'
4038

4139
-- | Verifies that @insertCoin@ preserves @Value@ invariants
4240
prop_insertCoinPreservesInvariants :: Value -> ValueAmount -> Property
@@ -91,25 +89,15 @@ prop_containsAfterDeletion v =
9189
fl = V.toFlatList v
9290
vs = scanr (\(c, t, _) -> V.deleteCoin c t) v fl
9391

94-
checkBookKeeping :: Value -> Property
95-
checkBookKeeping v =
92+
checkSizes :: Value -> Property
93+
checkSizes v =
9694
(expectedMaxInnerSize === actualMaxInnerSize)
9795
.&&. (expectedSize === actualSize)
98-
.&&. (expectedMaxKeyLength === actualMaxKeyLength)
9996
where
10097
expectedMaxInnerSize = fromMaybe 0 . maximumMay $ Map.map Map.size (V.unpack v)
10198
actualMaxInnerSize = V.maxInnerSize v
10299
expectedSize = sum $ Map.map Map.size (V.unpack v)
103100
actualSize = V.totalSize v
104-
expectedMaxKeyLength =
105-
let maxOuter =
106-
fromMaybe 0 . maximumMay $
107-
[B.length k | k <- Map.keys (V.unpack v)]
108-
maxInner =
109-
fromMaybe 0 . maximumMay $
110-
[B.length k | inner <- Map.elems (V.unpack v), k <- Map.keys inner]
111-
in max maxOuter maxInner
112-
actualMaxKeyLength = V.maxKeyLength v
113101

114102
checkInvariants :: Value -> Property
115103
checkInvariants (V.unpack -> v) =

0 commit comments

Comments
 (0)