on master help sign in
Reduce startup time in loop-through mode with 80%-90%

Instead of 100 ms - 50 ms, startup takes 10 ms - 5 ms. HighlightingAssets::get_syntax_set() is never called when e.g. piping the bat output to a file (see Config::loop_through), so by loading the SyntaxSet only when needed, we radically improve startup time when it is not needed.

commit6acec2c074e0a341edb08f1014d97a84096a68c0 authorMartin Nordholts <enselic@gmail.com> dateThu 22 Jul 2021, 10:39 (5 years ago) parent1bac375
4 files changed+71−9
CHANGELOG.md+1 Cargo.lock+1 Cargo.toml+1 src/assets.rs+68−9
CHANGELOG.md+1
@@ -9,6 +9,7 @@
99## Other
1010
1111- Load cached assets as fast as integrated assets, see #1753 (@Enselic)
12+- Greatly reduce startup time in loop-through mode, e.g. when redirecting output. Instead of *50 ms* - *100 ms*, startup takes *5 ms* - *10 ms*. See #1747 (@Enselic)
1213
1314
1415## Syntaxes
Cargo.lock+1
@@ -101,6 +101,7 @@ dependencies = [
101101 "globset",
102102 "grep-cli",
103103 "lazy_static",
104+ "lazycell",
104105 "nix",
105106 "path_abs",
106107 "predicates",
Cargo.toml+1
@@ -38,6 +38,7 @@ ansi_term = "^0.12.1"
3838ansi_colours = "^1.0"
3939console = "0.14.1"
4040lazy_static = { version = "1.4", optional = true }
41+lazycell = "1.0"
4142wild = { version = "2.0", optional = true }
4243content_inspector = "0.2.4"
4344encoding = "0.2"
src/assets.rs+68−9
@@ -1,7 +1,9 @@
11use std::collections::BTreeMap;
22use std::ffi::OsStr;
33use std::fs;
4−use std::path::Path;
4+use std::path::{Path, PathBuf};
5+
6+use lazycell::LazyCell;
57
68use syntect::dumps::{dump_to_file, from_binary, from_reader};
79use syntect::highlighting::{Theme, ThemeSet};
@@ -17,7 +19,8 @@ use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
1719
1820#[derive(Debug)]
1921pub struct HighlightingAssets {
20− syntax_set: SyntaxSet,
22+ syntax_set_cell: LazyCell<SyntaxSet>,
23+ serialized_syntax_set: Option<SerializedSyntaxSet>,
2124 pub(crate) theme_set: ThemeSet,
2225 fallback_theme: Option<&'static str>,
2326}
@@ -40,9 +43,21 @@ const IGNORED_SUFFIXES: [&str; 10] = [
4043];
4144
4245impl HighlightingAssets {
43− fn new(syntax_set: SyntaxSet, theme_set: ThemeSet) -> Self {
46+ fn new(
47+ syntax_set: Option<SyntaxSet>,
48+ serialized_syntax_set: Option<SerializedSyntaxSet>,
49+ theme_set: ThemeSet,
50+ ) -> Self {
51+ assert!(syntax_set.is_some() || serialized_syntax_set.is_some());
52+
53+ let syntax_set_cell = LazyCell::new();
54+ if let Some(syntax_set) = syntax_set {
55+ syntax_set_cell.fill(syntax_set).expect("can never fail");
56+ }
57+
4458 HighlightingAssets {
45− syntax_set,
59+ syntax_set_cell,
60+ serialized_syntax_set,
4661 theme_set,
4762 fallback_theme: None,
4863 }
@@ -97,20 +112,30 @@ impl HighlightingAssets {
97112 }
98113
99114 Ok(HighlightingAssets::new(
100− syntax_set_builder.build(),
115+ Some(syntax_set_builder.build()),
116+ None,
101117 theme_set,
102118 ))
103119 }
104120
105121 pub fn from_cache(cache_path: &Path) -> Result<Self> {
106122 Ok(HighlightingAssets::new(
107− asset_from_cache(&cache_path.join("syntaxes.bin"), "syntax set")?,
123+ None,
124+ Some(SerializedSyntaxSet::FromFile(
125+ cache_path.join("syntaxes.bin"),
126+ )),
108127 asset_from_cache(&cache_path.join("themes.bin"), "theme set")?,
109128 ))
110129 }
111130
112131 pub fn from_binary() -> Self {
113− HighlightingAssets::new(get_integrated_syntaxset(), get_integrated_themeset())
132+ HighlightingAssets::new(
133+ None,
134+ Some(SerializedSyntaxSet::FromBinary(
135+ get_serialized_integrated_syntaxset(),
136+ )),
137+ get_integrated_themeset(),
138+ )
114139 }
115140
116141 pub fn save_to_cache(&self, target_dir: &Path, current_version: &str) -> Result<()> {
@@ -137,7 +162,17 @@ impl HighlightingAssets {
137162 }
138163
139164 pub(crate) fn get_syntax_set(&self) -> Result<&SyntaxSet> {
140− Ok(&self.syntax_set)
165+ if !self.syntax_set_cell.filled() {
166+ self.syntax_set_cell.fill(
167+ self.serialized_syntax_set
168+ .as_ref()
169+ .expect("a dev forgot to setup serialized_syntax_set, please report to https://github.com/sharkdp/bat/issues")
170+ .deserialize()?
171+ ).unwrap();
172+ }
173+
174+ // It is safe to .unwrap() because we just made sure it was .filled()
175+ Ok(self.syntax_set_cell.borrow().unwrap())
141176 }
142177
143178 /// Use [Self::get_syntaxes] instead
@@ -316,8 +351,32 @@ impl HighlightingAssets {
316351 }
317352}
318353
354+/// A SyntaxSet in serialized form, i.e. bincoded and flate2 compressed.
355+/// We keep it in this format since we want to load it lazily.
356+#[derive(Debug)]
357+enum SerializedSyntaxSet {
358+ /// The data comes from a user-generated cache file.
359+ FromFile(PathBuf),
360+
361+ /// The data to use is embedded into the bat binary.
362+ FromBinary(&'static [u8]),
363+}
364+
365+impl SerializedSyntaxSet {
366+ fn deserialize(&self) -> Result<SyntaxSet> {
367+ match self {
368+ SerializedSyntaxSet::FromBinary(data) => Ok(from_binary(data)),
369+ SerializedSyntaxSet::FromFile(ref path) => asset_from_cache(&path, "syntax set"),
370+ }
371+ }
372+}
373+
374+fn get_serialized_integrated_syntaxset() -> &'static [u8] {
375+ include_bytes!("../assets/syntaxes.bin")
376+}
377+
319378fn get_integrated_syntaxset() -> SyntaxSet {
320− from_binary(include_bytes!("../assets/syntaxes.bin"))
379+ from_binary(get_serialized_integrated_syntaxset())
321380}
322381
323382fn get_integrated_themeset() -> ThemeSet {