From 2b4339663c92699029216382656a1688a540455e Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:56:37 +0800 Subject: [PATCH 1/3] Builtin glob matchers build offload, v2 --- src/bin/bat/app.rs | 4 ++++ src/syntax_mapping.rs | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index a2c09770..f8007897 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -122,6 +122,10 @@ impl App { }; let mut syntax_mapping = SyntaxMapping::new(); + // start building glob matchers for builtin mappings immediately + // this is an appropriate approach because it's statistically likely that + // all the custom mappings need to be checked + syntax_mapping.start_offload_build_all(); if let Some(values) = self.matches.get_many::("ignored-suffix") { for suffix in values { diff --git a/src/syntax_mapping.rs b/src/syntax_mapping.rs index 0dac0c02..7c96c513 100644 --- a/src/syntax_mapping.rs +++ b/src/syntax_mapping.rs @@ -1,6 +1,14 @@ -use std::path::Path; +use std::{ + path::Path, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread, +}; use globset::{Candidate, GlobBuilder, GlobMatcher}; +use once_cell::sync::Lazy; use crate::error::Result; use builtin::BUILTIN_MAPPINGS; @@ -44,7 +52,20 @@ pub struct SyntaxMapping<'a> { /// /// Rules in front have precedence. custom_mappings: Vec<(GlobMatcher, MappingTarget<'a>)>, + pub(crate) ignored_suffixes: IgnoredSuffixes<'a>, + + /// A flag to halt glob matcher building, which is offloaded to another thread. + /// + /// We have this so that we can signal the thread to halt early when appropriate. + halt_glob_build: Arc, +} + +impl<'a> Drop for SyntaxMapping<'a> { + fn drop(&mut self) { + // signal the offload thread to halt early + self.halt_glob_build.store(true, Ordering::Relaxed); + } } impl<'a> SyntaxMapping<'a> { @@ -52,6 +73,24 @@ impl<'a> SyntaxMapping<'a> { Default::default() } + /// Start a thread to build the glob matchers for all builtin mappings. + /// + /// The use of this function while not necessary, is useful to speed up startup + /// times by starting this work early in parallel. + /// + /// The thread halts if/when `halt_glob_build` is set to true. + pub fn start_offload_build_all(&self) { + let halt = Arc::clone(&self.halt_glob_build); + thread::spawn(move || { + for (matcher, _) in BUILTIN_MAPPINGS.iter() { + if halt.load(Ordering::Relaxed) { + break; + } + Lazy::force(matcher); + } + }); + } + pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> { let matcher = make_glob_matcher(from)?; self.custom_mappings.push((matcher, to)); From c7bce466221a093c2ee0ff21eec07841f7692077 Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:24:36 +0800 Subject: [PATCH 2/3] Write changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a672edcb..fde56d37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - Update git-version dependency to use Syn v2, see #2816 (@dtolnay) - Update git2 dependency to v0.18.2, see #2852 (@eth-p) - Apply clippy fixes #2864 (@cyqsimon) +- Faster startup by offloading glob matcher building to a worker thread #2868 (@cyqsimon) ## Syntaxes From 26ac1795484f1a8242c180919da95d6cf841da7c Mon Sep 17 00:00:00 2001 From: cyqsimon <28627918+cyqsimon@users.noreply.github.com> Date: Mon, 26 Feb 2024 11:31:30 +0800 Subject: [PATCH 3/3] Add note on thread synchronization --- src/syntax_mapping.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/syntax_mapping.rs b/src/syntax_mapping.rs index 7c96c513..a149f9bb 100644 --- a/src/syntax_mapping.rs +++ b/src/syntax_mapping.rs @@ -89,6 +89,11 @@ impl<'a> SyntaxMapping<'a> { Lazy::force(matcher); } }); + // Note that this thread is not joined upon completion because there's + // no shared resources that need synchronization to be safely dropped. + // If we later add code into this thread that requires interesting + // resources (e.g. IO), it would be a good idea to store the handle + // and join it on drop. } pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> {