Skip to content

Commit

Permalink
Support sharing ModuleInfoCache between pipeline runs (#2753)
Browse files Browse the repository at this point in the history
This PR delays running of the pipeline `MCache` (renamed to
`ModuleInfoCache`) to allow this cache to be shared between pipeline
runs in `processRecursiveUpToTyped`. `processRecursiveUpToTyped` is used
by the `juvix html` command to recursively build HTML for modules in a
project.

* Closes #2744

## Performance

The docs build is now much faster and takes much less memory:

Before:

```
$ /usr/bin/time -lh juvix html docs/index.juvix.md	
1m17.41s real		35.39s user		11.42s sys
          5918703616  maximum resident set size
                   0  average shared memory size
                   0  average unshared data size
                   0  average unshared stack size
             3665213  page reclaims
                 697  page faults
                   0  swaps
                   0  block input operations
                   0  block output operations
                   0  messages sent
                   0  messages received
                   0  signals received
              114533  voluntary context switches
               81450  involuntary context switches
        595152097097  instructions retired
        143688878963  cycles elapsed
         19323983744  peak memory footprint
```

After:

```
$ /usr/bin/time -lh juvix html docs/index.juvix.md
	8.35s real		5.76s user		0.62s sys
          2992160768  maximum resident set size
                   0  average shared memory size
                   0  average unshared data size
                   0  average unshared stack size
              221870  page reclaims
                 719  page faults
                   0  swaps
                   0  block input operations
                   0  block output operations
                   0  messages sent
                   0  messages received
                   0  signals received
                1965  voluntary context switches
                1962  involuntary context switches
         93909891240  instructions retired
         19317129226  cycles elapsed
          2963053632  peak memory footprint
```

## Notes

* `MCache` is renamed to `ModuleInfoCache`

* `ModuleInfoCache` must be defined in a separate module instead of
being defined in `Compiler.Pipeline.Driver` to avoid a cyclic
dependency. `ModuleInfoCache` is now used used in
`Juvix.Compiler.Pipeline` (in `PipelineEff`) and this module is imported
by `Juvix.Compiler.Pipeline.Driver`).
  • Loading branch information
paulcadman authored Apr 25, 2024
1 parent 778b626 commit 844f302
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 46 deletions.
4 changes: 3 additions & 1 deletion src/Juvix/Compiler/Pipeline.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.Termination.Che
import Juvix.Compiler.Nockma.Translation.FromTree qualified as NockmaTree
import Juvix.Compiler.Pipeline.Artifacts
import Juvix.Compiler.Pipeline.EntryPoint
import Juvix.Compiler.Pipeline.ImportParents
import Juvix.Compiler.Pipeline.Loader.PathResolver.Base
import Juvix.Compiler.Pipeline.Loader.PathResolver.Error
import Juvix.Compiler.Pipeline.ModuleInfoCache
import Juvix.Compiler.Pipeline.Package.Loader.Error
import Juvix.Compiler.Pipeline.Package.Loader.EvalEff
import Juvix.Compiler.Pipeline.Result
Expand All @@ -49,7 +51,7 @@ import Juvix.Data.Field

type PipelineAppEffects = '[TaggedLock, EmbedIO]

type PipelineLocalEff = '[PathResolver, EvalFileEff, Error PackageLoaderError, Error DependencyError, GitClone, Error GitProcessError, Process, Log, Reader EntryPoint, Files, Error JuvixError, HighlightBuilder, Internet]
type PipelineLocalEff = '[ModuleInfoCache, Reader ImportParents, PathResolver, EvalFileEff, Error PackageLoaderError, Error DependencyError, GitClone, Error GitProcessError, Process, Log, Reader EntryPoint, Files, Error JuvixError, HighlightBuilder, Internet]

type PipelineEff' r = PipelineLocalEff ++ r

