diff options
| -rw-r--r-- | benches/benchmark.rs | 55 | ||||
| -rw-r--r-- | src/reverse_log_reader.rs | 12 |
2 files changed, 64 insertions, 3 deletions
diff --git a/benches/benchmark.rs b/benches/benchmark.rs index c999811..43d53f7 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -2,6 +2,8 @@ mod utils; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use log_db::*; +use std::fs::OpenOptions; +use std::path::Path; use tempfile; use utils::*; @@ -156,6 +158,58 @@ pub fn get_various_memtable_capacities(c: &mut Criterion) { } } +fn reverse_read_file_with_various_buffer_sizes(c: &mut Criterion) { + let mut group = c.benchmark_group("reverse_read_file_with_various_buffer_sizes"); + group.sample_size(50); + + // odd powers of 2 + let buffer_sizes = [128, 512, 2048, 8192, 32768, 131_072, 524_288]; + const PREFILL_N: usize = 100_000; + + let data_dir_obj = tempfile::tempdir().expect("Failed to get tmpdir"); + let data_dir = &data_dir_obj + .path() + .to_str() + .expect("Failed to convert tmpdir path to str"); + + // Create a db instance for prefilling + let mut db = DB::configure() + .data_dir(&data_dir) + .fields(&vec![ + (Field::Id, RecordFieldType::Int), + (Field::Name, RecordFieldType::String), + (Field::Data, RecordFieldType::Bytes), + ]) + .primary_key(Field::Id) + .initialize() + .expect("Failed to initialize DB"); + + prefill_db(&mut db, PREFILL_N).expect("Failed to prefill DB"); + drop(db); + + for size in buffer_sizes { + group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &_size| { + let mut file = OpenOptions::new() + .read(true) + .open(Path::new(data_dir).join("db")) + .expect("Failed to open log file"); + + b.iter(|| { + let mut rev_reader = ReverseLogReader::new_with_size(&mut file, size) + .expect("Failed to create ReverseLogReader"); + + // This is to avoid optimizing out the loop + let mut i = 0; + for _ in &mut rev_reader { + i += 1; + } + + i + }); + }); + } +} + // Register the benchmark group criterion_group!( benches, @@ -163,5 +217,6 @@ criterion_group!( upsert_write_durability, get_from_disk_various_initial_sizes, get_various_memtable_capacities, + reverse_read_file_with_various_buffer_sizes, ); criterion_main!(benches); diff --git a/src/reverse_log_reader.rs b/src/reverse_log_reader.rs index 8a2126f..5d80dba 100644 --- a/src/reverse_log_reader.rs +++ b/src/reverse_log_reader.rs @@ -3,6 +3,7 @@ use std::fs::{self}; use std::io::{self, Read, Seek, SeekFrom}; pub struct ReverseLogReader<'a> { + /// The file to read from end to beginning. file: &'a mut fs::File, /// The internal buffer used to read from the file. /// It is populated with the last INTERNAL_BUF_SIZE bytes read from the file @@ -13,16 +14,21 @@ pub struct ReverseLogReader<'a> { /// is populated with the next (= closer to the start of the file) INTERNAL_BUF_SIZE bytes from the file. /// Note: This is the index of the next byte to be read from the internal buffer + 1 internal_pos: usize, + /// A flag indicating whether the record separator at the cursor position has been consumed. + /// Useful to avoid consuming the separator once when reading until an escape character, and + /// a second time when reading a new record and validating it ends in a separator. consumed_record_sep: bool, } -const INTERNAL_BUF_SIZE: usize = 4096; +// This value is based on the reverse_read_file_with_various_buffer_sizes benchmark. +// Greater values yield little to no performance improvement. +const DEFAULT_INTERNAL_BUF_SIZE: usize = 32768; impl<'a> ReverseLogReader<'a> { pub fn new(file: &mut fs::File) -> Result<ReverseLogReader, io::Error> { file.seek(SeekFrom::End(0))?; Ok(ReverseLogReader { file, - internal_buf: vec![0; INTERNAL_BUF_SIZE], + internal_buf: vec![0; DEFAULT_INTERNAL_BUF_SIZE], internal_pos: 0, consumed_record_sep: false, }) @@ -61,7 +67,7 @@ impl<'a> ReverseLogReader<'a> { self.consumed_record_sep = false; let mut result_buf: Vec<u8> = vec![]; - let mut read_buf = Vec::with_capacity(INTERNAL_BUF_SIZE); + let mut read_buf = Vec::with_capacity(self.internal_buf.len()); loop { read_buf.clear(); let read = self.read_until(ESCAPE_CHARACTER, &mut read_buf)?; |
