mirror of
https://github.com/sharkdp/bat.git
synced 2025-02-21 12:11:17 +01:00
Significantly improve performance by using a buffered writer
Before ``` Benchmark 1: seq 10000000 | bat Time (mean ± σ): 6.235 s ± 0.052 s [User: 3.664 s, System: 2.714 s] Range (min … max): 6.172 s … 6.355 s 10 runs ``` After ``` Benchmark 1: seq 10000000 | ./target/release/bat Time (mean ± σ): 215.9 ms ± 5.1 ms [User: 275.4 ms, System: 38.8 ms] Range (min … max): 210.3 ms … 224.9 ms 10 runs ``` Using `less` for comparison ``` Benchmark 1: seq 10000000 | less Time (mean ± σ): 637.3 ms ± 43.3 ms [User: 642.1 ms, System: 95.6 ms] Range (min … max): 584.5 ms … 700.1 ms 10 runs ``` And raw ``` Benchmark 1: seq 10000000 Time (mean ± σ): 63.1 ms ± 1.3 ms [User: 57.1 ms, System: 5.9 ms] Range (min … max): 62.1 ms … 66.0 ms 10 runs ``` Signed-off-by: Mohammad AlSaleh <CE.Mohammad.AlSaleh@gmail.com>
This commit is contained in:
parent
eca6b8a376
commit
69a4bedb55
@ -44,6 +44,7 @@
|
|||||||
- Use bat's ANSI iterator during tab expansion, see #2998 (@eth-p)
|
- Use bat's ANSI iterator during tab expansion, see #2998 (@eth-p)
|
||||||
- Support 'statically linked binary' for aarch64 in 'Release' page, see #2992 (@tzq0301)
|
- Support 'statically linked binary' for aarch64 in 'Release' page, see #2992 (@tzq0301)
|
||||||
- Update options in shell completions and the man page of `bat`, see #2995 (@akinomyoga)
|
- Update options in shell completions and the man page of `bat`, see #2995 (@akinomyoga)
|
||||||
|
- Significantly improve performance by using a buffered writer, see #3101 (@MoSal)
|
||||||
|
|
||||||
## Syntaxes
|
## Syntaxes
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::io::{self, BufRead, Write};
|
use std::io::{self, BufRead, BufWriter, Write};
|
||||||
|
|
||||||
use crate::assets::HighlightingAssets;
|
use crate::assets::HighlightingAssets;
|
||||||
use crate::config::{Config, VisibleLines};
|
use crate::config::{Config, VisibleLines};
|
||||||
@ -88,9 +88,12 @@ impl<'b> Controller<'b> {
|
|||||||
clircle::Identifier::stdout()
|
clircle::Identifier::stdout()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const BUF_W_SZ: usize = 1 << 14;
|
||||||
let mut writer = match output_buffer {
|
let mut writer = match output_buffer {
|
||||||
Some(buf) => OutputHandle::FmtWrite(buf),
|
Some(buf) => OutputHandle::FmtWrite(buf),
|
||||||
None => OutputHandle::IoWrite(output_type.handle()?),
|
None => {
|
||||||
|
OutputHandle::IoWrite(BufWriter::with_capacity(BUF_W_SZ, output_type.handle()?))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let mut no_errors: bool = true;
|
let mut no_errors: bool = true;
|
||||||
let stderr = io::stderr();
|
let stderr = io::stderr();
|
||||||
@ -124,10 +127,10 @@ impl<'b> Controller<'b> {
|
|||||||
Ok(no_errors)
|
Ok(no_errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_input<R: BufRead>(
|
fn print_input<R: BufRead, W: io::Write>(
|
||||||
&self,
|
&self,
|
||||||
input: Input,
|
input: Input,
|
||||||
writer: &mut OutputHandle,
|
writer: &mut OutputHandle<W>,
|
||||||
stdin: R,
|
stdin: R,
|
||||||
stdout_identifier: Option<&Identifier>,
|
stdout_identifier: Option<&Identifier>,
|
||||||
is_first: bool,
|
is_first: bool,
|
||||||
@ -174,7 +177,7 @@ impl<'b> Controller<'b> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut printer: Box<dyn Printer> = if self.config.loop_through {
|
let mut printer: Box<dyn Printer<_>> = if self.config.loop_through {
|
||||||
Box::new(SimplePrinter::new(self.config))
|
Box::new(SimplePrinter::new(self.config))
|
||||||
} else {
|
} else {
|
||||||
Box::new(InteractivePrinter::new(
|
Box::new(InteractivePrinter::new(
|
||||||
@ -196,10 +199,10 @@ impl<'b> Controller<'b> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_file(
|
fn print_file<W: io::Write>(
|
||||||
&self,
|
&self,
|
||||||
printer: &mut dyn Printer,
|
printer: &mut dyn Printer<W>,
|
||||||
writer: &mut OutputHandle,
|
writer: &mut OutputHandle<W>,
|
||||||
input: &mut OpenedInput,
|
input: &mut OpenedInput,
|
||||||
add_header_padding: bool,
|
add_header_padding: bool,
|
||||||
#[cfg(feature = "git")] line_changes: &Option<LineChanges>,
|
#[cfg(feature = "git")] line_changes: &Option<LineChanges>,
|
||||||
@ -234,10 +237,10 @@ impl<'b> Controller<'b> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_file_ranges(
|
fn print_file_ranges<W: io::Write>(
|
||||||
&self,
|
&self,
|
||||||
printer: &mut dyn Printer,
|
printer: &mut dyn Printer<W>,
|
||||||
writer: &mut OutputHandle,
|
writer: &mut OutputHandle<W>,
|
||||||
reader: &mut InputReader,
|
reader: &mut InputReader,
|
||||||
line_ranges: &LineRanges,
|
line_ranges: &LineRanges,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@ -279,6 +282,7 @@ impl<'b> Controller<'b> {
|
|||||||
line_number += 1;
|
line_number += 1;
|
||||||
line_buffer.clear();
|
line_buffer.clear();
|
||||||
}
|
}
|
||||||
|
writer.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io::{self, BufWriter, Write};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use nu_ansi_term::Color::{Fixed, Green, Red, Yellow};
|
use nu_ansi_term::Color::{Fixed, Green, Red, Yellow};
|
||||||
@ -67,35 +67,42 @@ const EMPTY_SYNTECT_STYLE: syntect::highlighting::Style = syntect::highlighting:
|
|||||||
font_style: FontStyle::empty(),
|
font_style: FontStyle::empty(),
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum OutputHandle<'a> {
|
pub enum OutputHandle<'a, W: io::Write> {
|
||||||
IoWrite(&'a mut dyn io::Write),
|
IoWrite(BufWriter<W>),
|
||||||
FmtWrite(&'a mut dyn fmt::Write),
|
FmtWrite(&'a mut dyn fmt::Write),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OutputHandle<'a> {
|
impl<'a, W: io::Write> OutputHandle<'a, W> {
|
||||||
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> {
|
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::IoWrite(handle) => handle.write_fmt(args).map_err(Into::into),
|
Self::IoWrite(handle) => handle.write_fmt(args).map_err(Into::into),
|
||||||
Self::FmtWrite(handle) => handle.write_fmt(args).map_err(Into::into),
|
Self::FmtWrite(handle) => handle.write_fmt(args).map_err(Into::into),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn flush(&mut self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::IoWrite(handle) => handle.flush().map_err(Into::into),
|
||||||
|
Self::FmtWrite(_handle) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait Printer {
|
pub(crate) trait Printer<W: io::Write> {
|
||||||
fn print_header(
|
fn print_header(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut OutputHandle,
|
handle: &mut OutputHandle<W>,
|
||||||
input: &OpenedInput,
|
input: &OpenedInput,
|
||||||
add_header_padding: bool,
|
add_header_padding: bool,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
fn print_footer(&mut self, handle: &mut OutputHandle, input: &OpenedInput) -> Result<()>;
|
fn print_footer(&mut self, handle: &mut OutputHandle<W>, input: &OpenedInput) -> Result<()>;
|
||||||
|
|
||||||
fn print_snip(&mut self, handle: &mut OutputHandle) -> Result<()>;
|
fn print_snip(&mut self, handle: &mut OutputHandle<W>) -> Result<()>;
|
||||||
|
|
||||||
fn print_line(
|
fn print_line(
|
||||||
&mut self,
|
&mut self,
|
||||||
out_of_range: bool,
|
out_of_range: bool,
|
||||||
handle: &mut OutputHandle,
|
handle: &mut OutputHandle<W>,
|
||||||
line_number: usize,
|
line_number: usize,
|
||||||
line_buffer: &[u8],
|
line_buffer: &[u8],
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
@ -115,28 +122,28 @@ impl<'a> SimplePrinter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Printer for SimplePrinter<'a> {
|
impl<'a, W: io::Write> Printer<W> for SimplePrinter<'a> {
|
||||||
fn print_header(
|
fn print_header(
|
||||||
&mut self,
|
&mut self,
|
||||||
_handle: &mut OutputHandle,
|
_handle: &mut OutputHandle<W>,
|
||||||
_input: &OpenedInput,
|
_input: &OpenedInput,
|
||||||
_add_header_padding: bool,
|
_add_header_padding: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_footer(&mut self, _handle: &mut OutputHandle, _input: &OpenedInput) -> Result<()> {
|
fn print_footer(&mut self, _handle: &mut OutputHandle<W>, _input: &OpenedInput) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_snip(&mut self, _handle: &mut OutputHandle) -> Result<()> {
|
fn print_snip(&mut self, _handle: &mut OutputHandle<W>) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_line(
|
fn print_line(
|
||||||
&mut self,
|
&mut self,
|
||||||
out_of_range: bool,
|
out_of_range: bool,
|
||||||
handle: &mut OutputHandle,
|
handle: &mut OutputHandle<W>,
|
||||||
_line_number: usize,
|
_line_number: usize,
|
||||||
line_buffer: &[u8],
|
line_buffer: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@ -321,9 +328,9 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_horizontal_line_term(
|
fn print_horizontal_line_term<W: io::Write>(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut OutputHandle,
|
handle: &mut OutputHandle<W>,
|
||||||
style: Style,
|
style: Style,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
writeln!(
|
writeln!(
|
||||||
@ -334,7 +341,11 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_horizontal_line(&mut self, handle: &mut OutputHandle, grid_char: char) -> Result<()> {
|
fn print_horizontal_line<W: io::Write>(
|
||||||
|
&mut self,
|
||||||
|
handle: &mut OutputHandle<W>,
|
||||||
|
grid_char: char,
|
||||||
|
) -> Result<()> {
|
||||||
if self.panel_width == 0 {
|
if self.panel_width == 0 {
|
||||||
self.print_horizontal_line_term(handle, self.colors.grid)?;
|
self.print_horizontal_line_term(handle, self.colors.grid)?;
|
||||||
} else {
|
} else {
|
||||||
@ -372,7 +383,10 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_header_component_indent(&mut self, handle: &mut OutputHandle) -> Result<()> {
|
fn print_header_component_indent<W: io::Write>(
|
||||||
|
&mut self,
|
||||||
|
handle: &mut OutputHandle<W>,
|
||||||
|
) -> Result<()> {
|
||||||
if self.config.style_components.grid() {
|
if self.config.style_components.grid() {
|
||||||
write!(
|
write!(
|
||||||
handle,
|
handle,
|
||||||
@ -387,18 +401,18 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_header_component_with_indent(
|
fn print_header_component_with_indent<W: io::Write>(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut OutputHandle,
|
handle: &mut OutputHandle<W>,
|
||||||
content: &str,
|
content: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.print_header_component_indent(handle)?;
|
self.print_header_component_indent(handle)?;
|
||||||
writeln!(handle, "{content}")
|
writeln!(handle, "{content}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_header_multiline_component(
|
fn print_header_multiline_component<W: io::Write>(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut OutputHandle,
|
handle: &mut OutputHandle<W>,
|
||||||
content: &str,
|
content: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut content = content;
|
let mut content = content;
|
||||||
@ -446,10 +460,10 @@ impl<'a> InteractivePrinter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Printer for InteractivePrinter<'a> {
|
impl<'a, W: io::Write> Printer<W> for InteractivePrinter<'a> {
|
||||||
fn print_header(
|
fn print_header(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut OutputHandle,
|
handle: &mut OutputHandle<W>,
|
||||||
input: &OpenedInput,
|
input: &OpenedInput,
|
||||||
add_header_padding: bool,
|
add_header_padding: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@ -549,7 +563,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_footer(&mut self, handle: &mut OutputHandle, _input: &OpenedInput) -> Result<()> {
|
fn print_footer(&mut self, handle: &mut OutputHandle<W>, _input: &OpenedInput) -> Result<()> {
|
||||||
if self.config.style_components.grid()
|
if self.config.style_components.grid()
|
||||||
&& (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable)
|
&& (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable)
|
||||||
{
|
{
|
||||||
@ -559,7 +573,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_snip(&mut self, handle: &mut OutputHandle) -> Result<()> {
|
fn print_snip(&mut self, handle: &mut OutputHandle<W>) -> Result<()> {
|
||||||
let panel = self.create_fake_panel(" ...");
|
let panel = self.create_fake_panel(" ...");
|
||||||
let panel_count = panel.chars().count();
|
let panel_count = panel.chars().count();
|
||||||
|
|
||||||
@ -586,7 +600,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
|||||||
fn print_line(
|
fn print_line(
|
||||||
&mut self,
|
&mut self,
|
||||||
out_of_range: bool,
|
out_of_range: bool,
|
||||||
handle: &mut OutputHandle,
|
handle: &mut OutputHandle<W>,
|
||||||
line_number: usize,
|
line_number: usize,
|
||||||
line_buffer: &[u8],
|
line_buffer: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
Loading…
Reference in New Issue
Block a user