diff --git a/examples/tape-write-benchmark.rs b/examples/tape-write-benchmark.rs new file mode 100644 index 00000000..d5686e65 --- /dev/null +++ b/examples/tape-write-benchmark.rs @@ -0,0 +1,91 @@ +use std::{ + fs::File, + io::Read, + time::{Duration, SystemTime}, +}; + +use anyhow::{format_err, Error}; +use pbs_tape::TapeWrite; +use proxmox_backup::tape::drive::{LtoTapeHandle, TapeDriver}; + +const URANDOM_PATH: &str = "/dev/urandom"; +const CHUNK_SIZE: usize = 4 * 1024 * 1024; // 4 MiB +const LOG_LIMIT: usize = 4 * 1024 * 1024 * 1024; // 4 GiB + +fn write_chunks<'a>( + mut writer: Box, + blob_size: usize, + max_size: usize, + max_time: Duration, +) -> Result<(), Error> { + // prepare chunks in memory + + let mut blob: Vec = vec![0u8; blob_size]; + + let mut file = File::open(URANDOM_PATH)?; + file.read_exact(&mut blob[..])?; + + let start_time = SystemTime::now(); + loop { + let iteration_time = SystemTime::now(); + let mut count = 0; + let mut bytes_written = 0; + let mut idx = 0; + let mut incr_count = 0; + loop { + if writer.write_all(&blob)? { + eprintln!("LEOM reached"); + break; + } + + // modifying chunks a bit to mitigate compression/deduplication + blob[idx] = blob[idx].wrapping_add(1); + incr_count += 1; + if incr_count >= 256 { + incr_count = 0; + idx += 1; + } + count += 1; + bytes_written += blob_size; + + if bytes_written > max_size { + break; + } + } + + let elapsed = iteration_time.elapsed()?.as_secs_f64(); + let elapsed_total = start_time.elapsed()?; + eprintln!( + "{:.2}s: wrote {} chunks ({:.2} MB at {:.2} MB/s, average: {:.2} MB/s)", + elapsed_total.as_secs_f64(), + count, + bytes_written as f64 / 1_000_000.0, + (bytes_written as f64) / (1_000_000.0 * elapsed), + (writer.bytes_written() as f64) / (1_000_000.0 * elapsed_total.as_secs_f64()), + ); + + if elapsed_total > max_time { + break; + } + } + + Ok(()) +} +fn main() -> Result<(), Error> { + let mut args = std::env::args_os(); + args.next(); // binary name + let path = args.next().expect("no path to tape device given"); + let file = File::open(path).map_err(|err| format_err!("could not open tape device: {err}"))?; + let mut drive = LtoTapeHandle::new(file) + .map_err(|err| format_err!("error creating drive handle: {err}"))?; + write_chunks( + drive + .write_file() + .map_err(|err| format_err!("error starting file write: {err}"))?, + CHUNK_SIZE, + LOG_LIMIT, + Duration::new(60 * 20, 0), + ) + .map_err(|err| format_err!("error writing data to tape: {err}"))?; + Ok(()) +}