Skip to content

Commit 531487d

Browse files
committed
wip implement SizeOf in terms of MonadEval
1 parent efc9769 commit 531487d

File tree

12 files changed

+1112
-90
lines changed

12 files changed

+1112
-90
lines changed

bench/Bench.hs

Lines changed: 785 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
{-# LANGUAGE DeriveGeneric #-}
2+
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
3+
-- | Tests for the sizes of various values.
4+
5+
module Pact.Core.Test.SizeOfTests where
6+
7+
import Data.Default
8+
import Control.Monad.IO.Class
9+
import qualified Data.Map as Map
10+
import Data.Text
11+
import GHC.Generics (Generic)
12+
import Test.Tasty (TestTree, testGroup)
13+
import Test.Tasty.HUnit
14+
15+
import Pact.Core.Builtin
16+
import Pact.Core.Persistence.MockPersistence
17+
import Pact.Core.Names
18+
import Pact.Core.SizeOf
19+
( constructorCost,
20+
wordSize,
21+
SizeOf(..),
22+
SizeOfVersion(SizeOfV0, SizeOfV1, SizeOfV2) )
23+
import Pact.Core.Errors
24+
import Pact.Core.Environment.Types
25+
import Pact.Core.PactValue
26+
import Pact.Core.Serialise
27+
import Pact.Core.IR.Eval.Runtime.Types (runEvalM)
28+
29+
tests :: TestTree
30+
tests = testGroup "SizeOfTests" $
31+
[ testCase "int" $ do
32+
Right size <- getSize SizeOfV0 (1 :: Int)
33+
assertEqual "size should be 1" 16 size
34+
, testCase "string" $ do
35+
Right size <- getSize SizeOfV0 ("a" :: Text)
36+
assertEqual "size should be 3" 50 size
37+
, testCase "FieldKey" $ do
38+
Right size <- getSize SizeOfV0 (Field "a")
39+
assertEqual "size should be 50" 50 size
40+
, testCase "PactValue1" $ do
41+
Right size <- getSize SizeOfV0 (PInteger 1)
42+
assertEqual "size should be 32" 32 size
43+
, sizeOfSmallObject SizeOfV0 146
44+
, sizeOfSmallObject SizeOfV1 154
45+
, sizeOfGenericsTest SizeOfV0
46+
, sizeOfGenericsTest SizeOfV1
47+
, sizeOfGenericsTest SizeOfV2
48+
]
49+
50+
getSize :: SizeOf a => SizeOfVersion -> a -> IO (Either PactErrorI Bytes)
51+
getSize version value = do
52+
pdb <- mockPactDb serialisePact_repl_spaninfo
53+
ee <- defaultEvalEnv pdb replCoreBuiltinMap
54+
let es = def
55+
(v, _state) <- liftIO $ runEvalM ee es (sizeOf version value)
56+
return v
57+
58+
sizeOfSmallObject :: SizeOfVersion -> Bytes -> TestTree
59+
sizeOfSmallObject version expectation = testCase ("pactvalue-smallobject-" ++ show version) $ do
60+
Right size <- getSize version smallObject
61+
assertEqual "size should match expectation" expectation size
62+
where
63+
smallObject :: PactValue
64+
smallObject = PObject $ Map.fromList [(Field "a", PInteger 1)]
65+
66+
-- Testing whether derived instance for empty constructors is 1 word
67+
data A = A1 | A2 deriving (Eq, Show, Generic)
68+
data B = B1 Int | B2 Int Bool | B3 Int Bool A deriving (Eq, Show, Generic)
69+
data C a = C a deriving (Eq, Show, Generic)
70+
71+
instance SizeOf A
72+
instance SizeOf B
73+
instance SizeOf a => SizeOf (C a)
74+
75+
newtype D = D Int
76+
deriving (Eq, Show, Generic)
77+
78+
instance SizeOf D
79+
80+
newtype F = F Int
81+
deriving (Eq, Show, SizeOf)
82+
83+
sizeOfGenericsTest :: SizeOfVersion -> TestTree
84+
sizeOfGenericsTest szVer =
85+
testCase ("SizeOf " <> show szVer <> " generics conform to specification") $ do
86+
Right a1Size <- getSize szVer A1
87+
Right a2Size <- getSize szVer A2
88+
assertEqual "A1 wordSize" wordSize a1Size
89+
assertEqual "A2 wordSize" wordSize a2Size
90+
91+
Right b1Size <- getSize szVer (B1 0)
92+
Right b2Size <- getSize szVer (B2 0 True)
93+
Right b3Size <- getSize szVer (B3 0 True A1)
94+
Right intSize <- getSize szVer (0::Int)
95+
Right boolSize <- getSize szVer True
96+
assertEqual "B1 size" (intSize + constructorCost 1) b1Size
97+
assertEqual "B2 size" (intSize + boolSize + constructorCost 2) b2Size
98+
assertEqual "B3 size" (intSize + boolSize + a1Size + constructorCost 3) b3Size
99+
100+
Right cSize <- getSize szVer (C (B1 0))
101+
Right dSize <- getSize szVer (D 1)
102+
assertEqual "C size" (b1Size + constructorCost 1) cSize
103+
assertEqual "D size" (intSize + constructorCost 1) dSize
104+
105+
Right fSize <- getSize szVer (F 1)
106+
assertEqual "F size" intSize fSize

pact-tests/PactCoreTests.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import qualified Pact.Core.Test.StaticErrorTests as StaticErrorTests
1212
import qualified Pact.Core.Test.ZkTests as ZkTests
1313
import qualified Pact.Core.Test.PoseidonTests as PoseidonTests
1414
import qualified Pact.Core.Test.LanguageServer as LanguageServer
15+
import qualified Pact.Core.Test.SizeOfTests as SizeOfTests
1516

1617
main :: IO ()
1718
main = do
@@ -27,4 +28,5 @@ main = do
2728
, PoseidonTests.tests
2829
, PersistenceTests.tests
2930
, LanguageServer.tests
31+
, SizeOfTests.tests
3032
]

