mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-04 18:28:02 +00:00
perf: parallelize font loading and sync wait it (#1470)
* x * test memory fonts iter * perf: impl par
This commit is contained in:
parent
987758869f
commit
2ec7a26420
9 changed files with 180 additions and 42 deletions
46
Cargo.lock
generated
46
Cargo.lock
generated
|
@ -636,6 +636,12 @@ dependencies = [
|
|||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "condtype"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af"
|
||||
|
||||
[[package]]
|
||||
name = "confy"
|
||||
version = "0.6.1"
|
||||
|
@ -1006,6 +1012,31 @@ dependencies = [
|
|||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "divan"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0583193020b29b03682d8d33bb53a5b0f50df6daacece12ca99b904cfdcb8c4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"clap",
|
||||
"condtype",
|
||||
"divan-macros",
|
||||
"libc",
|
||||
"regex-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "divan-macros"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dc51d98e636f5e3b0759a39257458b22619cac7e96d932da6eeb052891bb67c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.1"
|
||||
|
@ -3003,6 +3034,12 @@ dependencies = [
|
|||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-lite"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
|
@ -4040,6 +4077,14 @@ version = "0.13.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dab89c3820bd63478ff48eb22413c7d2b4afc280aff271a6706ec7b1dc6ab18"
|
||||
|
||||
[[package]]
|
||||
name = "tinymist-bench-font-load"
|
||||
version = "0.13.4"
|
||||
dependencies = [
|
||||
"divan",
|
||||
"tinymist",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinymist-core"
|
||||
version = "0.13.4"
|
||||
|
@ -4262,6 +4307,7 @@ dependencies = [
|
|||
"js-sys",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"rayon",
|
||||
"reqwest",
|
||||
"rpds",
|
||||
"serde",
|
||||
|
|
|
@ -12,7 +12,7 @@ rust-version = "1.83"
|
|||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["crates/*", "tests"]
|
||||
members = ["benches/*", "crates/*", "tests"]
|
||||
|
||||
[workspace.dependencies]
|
||||
|
||||
|
|
21
benches/font-load/Cargo.toml
Normal file
21
benches/font-load/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "tinymist-bench-font-load"
|
||||
description = "Font loading bench for tinymist."
|
||||
authors.workspace = true
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
divan.workspace = true
|
||||
tinymist.workspace = true
|
||||
|
||||
[[bench]]
|
||||
name = "tinymist-bench-font-load"
|
||||
path = "src/load.rs"
|
||||
harness = false
|
||||
|
||||
[features]
|
||||
the-thesis = []
|
39
benches/font-load/src/load.rs
Normal file
39
benches/font-load/src/load.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use tinymist::{project::LspUniverseBuilder, Config};
|
||||
|
||||
fn main() {
|
||||
// initialize global variables
|
||||
|
||||
// Run registered benchmarks.
|
||||
divan::main();
|
||||
}
|
||||
|
||||
// Checks font loading performance of embedded fonts
|
||||
#[divan::bench]
|
||||
fn load_embedded() {
|
||||
let _embedded_fonts = Arc::new(LspUniverseBuilder::only_embedded_fonts().unwrap());
|
||||
}
|
||||
|
||||
// Checks font loading performance of system fonts
|
||||
#[divan::bench]
|
||||
fn load_system() {
|
||||
let config = Config::default();
|
||||
|
||||
let _fonts = config.compile.determine_fonts();
|
||||
}
|
||||
|
||||
/*
|
||||
Without Parallelization
|
||||
Timer precision: 17 ns
|
||||
tinymist_bench_font_load fastest │ slowest │ median │ mean │ samples │ iters
|
||||
├─ load_embedded 1.167 ms │ 1.697 ms │ 1.176 ms │ 1.188 ms │ 100 │ 100
|
||||
╰─ load_system 111.8 ms │ 123 ms │ 113.6 ms │ 114.3 ms │ 100 │ 100
|
||||
|
||||
With Parallelization
|
||||
Timer precision: 17 ns
|
||||
tinymist_bench_font_load fastest │ slowest │ median │ mean │ samples │ iters
|
||||
├─ load_embedded 130.8 µs │ 1.164 ms │ 157 µs │ 170.3 µs │ 100 │ 100
|
||||
╰─ load_system 14.44 ms │ 18.22 ms │ 15.37 ms │ 15.54 ms │ 100 │ 100
|
||||
|
||||
*/
|
|
@ -27,6 +27,7 @@ hex.workspace = true
|
|||
js-sys = { workspace = true, optional = true }
|
||||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
rayon.workspace = true
|
||||
reqwest = { workspace = true, optional = true }
|
||||
rpds.workspace = true
|
||||
serde.workspace = true
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::{
|
|||
};
|
||||
|
||||
use fontdb::Database;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use sha2::{Digest, Sha256};
|
||||
use tinymist_std::debug_loc::{DataSource, MemoryDataSource};
|
||||
use tinymist_std::error::prelude::*;
|
||||
|
@ -134,12 +135,12 @@ impl SystemFontSearcher {
|
|||
self.flush();
|
||||
|
||||
// Source3: add the fonts in memory.
|
||||
for font_data in opts.with_embedded_fonts {
|
||||
self.add_memory_font(match font_data {
|
||||
self.add_memory_fonts(opts.with_embedded_fonts.into_par_iter().map(|font_data| {
|
||||
match font_data {
|
||||
Cow::Borrowed(data) => Bytes::new(data),
|
||||
Cow::Owned(data) => Bytes::new(data),
|
||||
});
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -186,7 +187,8 @@ impl SystemFontSearcher {
|
|||
use fontdb::Source;
|
||||
use tinymist_std::debug_loc::FsDataSource;
|
||||
|
||||
for face in self.db.faces() {
|
||||
let face = self.db.faces().collect::<Vec<_>>();
|
||||
let info = face.into_par_iter().map(|face| {
|
||||
let path = match &face.source {
|
||||
Source::File(path) | Source::SharedFile(path, _) => path,
|
||||
// We never add binary sources to the database, so there
|
||||
|
@ -199,20 +201,26 @@ impl SystemFontSearcher {
|
|||
.with_face_data(face.id, FontInfo::new)
|
||||
.expect("database must contain this font");
|
||||
|
||||
// eprintln!("searched font: {idx} {:?}", path);
|
||||
info.map(|info| {
|
||||
let slot = FontSlot::new_boxed(LazyBufferFontLoader::new(
|
||||
LazyFile::new(path.clone()),
|
||||
face.index,
|
||||
))
|
||||
.describe(DataSource::Fs(FsDataSource {
|
||||
path: path.to_str().unwrap_or_default().to_owned(),
|
||||
}));
|
||||
|
||||
if let Some(info) = info {
|
||||
self.book.push(info);
|
||||
self.fonts.push(
|
||||
FontSlot::new_boxed(LazyBufferFontLoader::new(
|
||||
LazyFile::new(path.clone()),
|
||||
face.index,
|
||||
))
|
||||
.describe(DataSource::Fs(FsDataSource {
|
||||
path: path.to_str().unwrap_or_default().to_owned(),
|
||||
})),
|
||||
);
|
||||
}
|
||||
(info, slot)
|
||||
})
|
||||
});
|
||||
|
||||
for face in info.collect::<Vec<_>>() {
|
||||
let Some((info, slot)) = face else {
|
||||
continue;
|
||||
};
|
||||
|
||||
self.book.push(info);
|
||||
self.fonts.push(slot);
|
||||
}
|
||||
|
||||
self.db = Database::new();
|
||||
|
@ -238,6 +246,41 @@ impl SystemFontSearcher {
|
|||
}
|
||||
}
|
||||
|
||||
// /// Add an in-memory font.
|
||||
// pub fn add_memory_font(&mut self, data: Bytes) {
|
||||
// self.add_memory_fonts([data].into_par_iter());
|
||||
// }
|
||||
|
||||
/// Adds in-memory fonts.
|
||||
pub fn add_memory_fonts(&mut self, data: impl ParallelIterator<Item = Bytes>) {
|
||||
if !self.db.is_empty() {
|
||||
panic!("dirty font search state, please flush the searcher before adding memory fonts");
|
||||
}
|
||||
|
||||
let loaded = data.flat_map(|data| {
|
||||
FontInfo::iter(&data)
|
||||
.enumerate()
|
||||
.map(|(index, info)| {
|
||||
(
|
||||
info,
|
||||
FontSlot::new_boxed(BufferFontLoader {
|
||||
buffer: Some(data.clone()),
|
||||
index: index as u32,
|
||||
})
|
||||
.describe(DataSource::Memory(MemoryDataSource {
|
||||
name: "<memory>".to_owned(),
|
||||
})),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
for (info, slot) in loaded.collect::<Vec<_>>() {
|
||||
self.book.push(info);
|
||||
self.fonts.push(slot);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_system(&mut self) {
|
||||
self.db.load_system_fonts();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ use tinymist_query::{CompletionFeat, PositionEncoding};
|
|||
use tinymist_render::PeriscopeArgs;
|
||||
use tinymist_task::ExportTarget;
|
||||
use typst::foundations::IntoValue;
|
||||
use typst_shim::utils::{Deferred, LazyHash};
|
||||
use typst_shim::utils::LazyHash;
|
||||
|
||||
// todo: svelte-language-server responds to a Goto Definition request with
|
||||
// LocationLink[] even if the client does not report the
|
||||
|
@ -579,7 +579,7 @@ pub struct CompileConfig {
|
|||
/// Specifies the font paths
|
||||
pub font_paths: Vec<PathBuf>,
|
||||
/// Computed fonts based on configuration.
|
||||
pub fonts: OnceCell<Derived<Deferred<Arc<TinymistFontResolver>>>>,
|
||||
pub fonts: OnceCell<Derived<Arc<TinymistFontResolver>>>,
|
||||
/// Notify the compile status to the editor.
|
||||
pub notify_status: bool,
|
||||
/// Enable periscope document in hover.
|
||||
|
@ -750,17 +750,17 @@ impl CompileConfig {
|
|||
}
|
||||
|
||||
/// Determines the font resolver.
|
||||
pub fn determine_fonts(&self) -> Deferred<Arc<TinymistFontResolver>> {
|
||||
pub fn determine_fonts(&self) -> Arc<TinymistFontResolver> {
|
||||
// todo: on font resolving failure, downgrade to a fake font book
|
||||
let font = || {
|
||||
let opts = self.determine_font_opts();
|
||||
|
||||
log::info!("creating SharedFontResolver with {opts:?}");
|
||||
Derived(Deferred::new(|| {
|
||||
Derived(
|
||||
crate::project::LspUniverseBuilder::resolve_fonts(opts)
|
||||
.map(Arc::new)
|
||||
.expect("failed to create font book")
|
||||
}))
|
||||
.expect("failed to create font book"),
|
||||
)
|
||||
};
|
||||
self.fonts.get_or_init(font).clone().0
|
||||
}
|
||||
|
|
|
@ -189,17 +189,11 @@ impl ServerState {
|
|||
|
||||
log::info!("ServerState: creating ProjectState, entry: {entry:?}, inputs: {inputs:?}");
|
||||
|
||||
// todo: never fail?
|
||||
let embedded_fonts = Arc::new(LspUniverseBuilder::only_embedded_fonts().unwrap());
|
||||
let fonts = config.compile.determine_fonts();
|
||||
let package_registry =
|
||||
LspUniverseBuilder::resolve_package(cert_path.clone(), Some(&package));
|
||||
let verse = LspUniverseBuilder::build(
|
||||
entry,
|
||||
export_target,
|
||||
inputs,
|
||||
embedded_fonts,
|
||||
package_registry,
|
||||
);
|
||||
let verse =
|
||||
LspUniverseBuilder::build(entry, export_target, inputs, fonts, package_registry);
|
||||
|
||||
// todo: unify filesystem watcher
|
||||
let (dep_tx, dep_rx) = mpsc::unbounded_channel();
|
||||
|
@ -220,14 +214,6 @@ impl ServerState {
|
|||
},
|
||||
);
|
||||
|
||||
// Delayed Loads fonts
|
||||
let font_client = client.clone();
|
||||
let font_resolver = config.compile.determine_fonts();
|
||||
client.handle.spawn_blocking(move || {
|
||||
// Refresh fonts
|
||||
font_client.send_event(LspInterrupt::Font(font_resolver.wait().clone()));
|
||||
});
|
||||
|
||||
ProjectState {
|
||||
compiler,
|
||||
preview: handle.preview.clone(),
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
"docs:rs": "cargo doc --workspace --document-private-items --no-deps",
|
||||
"lint": "eslint editors/vscode/src",
|
||||
"lint-fix": "eslint editors/vscode/src --fix",
|
||||
"benches": "cargo bench --workspace",
|
||||
"bench": "cargo bench --workspace --bench",
|
||||
"test:grammar": "cd syntaxes/textmate && yarn run test",
|
||||
"build:typlite": "cargo build --bin typlite",
|
||||
"typlite": "target/debug/typlite",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue