Skip to content

Commit e621e40

Browse files
authored
Merge pull request #125 from unisoncomputing/cp/doc-eval-speedup
Speed up doc evaluation on Share
2 parents 4aa7285 + 579e052 commit e621e40

File tree

19 files changed

+589
-137
lines changed

19 files changed

+589
-137
lines changed

share-api.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ library
4545
Share.Branch
4646
Share.ChatApps
4747
Share.Codebase
48+
Share.Codebase.CodebaseRuntime
49+
Share.Codebase.CodeCache
4850
Share.Codebase.Types
4951
Share.Contribution
5052
Share.Env

src/Share/Backend.hs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@ import Control.Lens hiding ((??))
3838
import Data.List (unzip4, zipWith4, zipWith5)
3939
import Data.List qualified as List
4040
import Data.Map qualified as Map
41+
import Data.Set qualified as Set
4142
import Share.Codebase qualified as Codebase
43+
import Share.Codebase.CodeCache qualified as CC
4244
import Share.Codebase.Types (CodebaseRuntime (CodebaseRuntime, cachedEvalResult))
4345
import Share.Postgres (QueryM)
4446
import Share.Postgres qualified as PG
4547
import Share.Postgres.Causal.Conversions (namespaceStatsPgToV2)
4648
import Share.Postgres.Causal.Queries qualified as CausalQ
49+
import Share.Postgres.Definitions.Queries qualified as DefnQ
4750
import Share.Prelude
4851
import Share.Utils.Lens (asListOfDeduped)
4952
import U.Codebase.Branch qualified as V2Branch
@@ -289,24 +292,35 @@ evalDocRef ::
289292
Codebase.CodebaseRuntime s IO ->
290293
V2.TermReference ->
291294
m (Doc.EvaluatedDoc Symbol)
292-
evalDocRef codebase (CodebaseRuntime {codeLookup, cachedEvalResult, unisonRuntime}) termRef = do
295+
evalDocRef codebase (CodebaseRuntime {codeLookup, codeCache, cachedEvalResult, unisonRuntime}) termRef = PG.transactionSpan "evalDocRef" mempty do
293296
let tm = Term.ref () termRef
294-
-- TODO: batchify evalDoc.
295-
Doc.evalDoc terms typeOf eval decls tm
297+
case termRef of
298+
Reference.Builtin _ -> Doc.evalDoc terms typeOf eval decls tm
299+
Reference.DerivedId refId -> do
300+
termId <- DefnQ.expectTermIdsByRefIdsOf id refId
301+
(termDeps, typeDeps) <- DefnQ.termTransitiveDependencyRefs (Set.singleton termId)
302+
-- Prime the cache with all the terms and types we know we'll need.
303+
-- No need to store them manually, they'll be persistently cached automatically just by
304+
-- fetching
305+
_ <- CC.getTermsAndTypesByRefIdsOf codeCache traversed (Set.toList termDeps)
306+
_ <- CC.getTypeDeclsByRefIdsOf codeCache traversed (Set.toList typeDeps)
307+
-- TODO: batchify evalDoc within unison
308+
Doc.evalDoc terms typeOf eval decls tm
296309
where
310+
-- Loading one at a time is inefficient, so we prime the cache above.
297311
terms :: Reference -> m (Maybe (V1.Term Symbol ()))
298-
terms termRef@(Reference.Builtin _) = pure (Just (Term.ref () termRef))
299-
terms (Reference.DerivedId termRef) =
300-
-- TODO: batchify properly
301-
fmap (Term.unannotate . fst) <$> (Codebase.loadTermAndTypeByRefIdsOf codebase id termRef)
312+
terms = CC.termsForRefsOf codeCache id
302313

314+
-- Loading one at a time is inefficient, so we prime the cache above.
303315
typeOf :: Referent.Referent -> m (Maybe (V1.Type Symbol ()))
304-
typeOf termRef =
305-
-- TODO: batchify properly
306-
fmap void <$> Codebase.loadTypesOfReferentsOf codebase id (Cv.referent1to2 termRef)
316+
typeOf = CC.typesOfReferentsOf codeCache id
317+
318+
-- Loading one at a time is inefficient, so we prime the cache above.
319+
decls :: Reference -> m (Maybe (DD.Decl Symbol ()))
320+
decls ref = fmap (DD.amap (const ())) <$> CC.getTypeDeclsByRefsOf codeCache id ref
307321