pact-tng.cabal

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,21 @@ executable pact
265265
-- beware of the autogen modules. Remember to `cabal clean`!
266266
other-modules: PackageInfo_pact_tng
267267

268+
benchmark bench
269+
type: exitcode-stdio-1.0
270+
main-is: Bench.hs
271+
build-depends:
272+
base
273+
, containers
274+
, criterion
275+
, data-default
276+
, filepath
277+
, mtl
278+
, pact-tng
279+
, text
280+
ghc-options: -WAll -threaded -rtsopts "-with-rtsopts=-N"
281+
hs-source-dirs: bench
282+
default-language: Haskell2010
268283

269284
test-suite core-tests
270285
main-is: PactCoreTests.hs
@@ -326,6 +341,7 @@ test-suite core-tests
326341
, Pact.Core.Test.SerialiseTests
327342
, Pact.Core.Test.LegacySerialiseTests
328343
, Pact.Core.Gen.Serialise
344+
, Pact.Core.Test.SizeOfTests
329345
, Pact.Core.Test.StaticErrorTests
330346
, Pact.Core.Test.TestPrisms
331347
, Pact.Core.Test.ZkTests

pact/Pact/Core/Compile.hs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ evalTopLevel bEnv tlFinal deps = do
214214
CapGov _ -> pure ()
215215
let deps' = M.filterWithKey (\k _ -> S.member (_fqModule k) deps) (_loAllLoaded lo0)
216216
mdata = ModuleData m deps'
217-
chargeGasArgs (_mInfo m) (GModuleMemory (sizeOf SizeOfV0 m))
217+
mSize <- sizeOf SizeOfV0 m
218+
chargeGasArgs (_mInfo m) (GModuleMemory mSize)
218219
_ <- liftDbFunction2 (_mInfo m) $ writeModule pdb Write (view mName m) mdata
219220
let fqDeps = toFqDep (_mName m) (_mHash m) <$> _mDefs m
220221
newLoaded = M.fromList fqDeps
@@ -229,7 +230,8 @@ evalTopLevel bEnv tlFinal deps = do
229230
TLInterface iface -> do
230231
let deps' = M.filterWithKey (\k _ -> S.member (_fqModule k) deps) (_loAllLoaded lo0)
231232
mdata = InterfaceData iface deps'
232-
chargeGasArgs (_ifInfo iface) (GModuleMemory (sizeOf SizeOfV0 iface))
233+
ifaceSize <- sizeOf SizeOfV0 iface
234+
chargeGasArgs (_ifInfo iface) (GModuleMemory ifaceSize)
233235
_ <- liftDbFunction2 (_ifInfo iface) $ writeModule pdb Write (view ifName iface) mdata
234236
let fqDeps = toFqDep (_ifName iface) (_ifHash iface)
235237
<$> mapMaybe ifDefToDef (_ifDefns iface)

pact/Pact/Core/Environment/Types.hs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ module Pact.Core.Environment.Types
4545
, MonadEvalState(..)
4646
, MonadEval
4747
, defaultEvalEnv
48+
, GasLogEntry(..)
49+
, Bytes
50+
, SizeOfByteLimit(..)
4851
) where
4952

5053

@@ -56,6 +59,7 @@ import Data.Text(Text)
5659
import Data.Map.Strict(Map)
5760
import Data.IORef
5861
import Data.Default
62+
import Data.Word (Word64)
5963

6064
import Control.DeepSeq
6165
import GHC.Generics
@@ -65,6 +69,7 @@ import qualified Data.Text as T
6569
import qualified Data.Map.Strict as M
6670

