Skip to content

Commit

Permalink
Significantly improve performance by using a buffered writer
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
MoSal committed Oct 9, 2024
1 parent eca6b8a commit 73a2d58
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 38 deletions.
24 changes: 13 additions & 11 deletions src/controller.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::io::{self, BufRead, Write};
use std::io::{self, BufRead, BufWriter, Write};

use crate::assets::HighlightingAssets;
use crate::config::{Config, VisibleLines};
Expand Down Expand Up @@ -88,9 +88,10 @@ impl<'b> Controller<'b> {
clircle::Identifier::stdout()
};

const BUF_W_SZ: usize = 1 << 14;
let mut writer = match output_buffer {
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 stderr = io::stderr();
Expand Down Expand Up @@ -124,10 +125,10 @@ impl<'b> Controller<'b> {
Ok(no_errors)
}

fn print_input<R: BufRead>(
fn print_input<R: BufRead, W: io::Write>(
&self,
input: Input,
writer: &mut OutputHandle,
writer: &mut OutputHandle<W>,
stdin: R,
stdout_identifier: Option<&Identifier>,
is_first: bool,
Expand Down Expand Up @@ -174,7 +175,7 @@ impl<'b> Controller<'b> {
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))
} else {
Box::new(InteractivePrinter::new(
Expand All @@ -196,10 +197,10 @@ impl<'b> Controller<'b> {
)
}

fn print_file(
fn print_file<W: io::Write>(
&self,
printer: &mut dyn Printer,
writer: &mut OutputHandle,
printer: &mut dyn Printer<W>,
writer: &mut OutputHandle<W>,
input: &mut OpenedInput,
add_header_padding: bool,
#[cfg(feature = "git")] line_changes: &Option<LineChanges>,
Expand Down Expand Up @@ -234,10 +235,10 @@ impl<'b> Controller<'b> {
Ok(())
}

fn print_file_ranges(
fn print_file_ranges<W: io::Write>(
&self,
printer: &mut dyn Printer,
writer: &mut OutputHandle,
printer: &mut dyn Printer<W>,
writer: &mut OutputHandle<W>,
reader: &mut InputReader,
line_ranges: &LineRanges,
) -> Result<()> {
Expand Down Expand Up @@ -279,6 +280,7 @@ impl<'b> Controller<'b> {
line_number += 1;
line_buffer.clear();
}
writer.flush()?;
Ok(())
}
}
61 changes: 34 additions & 27 deletions src/printer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt;
use std::io;
use std::io::{self, BufWriter, Write};
use std::vec::Vec;

use nu_ansi_term::Color::{Fixed, Green, Red, Yellow};
Expand Down Expand Up @@ -67,35 +67,42 @@ const EMPTY_SYNTECT_STYLE: syntect::highlighting::Style = syntect::highlighting:
font_style: FontStyle::empty(),
};

pub enum OutputHandle<'a> {
IoWrite(&'a mut dyn io::Write),
pub enum OutputHandle<'a, W: io::Write> {
IoWrite(BufWriter<W>),
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<()> {
match self {
Self::IoWrite(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(
&mut self,
handle: &mut OutputHandle,
handle: &mut OutputHandle<W>,
input: &OpenedInput,
add_header_padding: bool,
) -> 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(
&mut self,
out_of_range: bool,
handle: &mut OutputHandle,
handle: &mut OutputHandle<W>,
line_number: usize,
line_buffer: &[u8],
) -> Result<()>;
Expand All @@ -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(
&mut self,
_handle: &mut OutputHandle,
_handle: &mut OutputHandle<W>,
_input: &OpenedInput,
_add_header_padding: bool,
) -> Result<()> {
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(())
}

fn print_snip(&mut self, _handle: &mut OutputHandle) -> Result<()> {
fn print_snip(&mut self, _handle: &mut OutputHandle<W>) -> Result<()> {
Ok(())
}

fn print_line(
&mut self,
out_of_range: bool,
handle: &mut OutputHandle,
handle: &mut OutputHandle<W>,
_line_number: usize,
line_buffer: &[u8],
) -> Result<()> {
Expand Down Expand Up @@ -321,9 +328,9 @@ impl<'a> InteractivePrinter<'a> {
})
}

fn print_horizontal_line_term(
fn print_horizontal_line_term<W: io::Write>(
&mut self,
handle: &mut OutputHandle,
handle: &mut OutputHandle<W>,
style: Style,
) -> Result<()> {
writeln!(
Expand All @@ -334,7 +341,7 @@ impl<'a> InteractivePrinter<'a> {
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 {
self.print_horizontal_line_term(handle, self.colors.grid)?;
} else {
Expand Down Expand Up @@ -372,7 +379,7 @@ 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() {
write!(
handle,
Expand All @@ -387,18 +394,18 @@ impl<'a> InteractivePrinter<'a> {
}
}

fn print_header_component_with_indent(
fn print_header_component_with_indent<W: io::Write>(
&mut self,
handle: &mut OutputHandle,
handle: &mut OutputHandle<W>,
content: &str,
) -> Result<()> {
self.print_header_component_indent(handle)?;
writeln!(handle, "{content}")
}

fn print_header_multiline_component(
fn print_header_multiline_component<W: io::Write>(
&mut self,
handle: &mut OutputHandle,
handle: &mut OutputHandle<W>,
content: &str,
) -> Result<()> {
let mut content = content;
Expand Down Expand Up @@ -446,10 +453,10 @@ impl<'a> InteractivePrinter<'a> {
}
}

impl<'a> Printer for InteractivePrinter<'a> {
impl<'a, W: io::Write> Printer<W> for InteractivePrinter<'a> {
fn print_header(
&mut self,
handle: &mut OutputHandle,
handle: &mut OutputHandle<W>,
input: &OpenedInput,
add_header_padding: bool,
) -> Result<()> {
Expand Down Expand Up @@ -549,7 +556,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
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()
&& (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable)
{
Expand All @@ -559,7 +566,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_count = panel.chars().count();

Expand All @@ -586,7 +593,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
fn print_line(
&mut self,
out_of_range: bool,
handle: &mut OutputHandle,
handle: &mut OutputHandle<W>,
line_number: usize,
line_buffer: &[u8],
) -> Result<()> {
Expand Down

0 comments on commit 73a2d58

Please sign in to comment.