Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 73 additions & 163 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,28 @@
use cfb::{CompoundFile, CreateStreamOptions};
use criterion::{
criterion_group, criterion_main, BenchmarkId, Criterion, Throughput,
};
use cfb::CompoundFile;
use criterion::{criterion_group, criterion_main, Criterion, Throughput};
use std::fs::OpenOptions;
use std::hint::black_box;
use std::io::Cursor;
use std::io::Read;
use std::io::Write;
use tempfile::NamedTempFile;

fn write_many_streams(
n: usize,
size: usize,
stream_buffer_size: Option<usize>,
) -> Vec<u8> {
fn write_many_streams(n: usize, size: usize) -> Vec<u8> {
let mut buff = Vec::new();
{
let mut test_comp =
CompoundFile::create(Cursor::new(&mut buff)).unwrap();
let data = vec![0; size];
for i in 0..n {
let name = format!("test{i}");
let mut stream = match stream_buffer_size {
Some(buf_size) => {
let options =
CreateStreamOptions::new().buffer_size(buf_size);
test_comp
.create_stream_with_options(name, options)
.unwrap()
}
None => test_comp.create_stream(name).unwrap(),
};
let mut stream = test_comp.create_stream(name).unwrap();
stream.write_all(&data).unwrap();
}
}
buff
}

fn write_many_streams_disk(
n: usize,
size: usize,
stream_buffer_size: Option<usize>,
) {
fn write_many_streams_disk(n: usize, size: usize) {
let tmpfile = NamedTempFile::new().unwrap();
{
let mut test_comp = CompoundFile::create(
Expand All @@ -54,159 +36,87 @@ fn write_many_streams_disk(
let data = vec![0; size];
for i in 0..n {
let name = format!("test{i}");
let mut stream = match stream_buffer_size {
Some(buf_size) => {
let options =
CreateStreamOptions::new().buffer_size(buf_size);
test_comp
.create_stream_with_options(name, options)
.unwrap()
}
None => test_comp.create_stream(name).unwrap(),
};
let mut stream = test_comp.create_stream(name).unwrap();
stream.write_all(&data).unwrap();
}
}
// File is deleted when tmpfile is dropped
}

fn criterion_benchmark(c: &mut Criterion) {
// Buffer sizes to compare (in bytes). `None` means the default internal
// stream buffer size.
let buffer_sizes: &[(Option<usize>, &str)] = &[
(None, "default"),
(Some(8 * 1024), "buffer=8192"),
(Some(64 * 1024), "buffer=65536"),
(Some(256 * 1024), "buffer=262144"),
(Some(1024 * 1024), "buffer=1048576"),
];

// many small streams with throughput reporting
let mut small = c.benchmark_group("write many smaller streams");
let size = 64usize;
let n = 1000;
let total_bytes = (n * size) as u64;
small.sample_size(10);
small.throughput(Throughput::Bytes(total_bytes));

for (buf, label) in buffer_sizes {
small.bench_with_input(
BenchmarkId::new("total", *label),
&size,
|b, &s| {
b.iter(|| {
let out = write_many_streams(
black_box(n),
black_box(s),
buf.map(black_box),
);
black_box(out);
})
},
);
fn read_many_streams(buff: &[u8], n: usize) {
let mut test_comp = CompoundFile::open(Cursor::new(buff)).unwrap();
for i in 0..n {
let name = format!("test{i}");
let mut stream = test_comp.open_stream(name).unwrap();
let mut sink = Vec::new();
stream.read_to_end(&mut sink).unwrap();
black_box(sink);
}
small.finish();

// streams just below the MiniFAT cutoff
let mut cutoff = c.benchmark_group("write MiniFAT max streams");
let size = (4 * 1024) - 1;
let n = 500;
let total_bytes = (n * size) as u64;
cutoff.sample_size(10);
cutoff.throughput(Throughput::Bytes(total_bytes));

for (buf, label) in buffer_sizes {
cutoff.bench_with_input(
BenchmarkId::new("total", *label),
&size,
|b, &s| {
b.iter(|| {
let out = write_many_streams(
black_box(n),
black_box(s),
buf.map(black_box),
);
black_box(out);
})
},
);
}
cutoff.finish();
}

// several medium streams with throughput reporting
let mut medium = c.benchmark_group("write several medium streams");
let size = 1024 * 1024usize;
let n = 50;
let total_bytes = (n * size) as u64;
medium.sample_size(10);
medium.throughput(Throughput::Bytes(total_bytes));
fn criterion_benchmark(c: &mut Criterion) {
let stream_benches = [
// (label, stream_size, stream_count)
("n=10000,size=0B", 0, 10000usize),
("n=1000,size=64B", 64usize, 1000usize),
("n=100,size=4KiB-1 (MiniFAT)", 1024 * 4 - 1, 100usize),
("n=100,size=4KiB (FAT)", 1024 * 4, 100usize),
("n=50,size=1MiB", 1024 * 1024usize, 50usize),
("n=1,size=256MiB", 256 * 1024 * 1024usize, 1usize),
];

for (buf, label) in buffer_sizes {
medium.bench_with_input(
BenchmarkId::new("total", *label),
&size,
|b, &s| {
b.iter(|| {
let out = write_many_streams(
black_box(n),
black_box(s),
buf.map(black_box),
);
black_box(out);
})
},
);
let mut group = c.benchmark_group("write_streams_memory");
for (label, stream_size, stream_count) in stream_benches {
let total_bytes = (stream_count * stream_size) as u64;
group.sample_size(10);
if total_bytes > 0 {
group.throughput(Throughput::Bytes(total_bytes));
}
group.bench_function(label, |b| {
b.iter(|| {
let out = write_many_streams(
black_box(stream_count),
black_box(stream_size),
);
black_box(out);
})
});
}
medium.finish();
group.finish();

// single large stream with throughput reporting
let mut group = c.benchmark_group("write large stream");
let size = 256 * 1024 * 1024usize;
let n = 1;
group.sample_size(10);
group.throughput(Throughput::Bytes(size as u64));
for (buf, label) in buffer_sizes {
group.bench_with_input(
BenchmarkId::new("total", *label),
&size,
|b, &s| {
b.iter(|| {
let out = write_many_streams(
black_box(n),
black_box(s),
buf.map(black_box),
);
black_box(out);
})
},
);
let mut disk_group = c.benchmark_group("write_streams_disk");
for (label, stream_size, stream_count) in stream_benches {
let total_bytes = (stream_count * stream_size) as u64;
disk_group.sample_size(10);
if total_bytes > 0 {
disk_group.throughput(Throughput::Bytes(total_bytes));
}
disk_group.bench_function(label, |b| {
b.iter(|| {
write_many_streams_disk(
black_box(stream_count),
black_box(stream_size),
);
})
});
}
group.finish();
disk_group.finish();

// many small streams with throughput reporting (disk)
let mut small_disk =
c.benchmark_group("write many smaller streams (disk)");
let size = 64usize;
let n = 1000;
let total_bytes = (n * size) as u64;
small_disk.sample_size(10);
small_disk.throughput(Throughput::Bytes(total_bytes));
for (buf, label) in buffer_sizes {
small_disk.bench_with_input(
BenchmarkId::new("total", *label),
&size,
|b, &s| {
b.iter(|| {
write_many_streams_disk(
black_box(n),
black_box(s),
buf.map(black_box),
);
})
},
);
let mut read_group = c.benchmark_group("read_streams_memory");
for (label, stream_size, stream_count) in stream_benches {
let total_bytes = (stream_count * stream_size) as u64;
let buff = write_many_streams(stream_count, stream_size);
read_group.sample_size(10);
if total_bytes > 0 {
read_group.throughput(Throughput::Bytes(total_bytes));
}
read_group.bench_function(label, |b| {
b.iter(|| {
read_many_streams(black_box(&buff), black_box(stream_count));
})
});
}
small_disk.finish();
read_group.finish();
}

criterion_group!(benches, criterion_benchmark);
Expand Down
2 changes: 2 additions & 0 deletions src/internal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod objtype;
pub mod path;
mod sector;
mod stream;
mod stream_buffer;
mod timestamp;
mod validate;
mod version;
Expand All @@ -31,6 +32,7 @@ pub use self::minichain::MiniChain;
pub use self::objtype::ObjType;
pub use self::sector::{Sector, SectorInit, Sectors};
pub use self::stream::Stream;
pub(crate) use self::stream_buffer::DEFAULT_STREAM_MAX_BUFFER_SIZE;
pub use self::timestamp::Timestamp;
pub use self::validate::Validation;
pub use self::version::Version;
Loading