use criterion::{criterion_group, criterion_main, Criterion, Throughput};
use rand::{thread_rng, Rng};
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use std::{
fs::File,
io::Write,
path::{Path, PathBuf},
process::{exit, Command},
time::Duration,
};
use tempfile::tempdir;
const SAMPLE_SIZE: usize = 20;
fn safe_files_upload(dir: &str) {
let output = Command::new("./target/release/safe")
.arg("files")
.arg("upload")
.arg(dir)
.arg("--retry-strategy") .arg("quick")
.output()
.expect("Failed to execute command");
if !output.status.success() {
let err = output.stderr;
let err_string = String::from_utf8(err).expect("Failed to parse error string");
panic!("Upload command executed with failing error code: {err_string:?}");
}
}
fn safe_files_download() {
let output = Command::new("./target/release/safe")
.arg("files")
.arg("download")
.output()
.expect("Failed to execute command");
if !output.status.success() {
let err = output.stderr;
let err_string = String::from_utf8(err).expect("Failed to parse error string");
panic!("Download command executed with failing error code: {err_string:?}");
}
}
fn generate_file(path: &PathBuf, file_size_mb: usize) {
let mut file = File::create(path).expect("Failed to create file");
let mut rng = thread_rng();
let n_small_chunks = file_size_mb * 1024 * 32;
for _ in 0..n_small_chunks {
let random_data: [u8; 32] = rng.gen();
file.write_all(&random_data)
.expect("Failed to write to file");
}
let size = file.metadata().expect("Failed to get metadata").len() as f64 / (1024 * 1024) as f64;
assert_eq!(file_size_mb as f64, size);
}
fn fund_cli_wallet() {
let _ = Command::new("./target/release/safe")
.arg("wallet")
.arg("get-faucet")
.arg("127.0.0.1:8000")
.output()
.expect("Failed to execute 'safe wallet get-faucet' command");
}
fn criterion_benchmark(c: &mut Criterion) {
if !Path::new("./target/release/safe").exists() {
eprintln!("Error: Binary ./target/release/safe does not exist. Please make sure to compile your project first");
exit(1);
}
let sizes: [u64; 2] = [1, 10];
for size in sizes.iter() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let temp_dir_path = temp_dir.into_path();
let temp_dir_path_str = temp_dir_path.to_str().expect("Invalid unicode encountered");
(0..23).into_par_iter().for_each(|idx| {
let path = temp_dir_path.join(format!("random_file_{size}_mb_{idx}"));
generate_file(&path, *size as usize);
});
fund_cli_wallet();
std::thread::sleep(Duration::from_secs(10));
let mut group = c.benchmark_group(format!("Upload Benchmark {size}MB"));
group.sampling_mode(criterion::SamplingMode::Flat);
group.measurement_time(Duration::from_secs(5));
group.warm_up_time(Duration::from_secs(5));
group.sample_size(SAMPLE_SIZE);
group.throughput(Throughput::Bytes(size * 1024 * 1024));
let bench_id = format!("safe files upload {size}mb");
group.bench_function(bench_id, |b| {
b.iter(|| safe_files_upload(temp_dir_path_str))
});
group.finish();
}
let mut group = c.benchmark_group("Download Benchmark".to_string());
group.sampling_mode(criterion::SamplingMode::Flat);
group.measurement_time(Duration::from_secs(10));
group.warm_up_time(Duration::from_secs(5));
let total_size: u64 = sizes
.iter()
.map(|size| (SAMPLE_SIZE as u64 + 2) * size)
.sum();
group.sample_size(SAMPLE_SIZE / 2);
group.throughput(Throughput::Bytes(total_size * 1024 * 1024));
let bench_id = "safe files download".to_string();
group.bench_function(bench_id, |b| b.iter(safe_files_download));
group.finish();
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);