summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin <mhaug@live.de>2021-08-18 18:12:26 +0200
committerGitHub <noreply@github.com>2021-08-18 18:12:26 +0200
commitc44ecbfbd2706bf8e09728f2c85135aa2299d542 (patch)
tree1a7fe1b7f1627ed63817030468476ef4a5dc2b9e
parent011865ab5c8943abcb64c7b545e265d1a65db32a (diff)
Move to exclusively oneshot benchmarks with Iai fork (#41)
-rw-r--r--Cargo.toml9
-rw-r--r--bench/Cargo.toml25
-rw-r--r--bench/src/clock.rs129
-rw-r--r--bench/src/parsing.rs38
-rw-r--r--benches/bench.typ45
-rw-r--r--benches/oneshot.rs82
-rw-r--r--src/lib.rs28
7 files changed, 157 insertions, 199 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 5f7f28f6..616ad9f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,9 +10,6 @@ cli = ["anyhow", "codespan-reporting", "fs", "same-file"]
fs = ["dirs", "memmap2", "same-file", "walkdir"]
layout-cache = []
-[workspace]
-members = ["bench"]
-
[profile.dev]
# Faster compilation
debug = 0
@@ -44,6 +41,7 @@ walkdir = { version = "2", optional = true }
walkdir = "2"
tiny-skia = "0.5"
usvg = { version = "0.15", default-features = false }
+iai = { git = "https://github.com/reknih/iai" }
[[bin]]
name = "typst"
@@ -53,3 +51,8 @@ required-features = ["cli"]
name = "typeset"
required-features = ["fs"]
harness = false
+
+[[bench]]
+name = "oneshot"
+path = "benches/oneshot.rs"
+harness = false
diff --git a/bench/Cargo.toml b/bench/Cargo.toml
deleted file mode 100644
index eec8a96e..00000000
--- a/bench/Cargo.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[package]
-name = "typst-bench"
-version = "0.0.1"
-authors = ["The Typst Project Developers"]
-edition = "2018"
-publish = false
-
-[features]
-default = ["layout-cache"]
-layout-cache = ["typst/layout-cache"]
-
-[dev-dependencies]
-criterion = { version = "0.3", features = ["html_reports"] }
-iai = "0.1"
-typst = { path = "..", default-features = false, features = ["fs"] }
-
-[[bench]]
-name = "clock"
-path = "src/clock.rs"
-harness = false
-
-[[bench]]
-name = "parsing"
-path = "src/parsing.rs"
-harness = false
diff --git a/bench/src/clock.rs b/bench/src/clock.rs
deleted file mode 100644
index 22f1b6df..00000000
--- a/bench/src/clock.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-use std::cell::RefCell;
-use std::path::Path;
-use std::rc::Rc;
-
-use criterion::{criterion_group, criterion_main, Criterion};
-
-use typst::diag::TypResult;
-use typst::eval::{eval, Module, State};
-use typst::export::pdf;
-use typst::layout::{layout, Frame, LayoutTree};
-use typst::loading::FsLoader;
-use typst::parse::parse;
-use typst::source::SourceId;
-use typst::syntax::SyntaxTree;
-use typst::Context;
-
-const FONT_DIR: &str = "../fonts";
-const TYP_DIR: &str = "../tests/typ";
-const CASES: &[&str] = &["coma.typ", "text/basic.typ"];
-
-fn benchmarks(c: &mut Criterion) {
- let loader = FsLoader::new().with_path(FONT_DIR).wrap();
- let ctx = Rc::new(RefCell::new(Context::new(loader)));
-
- for case in CASES {
- let path = Path::new(TYP_DIR).join(case);
- let name = path.file_stem().unwrap().to_string_lossy();
- let id = ctx.borrow_mut().sources.load(&path).unwrap();
- let case = Case::new(ctx.clone(), id);
-
- macro_rules! bench {
- ($step:literal, setup: |$ctx:ident| $setup:expr, code: $code:expr $(,)?) => {
- c.bench_function(&format!("{}-{}", $step, name), |b| {
- b.iter_batched(
- || {
- let mut $ctx = ctx.borrow_mut();
- $setup
- },
- |_| $code,
- criterion::BatchSize::PerIteration,
- )
- });
- };
- ($step:literal, $code:expr) => {
- c.bench_function(&format!("{}-{}", $step, name), |b| b.iter(|| $code));
- };
- }
-
- bench!("parse", case.parse());
- bench!("eval", case.eval());
- bench!("build", case.build());
-
- #[cfg(not(feature = "layout-cache"))]
- {
- bench!("layout", case.layout());
- bench!("typeset", case.typeset());
- }
-
- #[cfg(feature = "layout-cache")]
- {
- bench!("layout", setup: |ctx| ctx.layouts.clear(), code: case.layout());
- bench!("typeset", setup: |ctx| ctx.layouts.clear(), code: case.typeset());
- bench!("layout-cached", case.layout());
- bench!("typeset-cached", case.typeset());
- }
-
- bench!("pdf", case.pdf());
- }
-}
-
-/// A test case with prepared intermediate results.
-struct Case {
- ctx: Rc<RefCell<Context>>,
- state: State,
- id: SourceId,
- ast: SyntaxTree,
- module: Module,
- tree: LayoutTree,
- frames: Vec<Rc<Frame>>,
-}
-
-impl Case {
- fn new(ctx: Rc<RefCell<Context>>, id: SourceId) -> Self {
- let mut borrowed = ctx.borrow_mut();
- let state = State::default();
- let source = borrowed.sources.get(id);
- let ast = parse(source).unwrap();
- let module = eval(&mut borrowed, id, &ast).unwrap();
- let tree = module.template.to_tree(&state);
- let frames = layout(&mut borrowed, &tree);
- drop(borrowed);
- Self {
- ctx,
- state,
- id,
- ast,
- module,
- tree,
- frames,
- }
- }
-
- fn parse(&self) -> SyntaxTree {
- parse(self.ctx.borrow().sources.get(self.id)).unwrap()
- }
-
- fn eval(&self) -> TypResult<Module> {
- eval(&mut self.ctx.borrow_mut(), self.id, &self.ast)
- }
-
- fn build(&self) -> LayoutTree {
- self.module.template.to_tree(&self.state)
- }
-
- fn layout(&self) -> Vec<Rc<Frame>> {
- layout(&mut self.ctx.borrow_mut(), &self.tree)
- }
-
- fn typeset(&self) -> TypResult<Vec<Rc<Frame>>> {
- self.ctx.borrow_mut().typeset(self.id)
- }
-
- fn pdf(&self) -> Vec<u8> {
- pdf(&self.ctx.borrow(), &self.frames)
- }
-}
-
-criterion_group!(benches, benchmarks);
-criterion_main!(benches);
diff --git a/bench/src/parsing.rs b/bench/src/parsing.rs
deleted file mode 100644
index f95dfe75..00000000
--- a/bench/src/parsing.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use iai::{black_box, main};
-
-use typst::diag::TypResult;
-use typst::parse::{parse, Scanner, TokenMode, Tokens};
-use typst::source::SourceFile;
-use typst::syntax::SyntaxTree;
-
-const SRC: &str = include_str!("../../tests/typ/coma.typ");
-
-fn bench_decode() -> usize {
- // We don't use chars().count() because that has a special
- // superfast implementation.
- let mut count = 0;
- let mut chars = black_box(SRC).chars();
- while let Some(_) = chars.next() {
- count += 1;
- }
- count
-}
-
-fn bench_scan() -> usize {
- let mut count = 0;
- let mut scanner = Scanner::new(black_box(SRC));
- while let Some(_) = scanner.eat() {
- count += 1;
- }
- count
-}
-
-fn bench_tokenize() -> usize {
- Tokens::new(black_box(SRC), black_box(TokenMode::Markup)).count()
-}
-
-fn bench_parse() -> TypResult<SyntaxTree> {
- parse(&SourceFile::detached(black_box(SRC)))
-}
-
-main!(bench_decode, bench_scan, bench_tokenize, bench_parse);
diff --git a/benches/bench.typ b/benches/bench.typ
new file mode 100644
index 00000000..f290844b
--- /dev/null
+++ b/benches/bench.typ
@@ -0,0 +1,45 @@
+// Configuration with `page` and `font` functions.
+#page(width: 450pt, margins: 1cm)
+
+// There are variables and they can take normal values like strings, ...
+#let city = "Berlin"
+
+// ... but also "template" values. While these contain markup,
+// they are also values and can be summed, stored in arrays etc.
+// There are also more standard control flow structures, like #if and #for.
+#let university = [*Technische Universität {city}*]
+#let faculty = [*Fakultät II, Institut for Mathematik*]
+
+// The `box` function just places content into a rectangular container. When
+// the only argument to a function is a template, the parentheses can be omitted
+// (i.e. `f[a]` is the same as `f([a])`).
+#box[
+ // Backslash adds a forced line break.
+ #university \
+ #faculty \
+ Sekretariat MA \
+ Dr. Max Mustermann \
+ Ola Nordmann, John Doe
+]
+#align(right, box[*WiSe 2019/2020* \ Woche 3])
+
+// Adds vertical spacing.
+#v(6mm)
+
+// If the last argument to a function is a template, we can also place it behind
+// the parentheses.
+#align(center)[
+ // Markdown-like syntax for headings.
+ ==== 3. Übungsblatt Computerorientierte Mathematik II #v(4mm)
+ *Abgabe: 03.05.2019* (bis 10:10 Uhr in MA 001) #v(4mm)
+ *Alle Antworten sind zu beweisen.*
+]
+
+*1. Aufgabe* #align(right)[(1 + 1 + 2 Punkte)]
+
+Ein _Binärbaum_ ist ein Wurzelbaum, in dem jeder Knoten ≤ 2 Kinder hat.
+Die Tiefe eines Knotens _v_ ist die Länge des eindeutigen Weges von der Wurzel
+zu _v_, und die Höhe von _v_ ist die Länge eines längsten (absteigenden) Weges
+von _v_ zu einem Blatt. Die Höhe des Baumes ist die Höhe der Wurzel.
+
+#v(6mm)
diff --git a/benches/oneshot.rs b/benches/oneshot.rs
new file mode 100644
index 00000000..5f11589b
--- /dev/null
+++ b/benches/oneshot.rs
@@ -0,0 +1,82 @@
+use std::path::Path;
+
+use iai::{black_box, main, Iai};
+
+use typst::eval::eval;
+use typst::layout::layout;
+use typst::loading::{MemLoader};
+use typst::parse::{parse, Scanner, TokenMode, Tokens};
+use typst::source::{SourceFile, SourceId};
+use typst::Context;
+
+const SRC: &str = include_str!("bench.typ");
+
+fn context() -> (Context, SourceId) {
+ let font = include_bytes!("../fonts/EBGaramond-Regular.ttf");
+ let loader = MemLoader::new()
+ .with(Path::new("EBGaramond-Regular.ttf"), &font[..])
+ .wrap();
+ let mut ctx = Context::new(loader);
+ let id = ctx.sources.provide(Path::new(""), SRC.to_string());
+ (ctx, id)
+}
+
+fn bench_decode(iai: &mut Iai) {
+ iai.run(|| {
+ // We don't use chars().count() because that has a special
+ // superfast implementation.
+ let mut count = 0;
+ let mut chars = black_box(SRC).chars();
+ while let Some(_) = chars.next() {
+ count += 1;
+ }
+ count
+ })
+}
+
+fn bench_scan(iai: &mut Iai) {
+ iai.run(|| {
+ let mut count = 0;
+ let mut scanner = Scanner::new(black_box(SRC));
+ while let Some(_) = scanner.eat() {
+ count += 1;
+ }
+ count
+ })
+}
+
+fn bench_tokenize(iai: &mut Iai) {
+ iai.run(|| Tokens::new(black_box(SRC), black_box(TokenMode::Markup)).count());
+}
+
+fn bench_parse(iai: &mut Iai) {
+ iai.run(|| parse(&SourceFile::detached(SRC)));
+}
+
+fn bench_eval(iai: &mut Iai) {
+ let (mut ctx, id) = context();
+ let ast = ctx.parse(id).unwrap();
+ iai.run(|| eval(&mut ctx, id, &ast).unwrap());
+}
+
+fn bench_to_tree(iai: &mut Iai) {
+ let (mut ctx, id) = context();
+ let module = ctx.evaluate(id).unwrap();
+ iai.run(|| module.template.to_tree(ctx.state()));
+}
+
+fn bench_layout(iai: &mut Iai) {
+ let (mut ctx, id) = context();
+ let tree = ctx.execute(id).unwrap();
+ iai.run(|| layout(&mut ctx, &tree));
+}
+
+main!(
+ bench_decode,
+ bench_scan,
+ bench_tokenize,
+ bench_parse,
+ bench_eval,
+ bench_to_tree,
+ bench_layout
+);
diff --git a/src/lib.rs b/src/lib.rs
index b6484012..207567f8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -49,7 +49,8 @@ pub mod util;
use std::rc::Rc;
use crate::diag::TypResult;
-use crate::eval::{Scope, State};
+use crate::eval::{Scope, State, Module};
+use crate::syntax::SyntaxTree;
use crate::font::FontStore;
use crate::image::ImageStore;
#[cfg(feature = "layout-cache")]
@@ -88,11 +89,30 @@ impl Context {
ContextBuilder::default()
}
+ /// A read-only reference to the standard library scope.
+ pub fn std(&self) -> &Scope {
+ &self.std
+ }
+
+ /// A read-only reference to the state.
+ pub fn state(&self) -> &State {
+ &self.state
+ }
+
+ /// Parse a source file and return the resulting syntax tree.
+ pub fn parse(&mut self, id: SourceId) -> TypResult<SyntaxTree> {
+ parse::parse(self.sources.get(id))
+ }
+
+ /// Evaluate a source file and return the resulting module.
+ pub fn evaluate(&mut self, id: SourceId) -> TypResult<Module> {
+ let ast = self.parse(id)?;
+ eval::eval(self, id, &ast)
+ }
+
/// Execute a source file and produce the resulting layout tree.
pub fn execute(&mut self, id: SourceId) -> TypResult<LayoutTree> {
- let source = self.sources.get(id);
- let ast = parse::parse(source)?;
- let module = eval::eval(self, id, &ast)?;
+ let module = self.evaluate(id)?;
Ok(module.template.to_tree(&self.state))
}