Expand Down
71 changes: 28 additions & 43 deletions src/Juvix/Compiler/Pipeline/Driver.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module Juvix.Compiler.Pipeline.Driver
processFileToStoredCore,
processImport,
processRecursiveUpToTyped,
ModuleInfoCache,
evalModuleInfoCache,
)
where

Expand All @@ -21,7 +23,9 @@ import Juvix.Compiler.Internal.Translation.FromConcrete.Data.Context qualified a
import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.TypeChecking.Data.Context qualified as InternalTyped
import Juvix.Compiler.Internal.Translation.FromInternal.Data (InternalTypedResult)
import Juvix.Compiler.Pipeline
import Juvix.Compiler.Pipeline.ImportParents
import Juvix.Compiler.Pipeline.Loader.PathResolver
import Juvix.Compiler.Pipeline.ModuleInfoCache
import Juvix.Compiler.Store.Core.Extra
import Juvix.Compiler.Store.Extra qualified as Store
import Juvix.Compiler.Store.Language qualified as Store
Expand All @@ -34,63 +38,44 @@ import Juvix.Extra.Serialize
import Juvix.Prelude
import Path.Posix qualified as Path

newtype ImportParents = ImportParents
{ _importParents :: [TopModulePath]
}
deriving newtype (Semigroup, Monoid)

makeLenses ''ImportParents

newtype EntryIndex = EntryIndex
{ _entryIxEntry :: EntryPoint
}

makeLenses ''EntryIndex

instance Eq EntryIndex where
(==) = (==) `on` (^. entryIxEntry . entryPointModulePath)

instance Hashable EntryIndex where
hashWithSalt s = hashWithSalt s . (^. entryIxEntry . entryPointModulePath)

type MCache' a = Cache EntryIndex a

