mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Move to exclusively oneshot benchmarks with Iai fork (#41)
This commit is contained in:
parent
011865ab5c
commit
c44ecbfbd2
@ -10,9 +10,6 @@ cli = ["anyhow", "codespan-reporting", "fs", "same-file"]
|
|||||||
fs = ["dirs", "memmap2", "same-file", "walkdir"]
|
fs = ["dirs", "memmap2", "same-file", "walkdir"]
|
||||||
layout-cache = []
|
layout-cache = []
|
||||||
|
|
||||||
[workspace]
|
|
||||||
members = ["bench"]
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
# Faster compilation
|
# Faster compilation
|
||||||
debug = 0
|
debug = 0
|
||||||
@ -44,6 +41,7 @@ walkdir = { version = "2", optional = true }
|
|||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
tiny-skia = "0.5"
|
tiny-skia = "0.5"
|
||||||
usvg = { version = "0.15", default-features = false }
|
usvg = { version = "0.15", default-features = false }
|
||||||
|
iai = { git = "https://github.com/reknih/iai" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "typst"
|
name = "typst"
|
||||||
@ -53,3 +51,8 @@ required-features = ["cli"]
|
|||||||
name = "typeset"
|
name = "typeset"
|
||||||
required-features = ["fs"]
|
required-features = ["fs"]
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "oneshot"
|
||||||
|
path = "benches/oneshot.rs"
|
||||||
|
harness = false
|
||||||
|
@ -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
|
|
@ -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);
|
|
@ -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);
|
|
45
benches/bench.typ
Normal file
45
benches/bench.typ
Normal file
@ -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)
|
82
benches/oneshot.rs
Normal file
82
benches/oneshot.rs
Normal file
@ -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
|
||||||
|
);
|
28
src/lib.rs
28
src/lib.rs
@ -49,7 +49,8 @@ pub mod util;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::diag::TypResult;
|
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::font::FontStore;
|
||||||
use crate::image::ImageStore;
|
use crate::image::ImageStore;
|
||||||
#[cfg(feature = "layout-cache")]
|
#[cfg(feature = "layout-cache")]
|
||||||
@ -88,11 +89,30 @@ impl Context {
|
|||||||
ContextBuilder::default()
|
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.
|
/// Execute a source file and produce the resulting layout tree.
|
||||||
pub fn execute(&mut self, id: SourceId) -> TypResult<LayoutTree> {
|
pub fn execute(&mut self, id: SourceId) -> TypResult<LayoutTree> {
|
||||||
let source = self.sources.get(id);
|
let module = self.evaluate(id)?;
|
||||||
let ast = parse::parse(source)?;
|
|
||||||
let module = eval::eval(self, id, &ast)?;
|
|
||||||
Ok(module.template.to_tree(&self.state))
|
Ok(module.template.to_tree(&self.state))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user