1 extern crate base64;
2 #[macro_use]
3 extern crate criterion;
4 extern crate rand;
5
6 use base64::display;
7 use base64::{
8 decode, decode_config_buf, decode_config_slice, encode, encode_config_buf, encode_config_slice,
9 write, Config,
10 };
11
12 use criterion::{black_box, Bencher, Criterion, ParameterizedBenchmark, Throughput};
13 use rand::{FromEntropy, Rng};
14 use std::io::Write;
15
16 const TEST_CONFIG: Config = base64::STANDARD;
17
do_decode_bench(b: &mut Bencher, &size: &usize)18 fn do_decode_bench(b: &mut Bencher, &size: &usize) {
19 let mut v: Vec<u8> = Vec::with_capacity(size * 3 / 4);
20 fill(&mut v);
21 let encoded = encode(&v);
22
23 b.iter(|| {
24 let orig = decode(&encoded);
25 black_box(&orig);
26 });
27 }
28
do_decode_bench_reuse_buf(b: &mut Bencher, &size: &usize)29 fn do_decode_bench_reuse_buf(b: &mut Bencher, &size: &usize) {
30 let mut v: Vec<u8> = Vec::with_capacity(size * 3 / 4);
31 fill(&mut v);
32 let encoded = encode(&v);
33
34 let mut buf = Vec::new();
35 b.iter(|| {
36 decode_config_buf(&encoded, TEST_CONFIG, &mut buf).unwrap();
37 black_box(&buf);
38 buf.clear();
39 });
40 }
41
do_decode_bench_slice(b: &mut Bencher, &size: &usize)42 fn do_decode_bench_slice(b: &mut Bencher, &size: &usize) {
43 let mut v: Vec<u8> = Vec::with_capacity(size * 3 / 4);
44 fill(&mut v);
45 let encoded = encode(&v);
46
47 let mut buf = Vec::new();
48 buf.resize(size, 0);
49 b.iter(|| {
50 decode_config_slice(&encoded, TEST_CONFIG, &mut buf).unwrap();
51 black_box(&buf);
52 });
53 }
54
do_encode_bench(b: &mut Bencher, &size: &usize)55 fn do_encode_bench(b: &mut Bencher, &size: &usize) {
56 let mut v: Vec<u8> = Vec::with_capacity(size);
57 fill(&mut v);
58 b.iter(|| {
59 let e = encode(&v);
60 black_box(&e);
61 });
62 }
63
do_encode_bench_display(b: &mut Bencher, &size: &usize)64 fn do_encode_bench_display(b: &mut Bencher, &size: &usize) {
65 let mut v: Vec<u8> = Vec::with_capacity(size);
66 fill(&mut v);
67 b.iter(|| {
68 let e = format!("{}", display::Base64Display::with_config(&v, TEST_CONFIG));
69 black_box(&e);
70 });
71 }
72
do_encode_bench_reuse_buf(b: &mut Bencher, &size: &usize)73 fn do_encode_bench_reuse_buf(b: &mut Bencher, &size: &usize) {
74 let mut v: Vec<u8> = Vec::with_capacity(size);
75 fill(&mut v);
76 let mut buf = String::new();
77 b.iter(|| {
78 encode_config_buf(&v, TEST_CONFIG, &mut buf);
79 buf.clear();
80 });
81 }
82
do_encode_bench_slice(b: &mut Bencher, &size: &usize)83 fn do_encode_bench_slice(b: &mut Bencher, &size: &usize) {
84 let mut v: Vec<u8> = Vec::with_capacity(size);
85 fill(&mut v);
86 let mut buf = Vec::new();
87 // conservative estimate of encoded size
88 buf.resize(v.len() * 2, 0);
89 b.iter(|| {
90 encode_config_slice(&v, TEST_CONFIG, &mut buf);
91 });
92 }
93
do_encode_bench_stream(b: &mut Bencher, &size: &usize)94 fn do_encode_bench_stream(b: &mut Bencher, &size: &usize) {
95 let mut v: Vec<u8> = Vec::with_capacity(size);
96 fill(&mut v);
97 let mut buf = Vec::new();
98
99 buf.reserve(size * 2);
100 b.iter(|| {
101 buf.clear();
102 let mut stream_enc = write::EncoderWriter::new(&mut buf, TEST_CONFIG);
103 stream_enc.write_all(&v).unwrap();
104 stream_enc.flush().unwrap();
105 });
106 }
107
fill(v: &mut Vec<u8>)108 fn fill(v: &mut Vec<u8>) {
109 let cap = v.capacity();
110 // weak randomness is plenty; we just want to not be completely friendly to the branch predictor
111 let mut r = rand::rngs::SmallRng::from_entropy();
112 while v.len() < cap {
113 v.push(r.gen::<u8>());
114 }
115 }
116
117 const BYTE_SIZES: [usize; 5] = [3, 50, 100, 500, 3 * 1024];
118
119 // Benchmarks over these byte sizes take longer so we will run fewer samples to
120 // keep the benchmark runtime reasonable.
121 const LARGE_BYTE_SIZES: [usize; 3] = [3 * 1024 * 1024, 10 * 1024 * 1024, 30 * 1024 * 1024];
122
encode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize>123 fn encode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize> {
124 ParameterizedBenchmark::new("encode", do_encode_bench, byte_sizes.iter().cloned())
125 .warm_up_time(std::time::Duration::from_millis(500))
126 .measurement_time(std::time::Duration::from_secs(3))
127 .throughput(|s| Throughput::Bytes(*s as u64))
128 .with_function("encode_display", do_encode_bench_display)
129 .with_function("encode_reuse_buf", do_encode_bench_reuse_buf)
130 .with_function("encode_slice", do_encode_bench_slice)
131 .with_function("encode_reuse_buf_stream", do_encode_bench_stream)
132 }
133
decode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize>134 fn decode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize> {
135 ParameterizedBenchmark::new("decode", do_decode_bench, byte_sizes.iter().cloned())
136 .warm_up_time(std::time::Duration::from_millis(500))
137 .measurement_time(std::time::Duration::from_secs(3))
138 .throughput(|s| Throughput::Bytes(*s as u64))
139 .with_function("decode_reuse_buf", do_decode_bench_reuse_buf)
140 .with_function("decode_slice", do_decode_bench_slice)
141 }
142
bench(c: &mut Criterion)143 fn bench(c: &mut Criterion) {
144 c.bench("bench_small_input", encode_benchmarks(&BYTE_SIZES[..]));
145
146 c.bench(
147 "bench_large_input",
148 encode_benchmarks(&LARGE_BYTE_SIZES[..]).sample_size(10),
149 );
150
151 c.bench("bench_small_input", decode_benchmarks(&BYTE_SIZES[..]));
152
153 c.bench(
154 "bench_large_input",
155 decode_benchmarks(&LARGE_BYTE_SIZES[..]).sample_size(10),
156 );
157 }
158
159 criterion_group!(benches, bench);
160 criterion_main!(benches);
161