Skip to content

Commit

Permalink
[Costing] Increase the cost of constructors of '[]' (#6285)
Browse files Browse the repository at this point in the history
  • Loading branch information
effectfully authored Jul 18, 2024
1 parent 903e383 commit d89a339
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 23 deletions.
1 change: 0 additions & 1 deletion plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1476,7 +1476,6 @@ instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where
(runCostingFunThreeArguments . paramChooseList)

toBuiltinMeaning _semvar MkCons =

let mkConsDenotation
:: SomeConstant uni a -> SomeConstant uni [a] -> BuiltinResult (Opaque val [a])
mkConsDenotation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,25 @@ instance ExMemoryUsage BS.ByteString where
{-# INLINE memoryUsage #-}

instance ExMemoryUsage T.Text where
-- This is slow and inaccurate, but matches the version that was originally deployed.
-- We may try and improve this in future so long as the new version matches this exactly.
memoryUsage text = memoryUsage $ T.unpack text
-- This says that @Text@ allocates 1 'CostingInteger' worth of memory (i.e. 8 bytes) per
-- character, which is a conservative overestimate (i.e. is safe) regardless of whether @Text@
-- is UTF16-based (like it used to when we implemented this instance) or UTF8-based (like it is
-- now).
--
-- Note that the @ExMemoryUsage Char@ instance does not affect this one, this is for performance
-- reasons, since @T.length@ is O(1) unlike @sum . map (memoryUsage @Char) . T.unpack@. We used
-- to have the latter, but changed it to the former for easy performance gains.
--
-- We may want to make this a bit less of an overestimate in future just not to overcharge
-- users.
memoryUsage = singletonRose . fromIntegral . T.length
{-# INLINE memoryUsage #-}

instance ExMemoryUsage Int where
memoryUsage _ = singletonRose 1
{-# INLINE memoryUsage #-}

-- If you ever change this, also change @ExMemoryUsage T.Text@.
instance ExMemoryUsage Char where
memoryUsage _ = singletonRose 1
{-# INLINE memoryUsage #-}
Expand All @@ -231,8 +241,24 @@ instance ExMemoryUsage Bool where
memoryUsage _ = singletonRose 1
{-# INLINE memoryUsage #-}

-- | Add two 'CostRose's. We don't make this into a 'Semigroup' instance, because there exist
-- different ways to add two 'CostRose's (e.g. we could optimize the case when one of the roses
-- contains only one element or we can make the function lazy in the second argument). Here we chose
-- the version that is most efficient when the first argument is a statically known constant (we
-- didn't do any benchmarking though, so it may not be the most efficient one) as we need this
-- below.
addConstantRose :: CostRose -> CostRose -> CostRose
addConstantRose (CostRose cost1 forest1) (CostRose cost2 forest2) =
CostRose (cost1 + cost2) (forest1 ++ forest2)
{-# INLINE addConstantRose #-}

instance ExMemoryUsage a => ExMemoryUsage [a] where
memoryUsage = CostRose 0 . map memoryUsage
memoryUsage = CostRose nilCost . map (addConstantRose consRose . memoryUsage) where
-- As per https://wiki.haskell.org/GHC/Memory_Footprint
nilCost = 1
{-# INLINE nilCost #-}
consRose = singletonRose 3
{-# INLINE consRose #-}
{-# INLINE memoryUsage #-}

{- Another naive traversal for size. This accounts for the number of nodes in
Expand All @@ -253,29 +279,17 @@ instance ExMemoryUsage a => ExMemoryUsage [a] where
-}
instance ExMemoryUsage Data where
memoryUsage = sizeData where
-- The cost of each node of the 'Data' object (in addition to the cost of its content).
nodeMem = singletonRose 4
{-# INLINE nodeMem #-}

-- Add two 'CostRose's. We don't make this into a 'Semigroup' instance, because there exist
-- different ways to add two 'CostRose's (e.g. we could optimize the case when one of the
-- roses contains only one element or we can make the function lazy in the second argument).
-- Here we chose the version that is most efficient when the first argument is @nodeMem@ (we
-- didn't do any benchmarking though, so it may not be the most efficient one) -- we don't
-- have any other cases.
combine (CostRose cost1 forest1) (CostRose cost2 forest2) =
CostRose (cost1 + cost2) (forest1 ++ forest2)
{-# INLINE combine #-}

sizeData d = combine nodeMem $ case d of
-- TODO: include the size of the tag, but not just yet. See SCP-3677.
dataNodeRose = singletonRose 4
{-# INLINE dataNodeRose #-}

sizeData d = addConstantRose dataNodeRose $ case d of
-- TODO: include the size of the tag, but not just yet. See PLT-1176.
Constr _ l -> CostRose 0 $ l <&> sizeData
Map l -> CostRose 0 $ l >>= \(d1, d2) -> [d1, d2] <&> sizeData
List l -> CostRose 0 $ l <&> sizeData
I n -> memoryUsage n
B b -> memoryUsage b


{- Note [Costing constant-size types]
The memory usage of each of the BLS12-381 types is constant, so we may be able
to optimise things a little by ensuring that we don't re-compute the size of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ test_flattenCostRoseHandlesBottom =
test_costsAreNeverNegative :: TestTree
test_costsAreNeverNegative =
testProperty "costs coming from 'memoryUsage' are never negative" $
withMaxSuccess 500 $ \(val :: Some (ValueOf DefaultUni)) ->
withMaxSuccess 1000 $ \(val :: Some (ValueOf DefaultUni)) ->
all (>= 0) . toCostList . flattenCostRose $ memoryUsage val

test_costing :: TestTree
Expand Down

1 comment on commit d89a339

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Plutus Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: d89a339 Previous: 903e383 Ratio
nofib-clausify/formula1 3146 μs 2957 μs 1.06
nofib-clausify/formula2 4195 μs 3956 μs 1.06

This comment was automatically generated by workflow using github-action-benchmark.

CC: @IntersectMBO/plutus-core

Please sign in to comment.