Skip to content

Commit 34e9bf2

Browse files
authored
[Evaluation] [Names] Define all lookups in terms of 'contIndexZero' (#6702)
Instead of 4 different implementation this will make it 1 main and 3 derivative ones. This appears to make the validation benchmarks faster by a percent and the nofib benchmarks faster by a couple of percent while simplifying the code, so a clear win.
1 parent b570f32 commit 34e9bf2

File tree

3 files changed

+65
-88
lines changed

3 files changed

+65
-88
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Changed
2+
3+
- In #6702 made variable lookup faster increasing overall performance of the evaluator by 1%.

plutus-core/index-envs/src/Data/RandomAccessList/SkewBinary.hs

Lines changed: 61 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
{-# LANGUAGE ViewPatterns #-}
88
module Data.RandomAccessList.SkewBinary
99
( RAList(Cons,Nil)
10+
, contIndexZero
11+
, contIndexOne
1012
, safeIndexZero
1113
, unsafeIndexZero
1214
, safeIndexOne
13-
, safeIndexOneCont
1415
, unsafeIndexOne
1516
, Data.RandomAccessList.SkewBinary.null
1617
, uncons
@@ -48,7 +49,7 @@ data RAList a = BHead
4849
null :: RAList a -> Bool
4950
null Nil = True
5051
null _ = False
51-
{-# INLINABLE null #-}
52+
{-# INLINE null #-}
5253

5354
{-# complete Cons, Nil #-}
5455
{-# complete BHead, Nil #-}
@@ -63,6 +64,7 @@ cons :: a -> RAList a -> RAList a
6364
cons x = \case
6465
(BHead w1 t1 (BHead w2 t2 ts')) | w1 == w2 -> BHead (2*w1+1) (Node x t1 t2) ts'
6566
ts -> BHead 1 (Leaf x) ts
67+
{-# INLINE cons #-}
6668

6769
-- /O(1)/
6870
uncons :: RAList a -> Maybe (a, RAList a)
@@ -74,122 +76,94 @@ uncons = \case
7476
-- split the node in two)
7577
in Just (x, BHead halfSize t1 $ BHead halfSize t2 ts)
7678
Nil -> Nothing
79+
{-# INLINE uncons #-}
7780

78-
-- 0-based
79-
unsafeIndexZero :: RAList a -> Word64 -> a
80-
unsafeIndexZero Nil _ = error "out of bounds"
81-
unsafeIndexZero (BHead w t ts) !i =
82-
if i < w
83-
then indexTree w i t
84-
else unsafeIndexZero ts (i-w)
85-
where
86-
indexTree :: Word64 -> Word64 -> Tree a -> a
87-
indexTree 1 0 (Leaf x) = x
88-
indexTree _ _ (Leaf _) = error "out of bounds"
89-
indexTree _ 0 (Node x _ _) = x
90-
indexTree treeSize offset (Node _ t1 t2) =
91-
let halfSize = unsafeShiftR treeSize 1 -- probably faster than `div w 2`
92-
in if offset <= halfSize
93-
then indexTree halfSize (offset - 1) t1
94-
else indexTree halfSize (offset - 1 - halfSize) t2
95-
96-
-- 0-based
97-
safeIndexZero :: RAList a -> Word64 -> Maybe a
98-
safeIndexZero Nil _ = Nothing
99-
safeIndexZero (BHead w t ts) !i =
100-
if i < w
101-
then indexTree w i t
102-
else safeIndexZero ts (i-w)
103-
where
104-
indexTree :: Word64 -> Word64 -> Tree a -> Maybe a
105-
indexTree 1 0 (Leaf x) = Just x
106-
indexTree _ _ (Leaf _) = Nothing
107-
indexTree _ 0 (Node x _ _) = Just x
108-
indexTree treeSize offset (Node _ t1 t2) =
109-
let halfSize = unsafeShiftR treeSize 1 -- probably faster than `div w 2`
110-
in if offset <= halfSize
111-
then indexTree halfSize (offset - 1) t1
112-
else indexTree halfSize (offset - 1 - halfSize) t2
113-
114-
-- 1-based
115-
unsafeIndexOne :: RAList a -> Word64 -> a
116-
unsafeIndexOne Nil _ = error "out of bounds"
117-
unsafeIndexOne (BHead w t ts) !i =
118-
if i <= w
119-
then indexTree w i t
120-
else unsafeIndexOne ts (i-w)
121-
where
122-
indexTree :: Word64 -> Word64 -> Tree a -> a
123-
indexTree _ 0 _ = error "index zero"
124-
indexTree 1 1 (Leaf x) = x
125-
indexTree _ _ (Leaf _) = error "out of bounds"
126-
indexTree _ 1 (Node x _ _) = x
127-
indexTree treeSize offset (Node _ t1 t2) =
128-
let halfSize = unsafeShiftR treeSize 1 -- probably faster than `div w 2`
129-
offset' = offset - 1
130-
in if offset' <= halfSize
131-
then indexTree halfSize offset' t1
132-
else indexTree halfSize (offset' - halfSize) t2
133-
134-
{- Note [Optimizations of safeIndexOneCont]
135-
Bangs in the local definitions of 'safeIndexOneCont' are needed to tell GHC that the functions are
81+
{- Note [Optimizations of contIndexZero]
82+
Bangs in the local definitions of 'contIndexZero' are needed to tell GHC that the functions are
13683
strict in the 'Word64' argument, so that GHC produces workers operating on @Word64#@.
13784
13885
The function itself is CPS-ed, so that the arguments force the local definitions to be retained
139-
within 'safeIndexOneCont' instead of being pulled out via full-laziness or some other optimization
140-
pass. This ensures that when 'safeIndexOneCont' gets inlined, the local definitions appear directly
141-
in the GHC Core, allowing GHC to inline the arguments of 'safeIndexOneCont' and transform the whole
86+
within 'contIndexZero' instead of being pulled out via full-laziness or some other optimization
87+
pass. This ensures that when 'contIndexZero' gets inlined, the local definitions appear directly
88+
in the GHC Core, allowing GHC to inline the arguments of 'contIndexZero' and transform the whole
14289
thing into a beautiful recursive join point full of @Word64#@s, i.e. allocating very little if
14390
anything at all.
14491
-}
14592

146-
-- See Note [Optimizations of safeIndexOneCont].
147-
safeIndexOneCont :: forall a b. b -> (a -> b) -> RAList a -> Word64 -> b
148-
safeIndexOneCont z f = findTree where
93+
-- See Note [Optimizations of contIndexZero].
94+
contIndexZero :: forall a b. b -> (a -> b) -> RAList a -> Word64 -> b
95+
contIndexZero z f = findTree where
14996
findTree :: RAList a -> Word64 -> b
150-
-- See Note [Optimizations of safeIndexOneCont].
97+
-- See Note [Optimizations of contIndexZero].
15198
findTree Nil !_ = z
15299
findTree (BHead w t ts) i =
153-
if i <= w
100+
if i < w
154101
then indexTree w i t
155102
else findTree ts (i-w)
156103

157104
indexTree :: Word64 -> Word64 -> Tree a -> b
158-
-- See Note [Optimizations of safeIndexOneCont].
159-
indexTree !w 1 t = case t of
105+
-- See Note [Optimizations of contIndexZero].
106+
indexTree !w 0 t = case t of
160107
Node x _ _ -> f x
161108
Leaf x -> if w == 1 then f x else z
162-
indexTree _ 0 _ = z -- "index zero"
163109
indexTree _ _ (Leaf _) = z
164110
indexTree treeSize offset (Node _ t1 t2) =
165111
let halfSize = unsafeShiftR treeSize 1 -- probably faster than `div w 2`
166-
offset' = offset - 1
167-
in if offset' <= halfSize
168-
then indexTree halfSize offset' t1
169-
else indexTree halfSize (offset' - halfSize) t2
170-
{-# INLINE safeIndexOneCont #-}
112+
in if offset <= halfSize
113+
then indexTree halfSize (offset - 1) t1
114+
else indexTree halfSize (offset - 1 - halfSize) t2
115+
{-# INLINE contIndexZero #-}
116+
117+
contIndexOne :: forall a b. b -> (a -> b) -> RAList a -> Word64 -> b
118+
contIndexOne z _ _ 0 = z
119+
contIndexOne z f t n = contIndexZero z f t (n - 1)
120+
{-# INLINE contIndexOne #-}
121+
122+
-- 0-based
123+
unsafeIndexZero :: RAList a -> Word64 -> a
124+
unsafeIndexZero = contIndexZero (error "out of bounds") id
125+
{-# INLINE unsafeIndexZero #-}
126+
127+
-- 0-based
128+
safeIndexZero :: RAList a -> Word64 -> Maybe a
129+
safeIndexZero = contIndexZero Nothing Just
130+
{-# INLINE safeIndexZero #-}
131+
132+
-- 1-based
133+
unsafeIndexOne :: RAList a -> Word64 -> a
134+
unsafeIndexOne = contIndexOne (error "out of bounds") id
135+
{-# INLINE unsafeIndexOne #-}
171136

172137
-- 1-based
173138
safeIndexOne :: RAList a -> Word64 -> Maybe a
174-
safeIndexOne = safeIndexOneCont Nothing Just
139+
safeIndexOne = contIndexOne Nothing Just
140+
{-# INLINE safeIndexOne #-}
175141

176142
instance RAL.RandomAccessList (RAList a) where
177143
type Element (RAList a) = a
178144

179-
{-# INLINABLE empty #-}
180145
empty = Nil
181-
{-# INLINABLE cons #-}
146+
{-# INLINE empty #-}
147+
182148
cons = Cons
183-
{-# INLINABLE uncons #-}
149+
{-# INLINE cons #-}
150+
184151
uncons = uncons
185-
{-# INLINABLE length #-}
186-
length Nil = 0
187-
length (BHead sz _ tl) = sz + RAL.length tl
188-
{-# INLINABLE indexZero #-}
152+
{-# INLINE uncons #-}
153+
154+
length = go 0 where
155+
go !acc Nil = acc
156+
go !acc (BHead sz _ tl) = go (acc + sz) tl
157+
{-# INLINE length #-}
158+
189159
indexZero = safeIndexZero
190-
{-# INLINABLE indexOne #-}
160+
{-# INLINE indexZero #-}
161+
191162
indexOne = safeIndexOne
192-
{-# INLINABLE unsafeIndexZero #-}
163+
{-# INLINE indexOne #-}
164+
193165
unsafeIndexZero = unsafeIndexZero
194-
{-# INLINABLE unsafeIndexOne #-}
166+
{-# INLINE unsafeIndexZero #-}
167+
195168
unsafeIndexOne = unsafeIndexOne
169+
{-# INLINE unsafeIndexOne #-}

plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Evaluation/Machine/Cek/Internal.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ enterComputeCek = computeCek
878878
-- | Look up a variable name in the environment.
879879
lookupVarName :: NamedDeBruijn -> CekValEnv uni fun ann -> CekM uni fun s (CekValue uni fun ann)
880880
lookupVarName varName@(NamedDeBruijn _ varIx) varEnv =
881-
Env.safeIndexOneCont
881+
Env.contIndexOne
882882
(throwingWithCause _MachineError OpenTermEvaluatedMachineError . Just $ Var () varName)
883883
pure
884884
varEnv

0 commit comments

Comments
 (0)