308322
eval :: V1.Term Symbol a -> m (Maybe (V1.Term Symbol ()))
309-
eval (Term.amap (const mempty) -> tm) = do
323+
eval (Term.amap (const mempty) -> tm) = PG.transactionSpan "eval" mempty do
310324
-- We use an empty ppe for evalutation, it's only used for adding additional context to errors.
311325
let evalPPE = PPE.empty
312326
termRef <- fmap eitherToMaybe . PG.transactionUnsafeIO . liftIO $ Rt.evaluateTerm' codeLookup cachedEvalResult evalPPE unisonRuntime tm
@@ -320,12 +334,6 @@ evalDocRef codebase (CodebaseRuntime {codeLookup, cachedEvalResult, unisonRuntim
320334
_ -> pure ()
321335
pure $ termRef <&> Term.amap (const mempty) . snd
322336

323-
decls :: Reference -> m (Maybe (DD.Decl Symbol ()))
324-
decls (Reference.DerivedId typeRef) =
325-
-- TODO: batchify properly
326-
fmap (DD.amap (const ())) <$> (Codebase.loadTypeDeclarationsByRefIdsOf codebase id typeRef)
327-
decls _ = pure Nothing
328-
329337
-- | Find all definitions and children reachable from the given 'V2Branch.Branch',
330338
lsBranch ::
331339
(QueryM m) =>
@@ -376,7 +384,7 @@ typeDeclHeader ::
376384
m (DisplayObject Syntax.SyntaxText Syntax.SyntaxText)
377385
typeDeclHeader codebase ppe r = case Reference.toId r of
378386
Just rid ->
379-
Codebase.loadTypeDeclarationsByRefIdsOf codebase id rid <&> \case
387+
Codebase.loadV1TypeDeclarationsByRefIdsOf codebase id rid <&> \case
380388
Nothing -> DisplayObject.MissingObject (Reference.toShortHash r)
381389
Just decl ->
382390
DisplayObject.UserObject $

src/Share/BackgroundJobs/Diffs/CausalDiffs.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Share.BackgroundJobs.Errors (reportError)
99
import Share.BackgroundJobs.Monad (Background)
1010
import Share.BackgroundJobs.Workers (newWorker)
1111
import Share.Codebase qualified as Codebase
12+
import Share.Codebase.CodebaseRuntime qualified as CR
1213
import Share.Env qualified as Env
1314
import Share.IDs qualified as IDs
1415
import Share.Metrics qualified as Metrics
@@ -98,8 +99,8 @@ maybeComputeAndStoreCausalDiff authZReceipt unisonRuntime (CausalDiffInfo {fromC
9899
let toCodebase = Codebase.codebaseEnv authZReceipt $ Codebase.codebaseLocationForUserCodebase toCodebaseOwner
99100
ContributionsQ.existsPrecomputedNamespaceDiff (fromCodebase, fromCausalId) (toCodebase, toCausalId) >>= \case
100101
True -> pure False
101-
False -> Codebase.withCodebaseRuntime fromCodebase unisonRuntime \fromRuntime -> do
102-
Codebase.withCodebaseRuntime toCodebase unisonRuntime \toRuntime -> do
102+
False -> CR.withCodebaseRuntime fromCodebase unisonRuntime \fromRuntime -> do
103+
CR.withCodebaseRuntime toCodebase unisonRuntime \toRuntime -> do
103104
_ <-
104105
Diffs.computeAndStoreCausalDiff
105106
authZReceipt

src/Share/BackgroundJobs/Search/DefinitionSync.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ syncTypes codebase namesPerspective rootBranchHashId typesCursor = do
388388
Reference.Builtin _ -> pure (mempty, Arity 0)
389389
Reference.DerivedId refId -> do
390390
-- TODO: batchify this
391-
decl <- lift (Codebase.loadTypeDeclarationsByRefIdsOf codebase id refId) `whenNothingM` throwError (NoDeclForType fqn ref)
391+
decl <- lift (Codebase.loadV1TypeDeclarationsByRefIdsOf codebase id refId) `whenNothingM` throwError (NoDeclForType fqn ref)
392392
pure $ (tokensForDecl refId decl, Arity . fromIntegral . length . DD.bound $ DD.asDataDecl decl)
393393
let basicTokens = Set.fromList [NameToken fqn, HashToken $ Reference.toShortHash ref]
394394
typeSummary <- lift $ Summary.typeSummaryForReference codebase ref (Just fqn) Nothing

src/Share/Codebase.hs

Lines changed: 10 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,22 @@ module Share.Codebase
99
codebaseOwner,
1010
CodebaseRuntime (..),
1111
codebaseEnv,
12-
withCodebaseRuntime,
13-
codebaseRuntimeTransaction,
1412
codebaseForProjectBranch,
1513
codebaseLocationForUserCodebase,
1614
codebaseLocationForProjectBranchCodebase,
1715
codebaseLocationForProjectRelease,
1816
CodebaseLocation (..),
1917

2018
-- * Definitions
21-
loadTermAndTypeByRefIdsOf,
19+
loadV1TermAndTypeByRefIdsOf,
2220
expectTermsByRefIdsOf,
2321
loadTypesOfTermsOf,
2422
expectTypesOfTermsOf,
2523
expectTypesOfReferentsOf,
2624
expectTypesOfConstructorsOf,
2725
loadTypesOfConstructorsOf,
2826
loadTypesOfReferentsOf,
29-
loadTypeDeclarationsByRefIdsOf,
27+
loadV1TypeDeclarationsByRefIdsOf,
3028
expectTypeDeclarationsByRefIdsOf,
3129
loadDeclKindsOf,
3230
expectDeclKindsOf,
@@ -38,7 +36,6 @@ module Share.Codebase
3836
-- * Eval
3937
loadCachedEvalResult,
4038
saveCachedEvalResult,
41-
codeLookupForUser,
4239

4340
-- * Causals
4441
CausalQ.loadCausalNamespace,
@@ -62,7 +59,6 @@ module Share.Codebase
6259
)
6360
where
6461

65-
import Control.Concurrent.STM (TVar, atomically, modifyTVar', newTVarIO, readTVarIO)
6662
import Control.Lens
6763
import Data.ByteString.Lazy.Char8 qualified as BL
6864
import Data.Map qualified as Map
@@ -105,8 +101,6 @@ import U.Codebase.Term qualified as TermV2
105101
import U.Codebase.Term qualified as V2.Term
106102
import Unison.Builtin qualified as Builtin
107103
import Unison.Builtin qualified as Builtins
108-
import Unison.Codebase.CodeLookup qualified as CL
109-
import Unison.Codebase.Runtime (Runtime)
110104
import Unison.Codebase.SqliteCodebase.Conversions qualified as Cv
111105
import Unison.ConstructorType qualified as CT
112106
import Unison.DataDeclaration qualified as DD
@@ -119,14 +113,11 @@ import Unison.Reference qualified as Reference
119113
import Unison.Reference qualified as V1
120114
import Unison.Referent qualified as V1
121115
import Unison.Referent qualified as V1Referent
122-
import Unison.Runtime.IOSource qualified as IOSource
123116
import Unison.ShortHash
124117
import Unison.ShortHash qualified as ShortHash
125118
import Unison.Symbol (Symbol)
126-
import Unison.Term qualified as Term
127119
import Unison.Term qualified as V1
128120
import Unison.Type qualified as V1
129-
import UnliftIO qualified
130121

131122
data CodebaseError
132123
= MissingTypeForTerm Reference.Reference
@@ -170,30 +161,6 @@ codebaseEnv !_authZReceipt codebaseLoc = do
170161
let codebaseOwner = Codebase.codebaseOwnerUserId codebaseLoc
171162
in CodebaseEnv {codebaseOwner}
172163

173-
-- | Construct a Runtime linked to a specific codebase and transaction.
174-
-- Don't use the runtime for one codebase with another codebase.
175-
-- Don't use this runtime in any transaction other than the one where it's created.
176-
withCodebaseRuntime :: (Exception e) => CodebaseEnv -> Runtime Symbol -> (forall s. CodebaseRuntime s IO -> PG.Transaction e r) -> PG.Transaction e r
177-
withCodebaseRuntime codebaseEnv sandboxedRuntime f = do
178-
rt <- PG.transactionUnsafeIO (codebaseRuntimeTransaction sandboxedRuntime codebaseEnv)
179-
PG.asUnliftIOTransaction $ do
180-
UnliftIO.withRunInIO \toIO -> do
181-
toIO . PG.UnliftIOTransaction $ f $ hoistCodebaseRuntime (toIO . PG.UnliftIOTransaction) rt
182-
183-
-- | Ideally, we'd use this – a runtime with lookup actions in transaction, not IO. But that will require refactoring to
184-
-- the runtime interface in ucm, so we can't use it for now. That's bad: we end up unsafely running separate
185-
-- transactions for inner calls to 'codeLookup' / 'cachedEvalResult', which can lead to deadlock due to a starved
186-
-- connection pool.
187-
codebaseRuntimeTransaction :: Runtime Symbol -> CodebaseEnv -> IO (CodebaseRuntime s (PG.Transaction e))
188-
codebaseRuntimeTransaction unisonRuntime codebase = do
189-
cacheVar <- newTVarIO (CodeLookupCache mempty mempty)
190-
pure
191-
CodebaseRuntime
192-
{ codeLookup = codeLookupForUser cacheVar codebase,
193-
cachedEvalResult = (fmap . fmap) Term.unannotate . loadCachedEvalResult codebase,
194-
unisonRuntime
195-
}
196-
197164
-- | Wrap a response in caching.
198165
-- This combinator respects the cachability stored on the provided auth receipt.
199166
cachedCodebaseResponse ::
@@ -218,8 +185,8 @@ cachedCodebaseResponse authzReceipt codebaseOwner endpointName providedCachePara
218185
codebaseViewCacheKey = IDs.toText (codebaseOwnerUserId codebaseOwner)
219186

220187
-- | Load terms and type.
221-
loadTermAndTypeByRefIdsOf :: (QueryM m) => CodebaseEnv -> Traversal s t TermReferenceId (Maybe (V1.Term Symbol Ann, V1.Type Symbol Ann)) -> s -> m t
222-
loadTermAndTypeByRefIdsOf codebase trav s = do
188+
loadV1TermAndTypeByRefIdsOf :: (QueryM m) => CodebaseEnv -> Traversal s t TermReferenceId (Maybe (V1.Term Symbol Ann, V1.Type Symbol Ann)) -> s -> m t
189+
loadV1TermAndTypeByRefIdsOf codebase trav s = do
223190
s
224191
& asListOf trav %%~ \refs -> do
225192
let hashes = refs <&> \(Reference.Id h _) -> h
@@ -257,7 +224,7 @@ convertTerms2to1Of trav s = do
257224
expectTermsByRefIdsOf :: (QueryM m) => CodebaseEnv -> Traversal s t TermReferenceId (V1.Term Symbol Ann, V1.Type Symbol Ann) -> s -> m t
258225
expectTermsByRefIdsOf codebase trav s = do
259226
s & asListOfDeduped trav \refIds -> do
260-
termsAndTypes <- loadTermAndTypeByRefIdsOf codebase traversed refIds
227+
termsAndTypes <- loadV1TermAndTypeByRefIdsOf codebase traversed refIds
261228
for (zip refIds termsAndTypes) \case
262229
(refId, Nothing) -> unrecoverableError (MissingTerm refId)
263230
(_, Just (term, typ)) -> pure (term, typ)
@@ -277,7 +244,7 @@ loadTypesOfTermsOf codebase trav s =
277244
<&> \typ ->
278245
(typ $> builtinAnnotation)
279246
in Left builtinType
280-
results <- loadTermAndTypeByRefIdsOf codebase (traversed . _Right) partitionedRefs
247+
results <- loadV1TermAndTypeByRefIdsOf codebase (traversed . _Right) partitionedRefs
281248
pure $
282249
results <&> \case
283250
Left builtin -> builtin
@@ -329,8 +296,8 @@ loadDeclKindsOf trav s =
329296
& DefnQ.loadDeclKindsOf (traversed . _Right)
330297
pure (fmap (either id id) xs)
331298

332-
loadTypeDeclarationsByRefIdsOf :: (QueryM m) => CodebaseEnv -> Traversal s t Reference.Id (Maybe (V1.Decl Symbol Ann)) -> s -> m t
333-
loadTypeDeclarationsByRefIdsOf codebase trav s =
299+
loadV1TypeDeclarationsByRefIdsOf :: (QueryM m) => CodebaseEnv -> Traversal s t Reference.Id (Maybe (V1.Decl Symbol Ann)) -> s -> m t
300+
loadV1TypeDeclarationsByRefIdsOf codebase trav s =
334301
s
335302
& asListOf trav %%~ \refIds -> do
336303
DefnQ.loadDeclsByRefIdsOf codebase traversed refIds
@@ -343,7 +310,7 @@ expectTypeDeclarationsByRefIdsOf :: (QueryM m) => CodebaseEnv -> Traversal s t R
343310
expectTypeDeclarationsByRefIdsOf codebase trav s = do
344311
s
345312
& asListOf trav %%~ \refIds -> do
346-
results <- loadTypeDeclarationsByRefIdsOf codebase traversed refIds
313+
results <- loadV1TypeDeclarationsByRefIdsOf codebase traversed refIds
347314
for (zip refIds results) \case
348315
(refId, Nothing) -> unrecoverableError (MissingDecl refId)
349316
(_, Just decl) -> pure decl
@@ -398,7 +365,7 @@ loadTypesOfConstructorsOf codebase trav s =
398365
refs <&> \case
399366
(Reference.Builtin _t, _conId) -> Left Nothing
400367
(Reference.DerivedId refId, conId) -> Right ((refId, conId), refId)
401-
results <- loadTypeDeclarationsByRefIdsOf codebase (traversed . _Right . _2) partitionedRefs
368+
results <- loadV1TypeDeclarationsByRefIdsOf codebase (traversed . _Right . _2) partitionedRefs
402369
for results \case
403370
(Right ((refId, _conId), Nothing)) -> (unrecoverableError (MissingDecl refId))
404371
(Right ((_refId, conId), (Just decl))) -> do
@@ -414,49 +381,6 @@ expectTypesOfConstructorsOf codebase trav s =
414381
((ref, conId), Nothing) -> unrecoverableError (MissingTypeForConstructor ref conId)
415382
(_, Just r) -> pure r
416383

417-
data CodeLookupCache = CodeLookupCache
418-
{ termCache :: Map Reference.Id (V1.Term Symbol Ann, V1.Type Symbol Ann),
419-
typeCache :: Map Reference.Id (V1.Decl Symbol Ann)
420-
}
421-
422-
-- | TODO: The code lookup should either be batchified or we should preload the cache with
423-
-- everything we think we'll need.
424-
codeLookupForUser :: TVar CodeLookupCache -> CodebaseEnv -> CL.CodeLookup Symbol (PG.Transaction e) Ann
425-
codeLookupForUser cacheVar codebaseOwner = do
426-
CL.CodeLookup (fmap (fmap fst) . getTermAndType) (fmap (fmap snd) . getTermAndType) getTypeDecl
427-
<> Builtin.codeLookup
428-
<> IOSource.codeLookupM
429-
where
430-
getTermAndType ::
431-
Reference.Id ->
432-
PG.Transaction e (Maybe (V1.Term Symbol Ann, V1.Type Symbol Ann))
433-
getTermAndType r = do
434-
CodeLookupCache {termCache} <- PG.transactionUnsafeIO (readTVarIO cacheVar)
435-
case Map.lookup r termCache of
436-
Just termAndType -> pure (Just termAndType)
437-
Nothing -> do
438-
maybeTermAndType <- loadTermAndTypeByRefIdsOf codebaseOwner id r
439-
whenJust maybeTermAndType \termAndType -> do
440-
PG.transactionUnsafeIO do
441-
atomically do
442-
modifyTVar' cacheVar \CodeLookupCache {termCache, ..} ->
443-
CodeLookupCache {termCache = Map.insert r termAndType termCache, ..}
444-
pure maybeTermAndType
445-
446-
getTypeDecl :: Reference.Id -> PG.Transaction e (Maybe (V1.Decl Symbol Ann))
447-
getTypeDecl r = do
448-
CodeLookupCache {typeCache} <- PG.transactionUnsafeIO (readTVarIO cacheVar)
449-
case Map.lookup r typeCache of
450-
Just typ -> pure (Just typ)
451-
Nothing -> do
452-
maybeType <- loadTypeDeclarationsByRefIdsOf codebaseOwner id r
453-
whenJust maybeType \typ ->
454-
PG.transactionUnsafeIO do
455-
atomically do
456-
modifyTVar' cacheVar \CodeLookupCache {typeCache, ..} ->
457-
CodeLookupCache {typeCache = Map.insert r typ typeCache, ..}
458-
pure maybeType
459-
460384
-- | Look up the result of evaluating a term if we have it cached.
461385
--
462386
-- This is intentionally not in CodebaseM because it's used to build the CodebaseEnv.

0 commit comments

Comments
 (0)