type MCache = MCache' (PipelineResult Store.ModuleInfo)
evalModuleInfoCache ::
forall r a.
(Members '[TaggedLock, Error JuvixError, Files, PathResolver] r) =>
Sem (ModuleInfoCache ': Reader ImportParents ': r) a ->
Sem r a
evalModuleInfoCache = runReader @ImportParents mempty . evalCacheEmpty processModule'

processFileUpToParsing ::
forall r.
(Members '[TaggedLock, HighlightBuilder, Error JuvixError, Files, PathResolver] r) =>
(Members '[TaggedLock, HighlightBuilder, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
EntryPoint ->
Sem r (PipelineResult Parser.ParserResult)
processFileUpToParsing entry =
runReader @ImportParents mempty
. evalCacheEmpty processModule'
$ processFileUpToParsing' entry
runReader @ImportParents mempty $
processFileUpToParsing' entry

processImport ::
forall r.
(Members '[TaggedLock, Error JuvixError, Files, PathResolver] r) =>
(Members '[TaggedLock, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
EntryPoint ->
Import 'Parsed ->
Sem r (PipelineResult Store.ModuleInfo)
processImport entry i =
runReader @ImportParents mempty
. evalCacheEmpty processModule'
$ processImport' entry (i ^. importModulePath)
runReader @ImportParents mempty $
processImport' entry (i ^. importModulePath)

processFileToStoredCore ::
forall r.
(Members '[TaggedLock, Error JuvixError, Files, PathResolver] r) =>
(Members '[TaggedLock, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
EntryPoint ->
Sem r (PipelineResult Core.CoreResult)
processFileToStoredCore entry =
runReader @ImportParents mempty
. evalCacheEmpty processModule'
$ processFileToStoredCore' entry
runReader @ImportParents mempty $
processFileToStoredCore' entry

processFileUpTo ::
forall r a.
(Members '[TaggedLock, HighlightBuilder, Reader EntryPoint, Error JuvixError, Files, PathResolver] r) =>
(Members '[TaggedLock, HighlightBuilder, Reader EntryPoint, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
Sem (Reader Parser.ParserResult ': Reader Store.ModuleTable ': NameIdGen ': r) a ->
Sem r (PipelineResult a)
processFileUpTo a = do
Expand All @@ -106,7 +91,7 @@ processFileUpTo a = do

processFileUpToParsing' ::
forall r.
(Members '[HighlightBuilder, Reader ImportParents, Error JuvixError, Files, PathResolver, MCache] r) =>
(Members '[HighlightBuilder, Reader ImportParents, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
EntryPoint ->
Sem r (PipelineResult Parser.ParserResult)
processFileUpToParsing' entry = do
Expand All @@ -122,15 +107,15 @@ processFileUpToParsing' entry = do

processImports' ::
forall r.
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, MCache] r) =>
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
EntryPoint ->
[TopModulePath] ->
Sem r Store.ModuleTable
processImports' entry imports = snd <$> processImports'' entry imports

processImports'' ::
forall r.
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, MCache] r) =>
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
EntryPoint ->
[TopModulePath] ->
Sem r (Bool, Store.ModuleTable)
Expand All @@ -144,7 +129,7 @@ processImports'' entry imports = do

processImport' ::
forall r a.
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, MCache' a] r) =>
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, ModuleInfoCache' a] r) =>
EntryPoint ->
TopModulePath ->
Sem r a
Expand Down Expand Up @@ -174,7 +159,7 @@ processImport' entry p = do

processFileToStoredCore' ::
forall r.
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, MCache] r) =>
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
EntryPoint ->
Sem r (PipelineResult Core.CoreResult)
processFileToStoredCore' entry = ignoreHighlightBuilder $ do
Expand All @@ -189,7 +174,7 @@ processFileToStoredCore' entry = ignoreHighlightBuilder $ do

processModule' ::
forall r.
(Members '[TaggedLock, Reader ImportParents, Error JuvixError, Files, PathResolver, MCache] r) =>
(Members '[TaggedLock, Reader ImportParents, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
EntryIndex ->
Sem r (PipelineResult Store.ModuleInfo)
processModule' (EntryIndex entry) = do
Expand Down Expand Up @@ -237,7 +222,7 @@ processModule' (EntryIndex entry) = do

processModule'' ::
forall r.
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, MCache] r) =>
(Members '[Reader ImportParents, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
Text ->
EntryPoint ->
Sem r (PipelineResult Store.ModuleInfo)
Expand All @@ -260,7 +245,7 @@ processModule'' sha256 entry = over pipelineResult mkModuleInfo <$> processFileT

processRecursiveUpToTyped ::
forall r.
(Members '[Reader EntryPoint, TaggedLock, HighlightBuilder, Error JuvixError, Files, PathResolver] r) =>
(Members '[Reader EntryPoint, TaggedLock, HighlightBuilder, Error JuvixError, Files, PathResolver, ModuleInfoCache] r) =>
Sem r (InternalTypedResult, [InternalTypedResult])
processRecursiveUpToTyped = do
entry <- ask
Expand Down
11 changes: 11 additions & 0 deletions src/Juvix/Compiler/Pipeline/ImportParents.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Juvix.Compiler.Pipeline.ImportParents where

import Juvix.Compiler.Concrete.Data
import Juvix.Prelude.Base

newtype ImportParents = ImportParents
{ _importParents :: [TopModulePath]
}
deriving newtype (Semigroup, Monoid)

makeLenses ''ImportParents
23 changes: 23 additions & 0 deletions src/Juvix/Compiler/Pipeline/ModuleInfoCache.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Juvix.Compiler.Pipeline.ModuleInfoCache where

import Juvix.Compiler.Pipeline.EntryPoint
import Juvix.Compiler.Pipeline.Result
import Juvix.Compiler.Store.Language qualified as Store
import Juvix.Data.Effect.Cache
import Juvix.Prelude.Base

newtype EntryIndex = EntryIndex
{ _entryIxEntry :: EntryPoint
}

makeLenses ''EntryIndex

instance Eq EntryIndex where
(==) = (==) `on` (^. entryIxEntry . entryPointModulePath)

instance Hashable EntryIndex where
hashWithSalt s = hashWithSalt s . (^. entryIxEntry . entryPointModulePath)

type ModuleInfoCache' a = Cache EntryIndex a

type ModuleInfoCache = ModuleInfoCache' (PipelineResult Store.ModuleInfo)
3 changes: 2 additions & 1 deletion src/Juvix/Compiler/Pipeline/Package/Loader/EvalEff/IO.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Juvix.Compiler.Core.Evaluator
import Juvix.Compiler.Core.Extra.Value
import Juvix.Compiler.Core.Language
import Juvix.Compiler.Pipeline
import Juvix.Compiler.Pipeline.Driver (processFileToStoredCore)
import Juvix.Compiler.Pipeline.Driver (evalModuleInfoCache, processFileToStoredCore)
import Juvix.Compiler.Pipeline.Package.Loader.Error
import Juvix.Compiler.Pipeline.Package.Loader.EvalEff
import Juvix.Compiler.Pipeline.Package.Loader.PathResolver
Expand Down Expand Up @@ -139,6 +139,7 @@ loadPackage' packagePath = do
. mapError (JuvixError @GitProcessError)
. runGitProcess
. runPackagePathResolver rootPath
. evalModuleInfoCache
$ (^. pipelineResult) <$> processFileToStoredCore packageEntryPoint
)
where
Expand Down
5 changes: 4 additions & 1 deletion src/Juvix/Compiler/Pipeline/Repl.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ import Juvix.Compiler.Internal qualified as Internal
import Juvix.Compiler.Internal.Translation.FromInternal.Analysis.Termination.Checker
import Juvix.Compiler.Pipeline.Artifacts
import Juvix.Compiler.Pipeline.Artifacts.PathResolver
import Juvix.Compiler.Pipeline.Driver (evalModuleInfoCache)
import Juvix.Compiler.Pipeline.Driver qualified as Driver
import Juvix.Compiler.Pipeline.EntryPoint
import Juvix.Compiler.Pipeline.Loader.PathResolver.Base
import Juvix.Compiler.Pipeline.Loader.PathResolver.Error
import Juvix.Compiler.Pipeline.ModuleInfoCache
import Juvix.Compiler.Pipeline.Package.Loader.Error
import Juvix.Compiler.Pipeline.Package.Loader.EvalEff.IO
import Juvix.Compiler.Pipeline.Result
Expand Down Expand Up @@ -111,7 +113,7 @@ compileExpression p =
>>= fromInternalExpression

registerImport ::
(Members '[TaggedLock, Error JuvixError, State Artifacts, Reader EntryPoint, Files, GitClone, PathResolver] r) =>
(Members '[TaggedLock, Error JuvixError, State Artifacts, Reader EntryPoint, Files, GitClone, PathResolver, ModuleInfoCache] r) =>
Import 'Parsed ->
Sem r ()
registerImport i = do
Expand Down Expand Up @@ -163,6 +165,7 @@ compileReplInputIO fp txt = do
. mapError (JuvixError @PackageLoaderError)
. runEvalFileEffIO
. runPathResolverArtifacts
. evalModuleInfoCache
$ do
p <- parseReplInput fp txt
case p of
Expand Down
2 changes: 2 additions & 0 deletions src/Juvix/Compiler/Pipeline/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ runIOEitherPipeline' entry a = do
. mapError (JuvixError @PackageLoaderError)
. runEvalFileEffIO
. runPathResolver'
. evalModuleInfoCache
$ a

mainIsPackageFile :: EntryPoint -> Bool
Expand Down Expand Up @@ -150,6 +151,7 @@ runReplPipelineIOEither' lockMode entry = do
. mapError (JuvixError @PackageLoaderError)
. runEvalFileEffIO
. runPathResolver'
. evalModuleInfoCache
$ entrySetup defaultDependenciesConfig >> processFileToStoredCore entry
return $ case eith of
Left err -> Left err
Expand Down

0 comments on commit 844f302

Please sign in to comment.