extern crate httpdirectory;
use core::time::Duration;
use criterion::{Criterion, criterion_group, criterion_main};
use httpdirectory::entry::Entry;
use httpdirectory::httpdirectoryentry::HttpDirectoryEntry;
use std::hint::black_box;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct HttpDirectory {
entries: Vec<HttpDirectoryEntry>,
url: Arc<String>,
request: Arc<String>, }
impl HttpDirectory {
pub fn filtering_naive<F>(&self, mut f: F) -> Self
where
F: FnMut(&HttpDirectoryEntry) -> bool,
{
let mut entries = self.entries.clone();
entries.retain(|elem| f(elem));
HttpDirectory {
entries,
url: self.url.clone(),
request: self.request.clone(),
}
}
pub fn filtering_v1<F>(&self, mut f: F) -> Self
where
F: FnMut(&HttpDirectoryEntry) -> bool,
{
let entries = self.entries.iter().filter(|entry| f(entry)).cloned().collect();
Self {
entries,
url: Arc::clone(&self.url),
request: Arc::clone(&self.request),
}
}
pub fn filtering_v3<F>(&self, mut f: F) -> Self
where
F: FnMut(&HttpDirectoryEntry) -> bool,
{
let entries = self
.entries
.iter()
.filter_map(|entry| {
if f(entry) {
Some(entry.clone())
} else {
None
}
})
.collect();
Self {
entries,
url: Arc::clone(&self.url),
request: Arc::clone(&self.request),
}
}
pub fn filtering_v4<F>(&self, mut f: F) -> Self
where
F: FnMut(&HttpDirectoryEntry) -> bool,
{
let mut entries = Vec::with_capacity(self.entries.len());
for entry in &self.entries {
if f(entry) {
entries.push(entry.clone());
}
}
entries.shrink_to_fit();
Self {
entries,
url: Arc::clone(&self.url),
request: Arc::clone(&self.request),
}
}
}
fn create_test_directory(size: usize) -> HttpDirectory {
let mut entries = Vec::with_capacity(size);
entries.push(HttpDirectoryEntry::ParentDirectory("../".to_string()));
for i in 0..size - 1 {
if i % 3 == 0 {
let entry = Entry::new(&format!("item_{}", i), &format!("/item_{}", i), "2024-01-01 10:10", "-");
entries.push(HttpDirectoryEntry::Directory(entry));
} else {
let entry = Entry::new(
&format!("item_{}", i),
&format!("/item_{}", i),
"2024-01-01 10:10",
&format!("{}", i * 1024),
);
entries.push(HttpDirectoryEntry::File(entry));
}
}
HttpDirectory {
entries,
url: Arc::new("http://example.com/directory".to_string()),
request: Arc::new("Reqwest client simulation".to_string()),
}
}
fn files_only(entry: &HttpDirectoryEntry) -> bool {
matches!(entry, HttpDirectoryEntry::File(_))
}
fn files_only_whose_size_is_greater_than_5120(entry: &HttpDirectoryEntry) -> bool {
match entry {
HttpDirectoryEntry::File(e) => e.size() > 5_120,
_ => false,
}
}
fn bench_filtering_methods(c: &mut Criterion) {
let sizes = vec![10, 50, 100, 500, 1000, 5000, 10000];
for size in sizes {
let directory = create_test_directory(size);
let mut group = c.benchmark_group(format!("filtering_size_{}", size));
if size >= 5000 {
group.measurement_time(Duration::new(5, 0));
}
group.bench_function("naive", |b| b.iter(|| black_box(directory.filtering_naive(files_only))));
group.bench_function("filter_cloned", |b| b.iter(|| black_box(directory.filtering_v1(files_only))));
group.bench_function("filter_map", |b| b.iter(|| black_box(directory.filtering_v3(files_only))));
group.bench_function("with_capacity", |b| b.iter(|| black_box(directory.filtering_v4(files_only))));
group.finish();
}
}
fn bench_filtering_with_different_filters(c: &mut Criterion) {
let directory = create_test_directory(1000);
let mut group = c.benchmark_group("filtering_with_different_filters");
group.bench_function("files_only_cloned", |b| b.iter(|| black_box(directory.filtering_v1(files_only))));
group.bench_function("files_only_capacity", |b| b.iter(|| black_box(directory.filtering_v4(files_only))));
group.bench_function("files_size_greater_than_5120_cloned", |b| {
b.iter(|| black_box(directory.filtering_v1(files_only_whose_size_is_greater_than_5120)))
});
group.bench_function("files_size_greater_than_5120_capacity", |b| {
b.iter(|| black_box(directory.filtering_v4(files_only_whose_size_is_greater_than_5120)))
});
group.finish();
}
fn bench_selectivity(c: &mut Criterion) {
let directory = create_test_directory(100);
let mut group = c.benchmark_group("selectivity");
group.bench_function("high_selectivity_cloned", |b| {
b.iter(|| {
black_box(directory.filtering_v1(|entry| match entry {
HttpDirectoryEntry::File(e) => e.size() > 900_000,
_ => false,
}))
})
});
group.bench_function("high_selectivity_capacity", |b| {
b.iter(|| {
black_box(directory.filtering_v4(|entry| match entry {
HttpDirectoryEntry::File(e) => e.size() > 900_000,
_ => false,
}))
})
});
group.bench_function("low_selectivity_cloned", |b| {
b.iter(|| {
black_box(directory.filtering_v1(|entry| match entry {
HttpDirectoryEntry::File(e) => e.size() > 5_000,
_ => false,
}))
})
});
group.bench_function("low_selectivity_capacity", |b| {
b.iter(|| {
black_box(directory.filtering_v4(|entry| match entry {
HttpDirectoryEntry::File(e) => e.size() > 1000, _ => false,
}))
})
});
group.finish();
}
criterion_group!(benches, bench_filtering_methods, bench_filtering_with_different_filters, bench_selectivity);
criterion_main!(benches);