Skip to content

Commit 86d645a

Browse files
committed
feat: regenerate cost models after rebase to account for validation overhead
Regenerate cost model parameters based on fresh benchmark runs after rebasing on master. This accounts for the negative amount validation added to valueContains in commit 531f1b8. Updated cost models: - lookupCoin: linear_in_z (intercept: 203599, slope: 7256) - valueContains: linear_in_y (intercept: 1000, slope: 130720) - valueData: constant_cost (156990) - unValueData: linear_in_x (intercept: 1000, slope: 36194) The benchmark data includes 350 measurement points across varying input sizes. All three cost model variants (A, B, C) have been updated consistently with identical parameters.
1 parent 6afef50 commit 86d645a

File tree

5 files changed

+425
-536
lines changed

5 files changed

+425
-536
lines changed

plutus-core/cost-model/budgeting-bench/Benchmarks/Values.hs

Lines changed: 60 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE BlockArguments #-}
22
{-# LANGUAGE ImportQualifiedPost #-}
33
{-# LANGUAGE NumericUnderscores #-}
4+
{-# LANGUAGE TupleSections #-}
45

56
module Benchmarks.Values (makeBenchmarks) where
67

@@ -10,6 +11,7 @@ import Common
1011
import Control.Monad (replicateM)
1112
import Criterion.Main (Benchmark)
1213
import Data.ByteString (ByteString)
14+
import Data.Int (Int64)
1315
import PlutusCore (DefaultFun (LookupCoin, UnValueData, ValueContains, ValueData))
1416
import PlutusCore.Evaluation.Machine.ExMemoryUsage (LogValueOuterOrMaxInner (..),
1517
ValueTotalSize (..))
@@ -40,7 +42,7 @@ lookupCoinBenchmark gen =
4042
(lookupCoinArgs gen) -- the argument combos to generate benchmarks for
4143

4244
lookupCoinArgs :: StdGen -> [(ByteString, ByteString, Value)]
43-
lookupCoinArgs gen = runStateGen_ gen $ \(g :: g) -> do
45+
lookupCoinArgs gen = runStateGen_ gen \(g :: g) -> do
4446
-- Add search keys to common test values
4547
let testValues = generateTestValues gen
4648
commonWithKeys <- mapM (withSearchKeys g . pure) testValues
@@ -49,17 +51,12 @@ lookupCoinArgs gen = runStateGen_ gen $ \(g :: g) -> do
4951
let valueSizes = [(100, 10), (500, 20), (1_000, 50), (2_000, 100)]
5052
additionalTests <-
5153
sequence $
52-
concat
53-
[ -- Value size tests (number of policies × tokens per policy)
54-
[ generateLookupTest g numPolicies tokensPerPolicy
55-
| (numPolicies, tokensPerPolicy) <- valueSizes
56-
]
57-
, -- Budget-constrained tests (at 30KB limit)
58-
[generateBudgetTest g 30_000]
59-
, -- Additional random tests for parameter spread
60-
replicate 50 (generateRandomLookupTest g)
61-
]
62-
54+
-- Value size tests (number of policies × tokens per policy)
55+
[ withSearchKeys g (generateConstrainedValue numPolicies tokensPerPolicy g)
56+
| (numPolicies, tokensPerPolicy) <- valueSizes
57+
]
58+
-- Additional random tests for parameter spread
59+
<> replicate 100 (withSearchKeys g (generateValue g))
6360
pure $ commonWithKeys ++ additionalTests
6461

6562
-- | Add random search keys to a Value (keys may or may not exist in the Value)
@@ -70,32 +67,6 @@ withSearchKeys g genValue = do
7067
key2 <- generateKeyBS g
7168
pure (key1, key2, value)
7269

73-
-- | Generate lookup test with specified parameters
74-
generateLookupTest
75-
:: (StatefulGen g m)
76-
=> g
77-
-> Int -- Number of policies
78-
-> Int -- Tokens per policy
79-
-> m (ByteString, ByteString, Value)
80-
generateLookupTest g numPolicies tokensPerPolicy =
81-
withSearchKeys g (generateConstrainedValue numPolicies tokensPerPolicy g)
82-
83-
-- | Generate budget-constrained test
84-
generateBudgetTest
85-
:: (StatefulGen g m)
86-
=> g
87-
-> Int -- Total budget
88-
-> m (ByteString, ByteString, Value)
89-
generateBudgetTest g budget =
90-
withSearchKeys g (generateValueWithBudget budget g)
91-
92-
-- | Generate random lookup test with varied parameters for better spread
93-
generateRandomLookupTest :: (StatefulGen g m) => g -> m (ByteString, ByteString, Value)
94-
generateRandomLookupTest g = do
95-
numPolicies <- uniformRM (1, 2_000) g
96-
tokensPerPolicy <- uniformRM (1, 1_000) g
97-
withSearchKeys g (generateConstrainedValue numPolicies tokensPerPolicy g)
98-
9970
----------------------------------------------------------------------------------------------------
10071
-- ValueContains -----------------------------------------------------------------------------------
10172

@@ -109,89 +80,23 @@ valueContainsBenchmark gen =
10980
(valueContainsArgs gen) -- the argument combos to generate benchmarks for
11081

11182
valueContainsArgs :: StdGen -> [(Value, Value)]
112-
valueContainsArgs gen = runStateGen_ gen \g -> do
113-
let baseValueSizes = [1, 10, 100, 1_000]
114-
sequence $
115-
concat
116-
[ -- Value size tests with varying sizes
117-
[ generateContainsTest g containerSize containedSize
118-
| containerSize <- baseValueSizes
119-
, containedSize <- baseValueSizes
120-
, containedSize <= containerSize
121-
]
122-
, -- Budget-constrained tests
123-
[generateContainsBudgetTest g 30_000]
124-
, -- Edge cases
125-
[ generateEmptyContainedTest g containerSize
126-
| containerSize <- [0, 10, 100, 1_000]
127-
]
128-
, -- Random tests for parameter spread (100 combinations)
129-
replicate 100 (generateRandomContainsTest g)
130-
]
131-
132-
-- | Generate valueContains test with specified parameters
133-
generateContainsTest
134-
:: (StatefulGen g m)
135-
=> g
136-
-> Int -- Container value size (number of policies)
137-
-> Int -- Contained value size (number of policies)
138-
-> m (Value, Value)
139-
generateContainsTest g containerSize containedSize = do
140-
-- Generate container value
141-
container <- generateConstrainedValue containerSize 10 g
142-
143-
-- Generate contained as subset of container (for true contains relationship)
144-
let containerList = Value.toList container
145-
containedEntries = take containedSize containerList
146-
83+
valueContainsArgs gen = runStateGen_ gen \g -> replicateM 100 do
84+
-- Generate a random container value
85+
container <- generateValue g
86+
-- Select a random subset of entries from the container to ensure contained ⊆ container
87+
containedSize <- uniformRM (0, Value.totalSize container) g
88+
-- Take the first containedSize entries to ensure contained ⊆ container
89+
let selectedEntries = take containedSize (Value.toFlatList container)
90+
91+
-- Group selected entries back by policy
14792
let contained =
148-
Value.fromList $
149-
[ (policyId, take (containedSize `div` max 1 (length containerList)) tokens)
150-
| (policyId, tokens) <- containedEntries
93+
Value.fromList
94+
[ (policyId, [(tokenName, quantity)])
95+
| (policyId, tokenName, quantity) <- selectedEntries
15196
]
15297

15398
pure (container, contained)
15499

155-
-- | Generate budget-constrained contains test
156-
generateContainsBudgetTest
157-
:: (StatefulGen g m)
158-
=> g
159-
-> Int -- Total budget
160-
-> m (Value, Value)
161-
generateContainsBudgetTest g budget = do
162-
container <- generateValueWithBudget budget g
163-
-- Generate smaller contained value (subset)
164-
let containerList = Value.toList container
165-
containedEntries = take (length containerList `div` 2) containerList
166-
pure (container, Value.fromList containedEntries)
167-
168-
-- | Generate test with empty contained value
169-
generateEmptyContainedTest
170-
:: (StatefulGen g m)
171-
=> g
172-
-> Int -- Container size (number of policies)
173-
-> m (Value, Value)
174-
generateEmptyContainedTest g containerSize = do
175-
container <- generateConstrainedValue containerSize 10 g
176-
pure (container, Value.empty)
177-
178-
-- | Generate random valueContains test with varied parameters for better spread
179-
generateRandomContainsTest :: (StatefulGen g m) => g -> m (Value, Value)
180-
generateRandomContainsTest g = do
181-
-- Generate random parameters with good spread
182-
containerEntries <- uniformRM (1, 5_000) g -- 1-5000 container entries
183-
containedEntries <- uniformRM (1, containerEntries) g -- 1-container count
184-
185-
-- Generate container value (1 token per policy for flat structure)
186-
container <- generateConstrainedValue containerEntries 1 g
187-
188-
-- Generate contained as subset of container entries
189-
let containerList = Value.toList container
190-
containedList = take containedEntries containerList
191-
contained = Value.fromList containedList
192-
193-
pure (container, contained)
194-
195100
----------------------------------------------------------------------------------------------------
196101
-- ValueData ---------------------------------------------------------------------------------------
197102

@@ -210,28 +115,41 @@ unValueDataBenchmark gen =
210115

211116
-- | Generate common test values for benchmarking
212117
generateTestValues :: StdGen -> [Value]
213-
generateTestValues gen = runStateGen_ gen \g -> do
214-
let
215-
baseValueSizes = [1, 10, 50, 100, 500, 1_000]
216-
217-
sequence $
218-
concat
219-
[ -- Empty value as edge case
220-
[pure Value.empty]
221-
, -- Standard value sizes
222-
[ generateConstrainedValue numPolicies 10 g
223-
| numPolicies <- baseValueSizes
224-
]
225-
, -- Budget-constrained tests
226-
[ generateValueWithBudget budget g
227-
| budget <- [1_000, 10_000, 30_000]
228-
]
229-
, -- Random tests for parameter spread (50 combinations)
230-
replicate 50 $ do
231-
numPolicies <- uniformRM (1, 1_000) g
232-
tokensPerPolicy <- uniformRM (1, 500) g
233-
generateConstrainedValue numPolicies tokensPerPolicy g
234-
]
118+
generateTestValues gen = runStateGen_ gen \g ->
119+
-- Empty value as edge case
120+
(Value.empty :)
121+
<$>
122+
-- Random tests for parameter spread (100 combinations)
123+
replicateM 100 (generateValue g)
124+
125+
-- | Generate Value with random budget between 1 and 30,000 bytes
126+
generateValue :: (StatefulGen g m) => g -> m Value
127+
generateValue g = do
128+
maxEntries <- uniformRM (1, maxValueEntries maxValueInBytes) g
129+
generateValueMaxEntries maxEntries g
130+
where
131+
-- Maximum budget for Value generation (30,000 bytes)
132+
maxValueInBytes :: Int
133+
maxValueInBytes = 30_000
134+
135+
-- Calculate maximum possible number of entries with maximal key sizes that fits in the budget
136+
maxValueEntries :: Int -> Int
137+
maxValueEntries budget =
138+
let bytesPerEntry =
139+
{- bytes per policy -} Value.maxKeyLen
140+
{- bytes per token -} + Value.maxKeyLen
141+
{- bytes per quantity (Int64 takes up 8 bytes) -} + 8
142+
in budget `div` bytesPerEntry
143+
144+
-- | Generate Value within total size budget
145+
generateValueMaxEntries :: (StatefulGen g m) => Int -> g -> m Value
146+
generateValueMaxEntries maxEntries g = do
147+
-- Uniform random distribution: cover full range from many policies (few tokens each)
148+
-- to few policies (many tokens each)
149+
numPolicies <- uniformRM (1, maxEntries) g
150+
let tokensPerPolicy = if numPolicies > 0 then maxEntries `div` numPolicies else 0
151+
152+
generateConstrainedValue numPolicies tokensPerPolicy g
235153

236154
-- | Generate constrained Value
237155
generateConstrainedValue
@@ -244,42 +162,13 @@ generateConstrainedValue numPolicies tokensPerPolicy g = do
244162
policyIds <- replicateM numPolicies (generateKey g)
245163
tokenNames <- replicateM tokensPerPolicy (generateKey g)
246164

247-
-- Generate positive quantities (1 to 1000000)
248-
let quantity :: Int -> Int -> Integer
249-
quantity policyIndex tokenIndex =
250-
fromIntegral (1 + (policyIndex * 1_000 + tokenIndex) `mod` 1_000_000)
165+
let quantity :: Integer
166+
quantity = fromIntegral (maxBound :: Int64)
251167

252168
nestedMap :: [(K, [(K, Integer)])]
253-
nestedMap =
254-
[ ( policyId
255-
, [ (tokenName, quantity policyIndex tokenIndex)
256-
| (tokenIndex, tokenName) <- zip [0 ..] tokenNames
257-
]
258-
)
259-
| (policyIndex, policyId) <- zip [0 ..] policyIds
260-
]
261-
pure $ Value.fromList nestedMap
169+
nestedMap = (,(,quantity) <$> tokenNames) <$> policyIds
262170

263-
-- | Generate Value within total size budget
264-
generateValueWithBudget
265-
:: (StatefulGen g m)
266-
=> Int -- Target total size budget
267-
-> g
268-
-> m Value
269-
generateValueWithBudget budget g = do
270-
let
271-
keySize = Value.maxKeyLen
272-
overhead = 8 -- bytes per amount
273-
274-
-- Calculate maximum possible entries with fixed key sizes
275-
bytesPerEntry = keySize + keySize + overhead -- policy + token + amount
276-
maxEntries = budget `div` bytesPerEntry
277-
278-
-- Simple distribution: try to balance policies and tokens
279-
numPolicies = max 1 (floor (sqrt (fromIntegral maxEntries :: Double)))
280-
tokensPerPolicy = if numPolicies > 0 then maxEntries `div` numPolicies else 0
281-
282-
generateConstrainedValue numPolicies tokensPerPolicy g
171+
pure $ Value.fromList nestedMap
283172

284173
----------------------------------------------------------------------------------------------------
285174
-- Other Generators --------------------------------------------------------------------------------

0 commit comments

Comments
 (0)