6771
import Pact.Core.Persistence
72+
import Pact.Core.Pretty
6873
import Pact.Core.Capabilities
6974
import Pact.Core.Guards
7075
import Pact.Core.PactValue
@@ -75,7 +80,6 @@ import Pact.Core.ChainData
7580
import Pact.Core.Errors
7681
import Pact.Core.Gas
7782
import Pact.Core.Namespace
78-
import Pact.Core.SizeOf
7983
import Pact.Core.Builtin (IsBuiltin)
8084

8185
-- | Execution flags specify behavior of the runtime environment,
@@ -174,12 +178,13 @@ data EvalState b i
174178
, _esDefPactExec :: !(Maybe DefPactExec)
175179
, _esGasLog :: Maybe [(Text, MilliGas)]
176180
-- ^ Sequence of gas expendature events.
181+
, _countBytesCounter :: Int
177182
} deriving (Show, Generic)
178183

179184
instance (NFData b, NFData i) => NFData (EvalState b i)
180185

181186
instance Default (EvalState b i) where
182-
def = EvalState def [] [] mempty Nothing Nothing
187+
def = EvalState def [] [] mempty Nothing Nothing 0
183188

184189
makeClassy ''EvalState
185190

@@ -204,8 +209,6 @@ type MonadEval b i m =
204209
, Exceptions.MonadCatch m
205210
, Default i
206211
, Show i
207-
, SizeOf b
208-
, SizeOf i
209212
, IsBuiltin b
210213
, Show b)
211214

@@ -228,3 +231,12 @@ defaultEvalEnv pdb m = do
228231
, _eeGasRef = gasRef
229232
, _eeGasModel = freeGasModel
230233
}
234+
235+
type Bytes = Word64
236+
237+
newtype SizeOfByteLimit
238+
= SizeOfByteLimit Bytes
239+
deriving Show
240+
241+
instance Pretty SizeOfByteLimit where
242+
pretty = pretty . show

pact/Pact/Core/Gas.hs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,13 @@ data GasArgs
158158
| GComparison !ComparisonType
159159
-- ^ Gas costs for comparisons
160160
| GPoseidonHashHackAChain !Int
161-
-- ^ poseidon-hash-hack-a-chain costs
161+
-- ^ poseidon-hash-hack-a-chain costs.
162162
| GModuleMemory !Word64
163+
| GCountBytes
164+
-- ^ Cost of computing SizeOf for N bytes.
163165
| GPassthrough MilliGas
164166
-- ^ Charge precise gas -- TODO: TEMPORARY
165-
deriving (Show)
167+
deriving (Show, Generic, NFData)
166168

167169
instance Pretty GasArgs where
168170
pretty = pretty . show

pact/Pact/Core/Gas/TableGasModel.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ runTableModel = \case
208208
GModuleMemory bytes -> moduleMemoryCost bytes
209209
GPassthrough milligas -> milligas
210210

211+
-- Running CountBytes costs 0.9 MilliGas, according to the analysis in bench/Bench.hs
212+
GCountBytes -> MilliGas 1
213+
211214
basicWorkGas :: Word64
212215
basicWorkGas = 25
213216

pact/Pact/Core/IR/Desugar.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ module Pact.Core.IR.Desugar
2525
, runDesugarReplTopLevel
2626
, DesugarOutput(..)
2727
, DesugarBuiltin(..)
28+
29+
, runDesugarModule
2830
) where
2931

3032
import Control.Applicative((<|>))

pact/Pact/Core/IR/Eval/CEK.hs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,8 +1356,8 @@ applyContToValue (BuiltinC env info frame cont) handler cv = do
13561356
let check' = if wt == Update then checkPartialSchema else checkSchema
13571357
if check' rv (_tvSchema tv) then do
13581358
let rdata = RowData rv
1359-
chargeGasArgs info (GWrite (sizeOf SizeOfV0 rv))
1360-
let serializationGasser = chargeGasArgs info . GPassthrough
1359+
rvSize <- sizeOf SizeOfV2 rv
1360+
chargeGasArgs info (GWrite rvSize)
13611361
_ <- liftDbFunction2 info $ _pdbWrite pdb serializationGasser wt (tvToDomain tv) rk rdata
13621362
returnCEKValue cont handler (VString "Write succeeded")
13631363
else returnCEK cont handler (VError "object does not match schema" info)
@@ -1420,15 +1420,16 @@ applyContToValue (BuiltinC env info frame cont) handler cv = do
14201420
enforceMeta Unmanaged = throwExecutionError info (InvalidEventCap fqn)
14211421
enforceMeta _ = pure ()
14221422
DefineKeysetC ksn newKs -> do
1423-
chargeGasArgs info (GWrite (sizeOf SizeOfV0 newKs))
1423+
newKsSize <- sizeOf SizeOfV2 newKs
1424+
chargeGasArgs info (GWrite newKsSize)
14241425
liftDbFunction2 info $ writeKeySet pdb Write ksn newKs
14251426
returnCEKValue cont handler (VString "Keyset write success")
14261427
DefineNamespaceC ns -> case v of
14271428
PBool allow ->
14281429
if allow then do
14291430
let nsn = _nsName ns
1430-
chargeGasArgs info (GWrite (sizeOf SizeOfV0 ns))
1431-
let serializationGasser = chargeGasArgs info . GPassthrough
1431+
nsSize <- sizeOf SizeOfV2 ns
1432+
chargeGasArgs info (GWrite nsSize)
14321433
liftDbFunction2 info $ _pdbWrite pdb serializationGasser Write DNamespaces nsn ns
14331434
returnCEKValue cont handler $ VString $ "Namespace defined: " <> (_namespaceName nsn)
14341435
else throwExecutionError info $ DefineNamespaceError "Namespace definition not permitted"

