use craft_codec::{
Compression, Config, Decoder, Encoder, Encryption, FrameSizes, Framing, Metadata,
};
use stats_alloc::{INSTRUMENTED_SYSTEM, Region, StatsAlloc};
use std::{
alloc::System,
hint::black_box,
time::{Duration, Instant},
};
#[global_allocator]
static GLOBAL: &StatsAlloc<System> = &INSTRUMENTED_SYSTEM;
fn measure(mut f: impl FnMut()) -> (u64, Duration) {
let start = Instant::now();
let mut count = 0;
loop {
for _ in 0..16 {
f();
count += 1;
}
if start.elapsed() >= Duration::from_millis(25) {
return (count, start.elapsed());
}
}
}
fn report(
profile: &str,
corpus: &str,
operation: &str,
size: usize,
stored: usize,
samples: (u64, Duration),
allocation_counts: (usize, usize),
) {
let (iterations, elapsed) = samples;
let (allocations, reallocations) = allocation_counts;
let ns = elapsed.as_secs_f64() * 1e9 / iterations as f64;
let mib = size as f64 * iterations as f64 / elapsed.as_secs_f64() / 1_048_576.0;
println!(
"{profile},{corpus},{operation},{size},{stored},{iterations},{ns:.0},{mib:.2},{allocations},{reallocations}"
);
}
fn bench(
profile: &str,
corpus: &str,
raw: &[u8],
compression: Compression,
encryption: Encryption,
) {
let mut metadata = Metadata::new(
Config::new(Framing::Fixed(raw.len() as u64), compression, encryption).unwrap(),
);
let mut encoder = Encoder::try_from(&metadata).unwrap();
let mut decoder = Decoder::try_from(&metadata).unwrap();
let mut buffer = raw.to_vec();
let sizes = encoder.encode_frame(0, &mut buffer).unwrap();
let encoded = buffer.clone();
let stored = encoded.len();
metadata.push(sizes).unwrap();
let spec = metadata.frame(0).unwrap();
let mut encode = || {
buffer.clear();
buffer.extend_from_slice(raw);
black_box(encoder.encode_frame(0, black_box(&mut buffer)).unwrap());
};
for _ in 0..8 {
encode();
}
let region = Region::new(GLOBAL);
let samples = measure(encode);
let stats = region.change();
report(
profile,
corpus,
"encode_with_input_copy",
raw.len(),
stored,
samples,
(stats.allocations, stats.reallocations),
);
let mut decode = || {
buffer.clear();
buffer.extend_from_slice(&encoded);
decoder
.decode_frame(spec.clone(), black_box(&mut buffer))
.unwrap();
black_box(&buffer);
};
for _ in 0..8 {
decode();
}
let region = Region::new(GLOBAL);
let samples = measure(decode);
let stats = region.change();
report(
profile,
corpus,
"decode_with_input_copy",
raw.len(),
stored,
samples,
(stats.allocations, stats.reallocations),
);
}
fn direct(corpus: &str, raw: &[u8]) {
#[cfg(feature = "aes-gcm")]
{
use aes_gcm::{KeyInit, aead::AeadInOut};
let cipher = aes_gcm::Aes256Gcm::new((&[3u8; 32]).into());
let mut buffer = raw.to_vec();
let region = Region::new(GLOBAL);
let samples = measure(|| {
buffer.copy_from_slice(raw);
black_box(
cipher
.encrypt_inout_detached(
&[0u8; 12].into(),
b"",
black_box(buffer.as_mut_slice()).into(),
)
.unwrap(),
);
});
report(
"direct_aes",
corpus,
"encode_with_input_copy",
raw.len(),
raw.len() + 16,
samples,
(region.change().allocations, region.change().reallocations),
);
}
#[cfg(feature = "lz4")]
{
use lz4_flex::block::{CompressTable, compress_into_with_table, get_maximum_output_size};
let mut table = CompressTable::large();
let mut output = vec![0; get_maximum_output_size(raw.len())];
let written = compress_into_with_table(raw, &mut output, &mut table).unwrap();
let region = Region::new(GLOBAL);
let samples = measure(|| {
black_box(compress_into_with_table(black_box(raw), &mut output, &mut table).unwrap());
});
report(
"direct_lz4",
corpus,
"compress_only_no_copy",
raw.len(),
written,
samples,
(region.change().allocations, region.change().reallocations),
);
}
let _ = (corpus, raw);
}
fn main() {
println!(
"profile,corpus,operation,raw_bytes,stored_bytes,iterations,ns_per_frame,mib_per_sec,allocations,reallocations"
);
for size in [4096, 16_384, 65_536, 262_144, 1_048_576] {
let mut random = 42u64;
let noise: Vec<u8> = (0..size)
.map(|_| {
random ^= random << 13;
random ^= random >> 7;
random ^= random << 17;
random as u8
})
.collect();
for (corpus, raw) in [
("repeated", vec![b'A'; size]),
("noise", noise.clone()),
(
"mixed",
[vec![b'A'; size / 2], noise[..size / 2].to_vec()].concat(),
),
] {
bench(
"identity",
corpus,
&raw,
Compression::None,
Encryption::None,
);
#[cfg(feature = "aes-gcm")]
bench(
"aes",
corpus,
&raw,
Compression::None,
Encryption::Aes256Gcm { key: [1; 32] },
);
#[cfg(feature = "lz4")]
bench("lz4", corpus, &raw, Compression::Lz4, Encryption::None);
#[cfg(all(feature = "aes-gcm", feature = "lz4"))]
bench(
"lz4_aes",
corpus,
&raw,
Compression::Lz4,
Encryption::Aes256Gcm { key: [2; 32] },
);
direct(corpus, &raw);
}
}
let config = craft_codec::Config::new(
Framing::Variable(65_536),
craft_codec::Compression::Lz4,
craft_codec::Encryption::None,
)
.unwrap();
let mut metadata = Metadata::new(config);
for _ in 0..16_384 {
metadata
.push(FrameSizes::new(65_536, 1000).unwrap())
.unwrap();
}
let region = Region::new(GLOBAL);
let samples = measure(|| {
black_box(metadata.range(900 * 1_048_576, Some(900 * 1_048_576 + 16))).unwrap();
});
let stats = region.change();
report(
"metadata",
"16384_frames",
"range_near_end",
0,
0,
samples,
(stats.allocations, stats.reallocations),
);
}