Skip to content

Commit

Permalink
Optimize Synchronizer::write and write_raw methods to re-use open…
Browse files Browse the repository at this point in the history
…ed maps.

Benchmarks:
```
synchronizer/write      time:   [17.183 µs 17.387 µs 17.647 µs]
                        thrpt:  [56.666 Kelem/s 57.515 Kelem/s 58.195 Kelem/s]
                 change:
                        time:   [-47.134% -45.506% -43.898%] (p = 0.00 < 0.05)
                        thrpt:  [+78.247% +83.508% +89.156%]
                        Performance has improved.
synchronizer/write_raw  time:   [147.12 ns 147.76 ns 148.57 ns]
                        thrpt:  [6.7310 Melem/s 6.7676 Melem/s 6.7970 Melem/s]
                 change:
                        time:   [-98.463% -98.442% -98.422%] (p = 0.00 < 0.05)
                        thrpt:  [+6236.1% +6320.0% +6405.5%]
                        Performance has improved.
synchronizer/read/check_bytes_true
                        time:   [41.194 ns 41.271 ns 41.369 ns]
                        thrpt:  [24.172 Melem/s 24.230 Melem/s 24.275 Melem/s]
                 change:
                        time:   [-0.3344% +0.8124% +1.6734%] (p = 0.12 > 0.05)
                        thrpt:  [-1.6458% -0.8058% +0.3355%]
                        No change in performance detected.
synchronizer/read/check_bytes_false
                        time:   [26.658 ns 26.668 ns 26.685 ns]
                        thrpt:  [37.475 Melem/s 37.498 Melem/s 37.513 Melem/s]
                 change:
                        time:   [-4.4520% -3.5968% -2.8378%] (p = 0.00 < 0.05)
                        thrpt:  [+2.9206% +3.7309% +4.6595%]
                        Performance has improved.
```
  • Loading branch information
bocharov committed Jul 9, 2024
1 parent 6b24b1e commit 4a3fdba
Showing 1 changed file with 33 additions and 19 deletions.
52 changes: 33 additions & 19 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ pub(crate) struct DataContainer {
/// Reader's current local instance version
version: Option<InstanceVersion>,
/// Read-only memory mapped files storing data
idx_mmaps: [Option<Mmap>; 2],
read_mmaps: [Option<Mmap>; 2],
/// Write-only memory mapped files storing data
write_mmaps: [Option<MmapMut>; 2],
}

impl DataContainer {
Expand All @@ -26,7 +28,8 @@ impl DataContainer {
DataContainer {
path_prefix: path_prefix.into(),
version: None,
idx_mmaps: [None, None],
read_mmaps: [None, None],
write_mmaps: [None, None],
}
}

Expand All @@ -36,27 +39,38 @@ impl DataContainer {
data: &[u8],
version: InstanceVersion,
) -> Result<usize, SynchronizerError> {
let mut opts = OpenOptions::new();
opts.read(true).write(true).create(true);
let mmap = &mut self.write_mmaps[version.idx()];
let data_size = version.size();

// Only add mode on Unix-based systems
#[cfg(unix)]
opts.mode(0o640);
// only open and mmap data file in the following cases:
// * if it never was opened/mapped before
// * if current mmap size is smaller than requested data size
if mmap.is_none() || mmap.as_ref().unwrap().len() < data_size {
let mut opts = OpenOptions::new();
opts.read(true).write(true).create(true);

let data_file = opts
.open(version.path(&self.path_prefix))
.map_err(FailedDataWrite)?;
// Only add mode on Unix-based systems
#[cfg(unix)]
opts.mode(0o640);

// grow data file when its current length exceeded
let data_len = data.len() as u64;
if data_len > data_file.metadata().map_err(FailedDataWrite)?.len() {
data_file.set_len(data_len).map_err(FailedDataWrite)?;
let data_file = opts
.open(version.path(&self.path_prefix))
.map_err(FailedDataWrite)?;

// grow data file when its current length exceeded
let data_len = data.len() as u64;
if data_len > data_file.metadata().map_err(FailedDataWrite)?.len() {
data_file.set_len(data_len).map_err(FailedDataWrite)?;
}

*mmap = Some(unsafe { MmapMut::map_mut(&data_file).map_err(FailedDataWrite)? });
}

// copy data to mapped file and ensure it's been flushed
let mut mmap = unsafe { MmapMut::map_mut(&data_file).map_err(FailedDataWrite)? };
mmap[..data.len()].copy_from_slice(data);
mmap.flush().map_err(FailedDataWrite)?;
if let Some(mmap) = mmap {
// copy data to mapped file and ensure it's been flushed
mmap[..data.len()].copy_from_slice(data);
mmap.flush().map_err(FailedDataWrite)?;
}

Ok(data.len())
}
Expand All @@ -67,7 +81,7 @@ impl DataContainer {
&mut self,
version: InstanceVersion,
) -> Result<(&[u8], bool), SynchronizerError> {
let mmap = &mut self.idx_mmaps[version.idx()];
let mmap = &mut self.read_mmaps[version.idx()];
let data_size = version.size();

// only open and mmap data file in the following cases:
Expand Down

0 comments on commit 4a3fdba

Please sign in to comment.