pact/Pact/Core/IR/Eval/CoreBuiltin.hs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -654,15 +654,19 @@ createEnumerateList
654654
-- ^ Step
655655
-> m (Vector Integer)
656656
createEnumerateList info from to inc
657-
| from == to = chargeGasArgs info (GMakeList 1 (sizeOf SizeOfV0 from)) *> pure (V.singleton from)
657+
| from == to = do
658+
fromSize <- sizeOf SizeOfV0 from
659+
chargeGasArgs info (GMakeList 1 fromSize)
660+
pure (V.singleton from)
658661
| inc == 0 = pure mempty -- note: covered by the flat cost
659662
| from < to, from + inc < from =
660663
throwExecutionError info (EnumerationError "enumerate: increment diverges below from interval bounds.")
661664
| from > to, from + inc > from =
662665
throwExecutionError info (EnumerationError "enumerate: increment diverges above from interval bounds.")
663666
| otherwise = do
664667
let len = succ (abs (from - to) `div` abs inc)
665-
chargeGasArgs info (GMakeList len (sizeOf SizeOfV0 (max (abs from) (abs to))))
668+
listSize <- sizeOf SizeOfV0 (max (abs from) (abs to))
669+
chargeGasArgs info (GMakeList len listSize)
666670
pure $ V.enumFromStepN from inc (fromIntegral len)
667671

668672
coreEnumerateStepN :: (CEKEval step b i m, MonadEval b i m) => NativeFunction step b i m
@@ -675,7 +679,8 @@ coreEnumerateStepN info b cont handler _env = \case
675679
makeList :: (CEKEval step b i m, MonadEval b i m) => NativeFunction step b i m
676680
makeList info b cont handler _env = \case
677681
[VLiteral (LInteger i), VPactValue v] -> do
678-
chargeGasArgs info (GMakeList (fromIntegral i) (sizeOf SizeOfV0 v))
682+
vSize <- sizeOf SizeOfV0 v
683+
chargeGasArgs info (GMakeList (fromIntegral i) vSize)
679684
returnCEKValue cont handler (VList (V.fromList (replicate (fromIntegral i) v)))
680685
args -> argsError info b args
681686

@@ -1104,7 +1109,8 @@ defineKeySet' info cont handler env ksname newKs = do
11041109
Left {} -> returnCEK cont handler (VError "incorrect keyset name format" info)
11051110
Right ksn -> do
11061111
let writeKs = do
1107-
chargeGasArgs info (GWrite (sizeOf SizeOfV0 newKs))
1112+
newKsSize <- sizeOf SizeOfV0 newKs
1113+
chargeGasArgs info (GWrite newKsSize)
11081114
liftDbFunction2 info $ writeKeySet pdb Write ksn newKs
11091115
returnCEKValue cont handler (VString "Keyset write success")
11101116
liftDbFunction info (readKeySet pdb ksn) >>= \case
@@ -1625,8 +1631,8 @@ coreDefineNamespace info b cont handler env = \case
16251631
enforceGuard info cont' handler env laoG
16261632
Nothing -> viewEvalEnv eeNamespacePolicy >>= \case
16271633
SimpleNamespacePolicy -> do
1628-
chargeGasArgs info (GWrite (sizeOf SizeOfV0 ns))
1629-
let serializationGasser = chargeGasArgs info . GPassthrough
1634+
nsSize <- sizeOf SizeOfV0 ns
1635+
chargeGasArgs info (GWrite nsSize)
16301636
liftDbFunction2 info $ _pdbWrite pdb serializationGasser Write DNamespaces nsn ns
16311637
returnCEKValue cont handler $ VString $ "Namespace defined: " <> n
16321638
SmartNamespacePolicy _ fun -> getModuleMember info pdb fun >>= \case

0 commit comments

Comments
 (0)