Skip to content

Commit e3d182d

Browse files
authored
Precompute nock hashes (#3384)
* Closes #3383 * Depends on: #3370 ### Benchmarks - `juvix-old` - the version from #3370 - `juvix` - the version from this PR Both compiled with optimizations. ``` $ hyperfine -w 1 -p 'rm -rf .juvix-build' 'juvix-old compile anoma test001.juvix --modular' 'juvix compile anoma test001.juvix --modular' Benchmark 1: juvix-old compile anoma test001.juvix --modular Time (mean ± σ): 6.178 s ± 0.041 s [User: 12.155 s, System: 0.879 s] Range (min … max): 6.101 s … 6.238 s 10 runs Benchmark 2: juvix compile anoma test001.juvix --modular Time (mean ± σ): 5.775 s ± 0.018 s [User: 11.636 s, System: 0.900 s] Range (min … max): 5.750 s … 5.794 s 10 runs Summary 'juvix compile anoma test001.juvix --modular' ran 1.07 ± 0.01 times faster than 'juvix-old compile anoma test001.juvix --modular' ``` ``` $ hyperfine -w 1 'juvix-old compile anoma test001.juvix --modular' 'juvix compile anoma test001.juvix --modular' Benchmark 1: juvix-old compile anoma test001.juvix --modular Time (mean ± σ): 2.160 s ± 0.049 s [User: 3.540 s, System: 0.829 s] Range (min … max): 2.092 s … 2.234 s 10 runs Benchmark 2: juvix compile anoma test001.juvix --modular Time (mean ± σ): 1.811 s ± 0.031 s [User: 3.209 s, System: 0.847 s] Range (min … max): 1.769 s … 1.856 s 10 runs Summary 'juvix compile anoma test001.juvix --modular' ran 1.19 ± 0.03 times faster than 'juvix-old compile anoma test001.juvix --modular' ``` ``` $ hyperfine -w 1 'juvix-old dev nockma run builtin-evaluator test001.nockma --storage test001.modules.nockma' 'juvix dev nockma run builtin-evaluator test001.nockma --storage test001.modules.nockma' Benchmark 1: juvix-old dev nockma run builtin-evaluator test001.nockma --storage test001.modules.nockma Time (mean ± σ): 6.693 s ± 0.056 s [User: 7.388 s, System: 1.212 s] Range (min … max): 6.614 s … 6.811 s 10 runs Benchmark 2: juvix dev nockma run builtin-evaluator test001.nockma --storage test001.modules.nockma Time (mean ± σ): 6.933 s ± 0.060 s [User: 7.650 s, System: 1.321 s] Range (min … max): 6.783 s … 6.997 s 10 runs Summary 'juvix-old dev nockma run builtin-evaluator test001.nockma --storage test001.modules.nockma' ran 1.04 ± 0.01 times faster than 'juvix dev nockma run builtin-evaluator test001.nockma --storage test001.modules.nockma' ``` ### Conclusion - Precomputing the hashes increases the performance of jamming, which dominates the final stage of compilation to Nock. - The additional field in the `CellInfo` datatype decreases evaluation performance, which is not compensated by the faster module load times. However, compilation is the main user-facing feature of the Juvix compiler.
1 parent 933eb17 commit e3d182d

File tree

5 files changed

+51
-38
lines changed

5 files changed

+51
-38
lines changed

src/Juvix/Compiler/Nockma/Encoding/Cue.hs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ atomToBits a' = do
165165
-- | Transform a vector of bits to a decoded term
166166
cueFromBits ::
167167
forall a r.
168-
( NockNatural a,
168+
( Hashable a,
169+
NockNatural a,
169170
Members
170171
'[ Error DecodingError,
171172
Error (ErrNockNatural' a)
@@ -191,7 +192,8 @@ cueFromByteStringNatural = cueFromByteString'
191192

192193
cueFromByteString' ::
193194
forall a r.
194-
( NockNatural a,
195+
( Hashable a,
196+
NockNatural a,
195197
Members
196198
'[ Error DecodingError,
197199
Error (ErrNockNatural' a)
@@ -204,7 +206,8 @@ cueFromByteString' = cueFromBits . cloneFromByteString
204206

205207
cueFromBitsSem ::
206208
forall a r.
207-
( NockNatural a,
209+
( Hashable a,
210+
NockNatural a,
208211
Members
209212
'[ BitReader,
210213
Error DecodingError,
@@ -236,9 +239,9 @@ cueFromBitsSem = registerElementStart $ do
236239

237240
goCell :: Sem r (Term a)
238241
goCell = do
239-
_cellLeft <- cueFromBitsSem
240-
_cellRight <- cueFromBitsSem
241-
let cell = TermCell (Cell' {_cellInfo = emptyCellInfo, ..})
242+
l <- cueFromBitsSem
243+
r <- cueFromBitsSem
244+
let cell = TermCell (mkCell l r)
242245
cacheCueTerm cell
243246
return cell
244247

@@ -266,7 +269,8 @@ cueFromBitsSem = registerElementStart $ do
266269
-- | Decode an nock Atom to a nock term
267270
cue ::
268271
forall a r.
269-
( NockNatural a,
272+
( Hashable a,
273+
NockNatural a,
270274
Members
271275
'[ Error DecodingError,
272276
Error (ErrNockNatural a)
@@ -283,7 +287,8 @@ cue a' =
283287
-- | A variant of cue with `ErrNockNatural` wrapped in a newtype to disambiguate it from DecodingError
284288
cue' ::
285289
forall a r.
286-
( NockNatural a,
290+
( Hashable a,
291+
NockNatural a,
287292
Members
288293
'[ Error DecodingError,
289294
Error (ErrNockNatural' a)
@@ -299,7 +304,8 @@ cueEither ::
299304
-- overlapping instances with `ErrNockNatural a` when errors are handled. See
300305
-- the comment above `ErrNockNatural' a` for more explanation.
301306
forall a r.
302-
( NockNatural a,
307+
( Hashable a,
308+
NockNatural a,
303309
Member (Error (ErrNockNatural a)) r
304310
) =>
305311
Atom a ->
@@ -314,7 +320,8 @@ cueFromByteString ::
314320
-- overlapping instances with `ErrNockNatural a` when errors are handled. See
315321
-- the comment above `ErrNockNatural' a` for more explanation.
316322
forall a r.
317-
( NockNatural a,
323+
( Hashable a,
324+
NockNatural a,
318325
Member (Error (ErrNockNatural a)) r
319326
) =>
320327
ByteString ->
@@ -326,7 +333,7 @@ cueFromByteString =
326333

327334
cueFromByteString'' ::
328335
forall a.
329-
(NockNatural a) =>
336+
(Hashable a, NockNatural a) =>
330337
ByteString ->
331338
Either (ErrNockNatural a) (Either DecodingError (Term a))
332339
cueFromByteString'' = run . runErrorNoCallStack . cueFromByteString

src/Juvix/Compiler/Nockma/Evaluator.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ evalProfile inistack initerm =
302302
goAnomaSetFromList :: Term a -> Term a
303303
goAnomaSetFromList arg =
304304
foldr
305-
(\t acc -> TermCell (Cell' t acc emptyCellInfo))
305+
(\t acc -> TermCell (mkCell t acc))
306306
(TermAtom nockNil)
307307
(nubHashable (checkTermToList arg))
308308

src/Juvix/Compiler/Nockma/Evaluator/Storage.hs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ newtype StorageKey a = StorageKey {_storageKeyTerm :: Term a}
1616
makeLenses ''Storage
1717
makeLenses ''StorageKey
1818

19-
stripMeta :: Term a -> Term a
19+
stripMeta :: (Hashable a) => Term a -> Term a
2020
stripMeta = \case
2121
TermAtom a ->
2222
TermAtom
@@ -25,17 +25,13 @@ stripMeta = \case
2525
_atomInfo = emptyAtomInfo
2626
}
2727
TermCell c ->
28-
TermCell
29-
Cell'
30-
{ _cellLeft = stripMeta (c ^. cellLeft),
31-
_cellRight = stripMeta (c ^. cellRight),
32-
_cellInfo = emptyCellInfo
33-
}
28+
TermCell $
29+
mkCell (stripMeta (c ^. cellLeft)) (stripMeta (c ^. cellRight))
3430

35-
instance (NockmaEq a) => NockmaEq (StorageKey a) where
31+
instance (Hashable a, NockmaEq a) => NockmaEq (StorageKey a) where
3632
nockmaEq (StorageKey s1) (StorageKey s2) = nockmaEq s1 s2
3733

38-
instance (NockmaEq a) => Eq (StorageKey a) where
34+
instance (Hashable a, NockmaEq a) => Eq (StorageKey a) where
3935
(==) = nockmaEq
4036

4137
instance (NockmaEq a, Hashable a) => Hashable (StorageKey a) where
@@ -52,10 +48,10 @@ instance (NockmaEq a, Hashable a) => Hashable (StorageKey a) where
5248
TermAtom a -> goAtom a
5349
TermCell c -> goCell c
5450

55-
instance (PrettyCode a, NockNatural a) => PrettyCode (StorageKey a) where
51+
instance (PrettyCode a, Hashable a, NockNatural a) => PrettyCode (StorageKey a) where
5652
ppCode k = ppCode (stripMeta (k ^. storageKeyTerm))
5753

58-
instance (NockmaEq a) => Semigroup (Storage a) where
54+
instance (Hashable a, NockmaEq a) => Semigroup (Storage a) where
5955
Storage s1 <> Storage s2 =
6056
Storage
6157
{ _storageKeyValueData = HashMap.union s1 s2

src/Juvix/Compiler/Nockma/Language.hs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,11 @@ instance Serialize Tag
8181
data CellInfo a = CellInfo
8282
{ _cellInfoLoc :: Irrelevant (Maybe Interval),
8383
_cellInfoTag :: Maybe Tag,
84-
_cellInfoCall :: Maybe (AnomaLibCall a)
84+
_cellInfoCall :: Maybe (AnomaLibCall a),
85+
_cellInfoHash :: Int
8586
}
8687
deriving stock (Show, Eq, Lift, Generic)
8788

88-
instance (Hashable a) => Hashable (CellInfo a)
89-
9089
instance (NFData a) => NFData (CellInfo a)
9190

9291
instance (Serialize a) => Serialize (CellInfo a)
@@ -102,7 +101,8 @@ instance (Eq a) => Eq (Cell a) where
102101
Cell' l r _ == Cell' l' r' _ = l == l' && r == r'
103102

104103
instance (Hashable a) => Hashable (Cell a) where
105-
hashWithSalt salt (Cell' l r _) = hashWithSalt salt (l, r)
104+
hashWithSalt salt (Cell' _ _ CellInfo {..}) = hashWithSalt salt _cellInfoHash
105+
hash (Cell' _ _ CellInfo {..}) = _cellInfoHash
106106

107107
instance (NFData a) => NFData (Cell a)
108108

@@ -280,8 +280,13 @@ makeLenses ''WithStack
280280
makeLenses ''AtomInfo
281281
makeLenses ''CellInfo
282282

283-
singletonProgram :: Term a -> Program a
284-
singletonProgram t = Program [StatementStandalone t]
283+
mkCell :: (Hashable a) => Term a -> Term a -> Cell a
284+
mkCell l r =
285+
Cell'
286+
{ _cellLeft = l,
287+
_cellRight = r,
288+
_cellInfo = emptyCellInfo {_cellInfoHash = hash (l, r)}
289+
}
285290

286291
isCell :: Term a -> Bool
287292
isCell = \case
@@ -294,8 +299,11 @@ isAtom = not . isCell
294299
atomHint :: Lens' (Atom a) (Maybe AtomHint)
295300
atomHint = atomInfo . atomInfoHint
296301

302+
singletonProgram :: Term a -> Program a
303+
singletonProgram t = Program [StatementStandalone t]
304+
297305
-- | Removes all extra information recursively
298-
removeInfoRec :: Term a -> Term a
306+
removeInfoRec :: forall a. (Hashable a) => Term a -> Term a
299307
removeInfoRec = go
300308
where
301309
go :: Term a -> Term a
@@ -557,17 +565,17 @@ opTrace' msg val = OpHint # (nockNilTagged "opTrace'" # msg) # val
557565

558566
{-# COMPLETE Cell #-}
559567

560-
pattern Cell :: Term a -> Term a -> Cell a
568+
pattern Cell :: (Hashable a) => Term a -> Term a -> Cell a
561569
pattern Cell {_cellLeft', _cellRight'} <- Cell' _cellLeft' _cellRight' _
562570
where
563-
Cell a b = Cell' a b emptyCellInfo
571+
Cell a b = mkCell a b
564572

565573
{-# COMPLETE TCell, TAtom #-}
566574

567-
pattern TCell :: Term a -> Term a -> Term a
575+
pattern TCell :: (Hashable a) => Term a -> Term a -> Term a
568576
pattern TCell l r <- TermCell (Cell' l r _)
569577
where
570-
TCell a b = TermCell (Cell a b)
578+
TCell a b = TermCell (mkCell a b)
571579

572580
pattern TAtom :: a -> Term a
573581
pattern TAtom a <- TermAtom (Atom a _)
@@ -579,7 +587,8 @@ emptyCellInfo =
579587
CellInfo
580588
{ _cellInfoCall = Nothing,
581589
_cellInfoTag = Nothing,
582-
_cellInfoLoc = Irrelevant Nothing
590+
_cellInfoLoc = Irrelevant Nothing,
591+
_cellInfoHash = 0
583592
}
584593

585594
emptyAtomInfo :: AtomInfo
@@ -605,14 +614,14 @@ instance (NockmaEq a) => NockmaEq [a] where
605614
instance (NockmaEq a) => NockmaEq (Atom a) where
606615
nockmaEq = nockmaEq `on` (^. atom)
607616

608-
instance (NockmaEq a) => NockmaEq (Term a) where
617+
instance (Hashable a, NockmaEq a) => NockmaEq (Term a) where
609618
nockmaEq = \cases
610619
(TermAtom a) (TermAtom b) -> nockmaEq a b
611620
(TermCell a) (TermCell b) -> nockmaEq a b
612621
TermCell {} TermAtom {} -> False
613622
TermAtom {} TermCell {} -> False
614623

615-
instance (NockmaEq a) => NockmaEq (Cell a) where
624+
instance (Hashable a, NockmaEq a) => NockmaEq (Cell a) where
616625
nockmaEq (Cell l r) (Cell l' r') = nockmaEq l l' && nockmaEq r r'
617626

618627
crash :: Term Natural

src/Juvix/Compiler/Nockma/Translation/FromSource/Base.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ cell = do
312312
CellInfo
313313
{ _cellInfoCall = c,
314314
_cellInfoTag = lbl,
315-
_cellInfoLoc = Irrelevant (Just (lloc <> rloc))
315+
_cellInfoLoc = Irrelevant (Just (lloc <> rloc)),
316+
_cellInfoHash = hash r
316317
}
317318
return (set cellInfo info r)
318319
where

0 commit comments

Comments
 (0)