From 8e700606bb64c4ffda87cec333f7c76eae244911 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Wed, 26 May 2021 23:36:03 +0200 Subject: Add a cache for unchanged layouts Co-Authored-By: Laurenz --- src/cache.rs | 34 ++++++++++++++++++++++++++++++++++ src/layout/mod.rs | 22 +++++++++++++++++++--- src/lib.rs | 5 ++++- src/main.rs | 6 ++++-- 4 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 src/cache.rs (limited to 'src') diff --git a/src/cache.rs b/src/cache.rs new file mode 100644 index 00000000..4cf97ba6 --- /dev/null +++ b/src/cache.rs @@ -0,0 +1,34 @@ +//! Caching for incremental compilation. + +use std::collections::HashMap; + +use crate::layout::{Frame, Regions}; + +/// A cache for incremental compilation. +#[derive(Default, Debug, Clone)] +pub struct Cache { + /// A map that holds the layouted nodes from past compilations. + pub frames: HashMap, +} + +impl Cache { + /// Create a new, empty cache. + pub fn new() -> Self { + Self::default() + } + + /// Clear the cache. + pub fn clear(&mut self) { + self.frames.clear(); + } +} + +/// Frames from past compilations and checks for their validity in future +/// compilations. +#[derive(Debug, Clone)] +pub struct FramesEntry { + /// The regions in which these frames are valid. + pub regions: Regions, + /// Cached frames for a node. + pub frames: Vec, +} diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 207d5bed..51b9bc64 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -23,12 +23,13 @@ use std::hash::{Hash, Hasher}; use decorum::NotNan; use fxhash::FxHasher64; +use crate::cache::{Cache, FramesEntry}; use crate::env::Env; use crate::geom::*; /// Layout a tree into a collection of frames. -pub fn layout(env: &mut Env, tree: &Tree) -> Vec { - tree.layout(&mut LayoutContext { env }) +pub fn layout(env: &mut Env, cache: &mut Cache, tree: &Tree) -> Vec { + tree.layout(&mut LayoutContext { env, cache }) } /// A tree of layout nodes. @@ -96,7 +97,19 @@ impl AnyNode { impl Layout for AnyNode { fn layout(&self, ctx: &mut LayoutContext, regions: &Regions) -> Vec { - self.node.layout(ctx, regions) + if let Some(hit) = ctx.cache.frames.get(&self.hash()) { + if &hit.regions == regions { + return hit.frames.clone(); + } + } + + let frames = self.node.layout(ctx, regions); + ctx.cache.frames.insert(self.hash(), FramesEntry { + regions: regions.clone(), + frames: frames.clone(), + }); + + frames } } @@ -164,6 +177,9 @@ pub trait Layout { pub struct LayoutContext<'a> { /// The environment from which fonts are gathered. pub env: &'a mut Env, + /// A cache which enables reuse of layout artifacts from past compilation + /// cycles. + pub cache: &'a mut Cache, } /// A sequence of regions to layout into. diff --git a/src/lib.rs b/src/lib.rs index 2802c386..8742aeb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ pub mod diag; #[macro_use] pub mod eval; +pub mod cache; pub mod color; pub mod env; pub mod exec; @@ -46,6 +47,7 @@ pub mod pretty; pub mod syntax; pub mod util; +use crate::cache::Cache; use crate::diag::Pass; use crate::env::Env; use crate::eval::Scope; @@ -55,6 +57,7 @@ use crate::layout::Frame; /// Process source code directly into a collection of frames. pub fn typeset( env: &mut Env, + cache: &mut Cache, src: &str, scope: &Scope, state: State, @@ -62,7 +65,7 @@ pub fn typeset( let parsed = parse::parse(src); let evaluated = eval::eval(env, &parsed.output, scope); let executed = exec::exec(env, &parsed.output, &evaluated.output, state); - let frames = layout::layout(env, &executed.output); + let frames = layout::layout(env, cache, &executed.output); let mut diags = parsed.diags; diags.extend(evaluated.diags); diff --git a/src/main.rs b/src/main.rs index 9a112d5a..aef0f573 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, bail, Context}; +use typst::cache::Cache; use typst::diag::Pass; use typst::env::{Env, FsLoader}; use typst::exec::State; @@ -39,11 +40,12 @@ fn main() -> anyhow::Result<()> { loader.search_system(); let mut env = Env::new(loader); - + let mut cache = Cache::new(); let scope = library::new(); let state = State::default(); - let Pass { output: frames, diags } = typeset(&mut env, &src, &scope, state); + let Pass { output: frames, diags } = + typeset(&mut env, &mut cache, &src, &scope, state); if !diags.is_empty() { let map = LineMap::new(&src); for diag in diags { -- cgit v1.2.3