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::{self, Read, 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_decode_bench_stream(b: &mut Bencher, &size: &usize)55 fn do_decode_bench_stream(b: &mut Bencher, &size: &usize) {
56     let mut v: Vec<u8> = Vec::with_capacity(size * 3 / 4);
57     fill(&mut v);
58     let encoded = encode(&v);
59 
60     let mut buf = Vec::new();
61     buf.resize(size, 0);
62     buf.truncate(0);
63 
64     b.iter(|| {
65         let mut cursor = io::Cursor::new(&encoded[..]);
66         let mut decoder = base64::read::DecoderReader::new(&mut cursor, TEST_CONFIG);
67         decoder.read_to_end(&mut buf).unwrap();
68         buf.clear();
69         black_box(&buf);
70     });
71 }
72 
do_encode_bench(b: &mut Bencher, &size: &usize)73 fn do_encode_bench(b: &mut Bencher, &size: &usize) {
74     let mut v: Vec<u8> = Vec::with_capacity(size);
75     fill(&mut v);
76     b.iter(|| {
77         let e = encode(&v);
78         black_box(&e);
79     });
80 }
81 
do_encode_bench_display(b: &mut Bencher, &size: &usize)82 fn do_encode_bench_display(b: &mut Bencher, &size: &usize) {
83     let mut v: Vec<u8> = Vec::with_capacity(size);
84     fill(&mut v);
85     b.iter(|| {
86         let e = format!("{}", display::Base64Display::with_config(&v, TEST_CONFIG));
87         black_box(&e);
88     });
89 }
90 
do_encode_bench_reuse_buf(b: &mut Bencher, &size: &usize)91 fn do_encode_bench_reuse_buf(b: &mut Bencher, &size: &usize) {
92     let mut v: Vec<u8> = Vec::with_capacity(size);
93     fill(&mut v);
94     let mut buf = String::new();
95     b.iter(|| {
96         encode_config_buf(&v, TEST_CONFIG, &mut buf);
97         buf.clear();
98     });
99 }
100 
do_encode_bench_slice(b: &mut Bencher, &size: &usize)101 fn do_encode_bench_slice(b: &mut Bencher, &size: &usize) {
102     let mut v: Vec<u8> = Vec::with_capacity(size);
103     fill(&mut v);
104     let mut buf = Vec::new();
105     // conservative estimate of encoded size
106     buf.resize(v.len() * 2, 0);
107     b.iter(|| {
108         encode_config_slice(&v, TEST_CONFIG, &mut buf);
109     });
110 }
111 
do_encode_bench_stream(b: &mut Bencher, &size: &usize)112 fn do_encode_bench_stream(b: &mut Bencher, &size: &usize) {
113     let mut v: Vec<u8> = Vec::with_capacity(size);
114     fill(&mut v);
115     let mut buf = Vec::new();
116 
117     buf.reserve(size * 2);
118     b.iter(|| {
119         buf.clear();
120         let mut stream_enc = write::EncoderWriter::new(&mut buf, TEST_CONFIG);
121         stream_enc.write_all(&v).unwrap();
122         stream_enc.flush().unwrap();
123     });
124 }
125 
do_encode_bench_string_stream(b: &mut Bencher, &size: &usize)126 fn do_encode_bench_string_stream(b: &mut Bencher, &size: &usize) {
127     let mut v: Vec<u8> = Vec::with_capacity(size);
128     fill(&mut v);
129 
130     b.iter(|| {
131         let mut stream_enc = write::EncoderStringWriter::new(TEST_CONFIG);
132         stream_enc.write_all(&v).unwrap();
133         stream_enc.flush().unwrap();
134         let _ = stream_enc.into_inner();
135     });
136 }
137 
do_encode_bench_string_reuse_buf_stream(b: &mut Bencher, &size: &usize)138 fn do_encode_bench_string_reuse_buf_stream(b: &mut Bencher, &size: &usize) {
139     let mut v: Vec<u8> = Vec::with_capacity(size);
140     fill(&mut v);
141 
142     let mut buf = String::new();
143     b.iter(|| {
144         buf.clear();
145         let mut stream_enc = write::EncoderStringWriter::from(&mut buf, TEST_CONFIG);
146         stream_enc.write_all(&v).unwrap();
147         stream_enc.flush().unwrap();
148         let _ = stream_enc.into_inner();
149     });
150 }
151 
fill(v: &mut Vec<u8>)152 fn fill(v: &mut Vec<u8>) {
153     let cap = v.capacity();
154     // weak randomness is plenty; we just want to not be completely friendly to the branch predictor
155     let mut r = rand::rngs::SmallRng::from_entropy();
156     while v.len() < cap {
157         v.push(r.gen::<u8>());
158     }
159 }
160 
161 const BYTE_SIZES: [usize; 5] = [3, 50, 100, 500, 3 * 1024];
162 
163 // Benchmarks over these byte sizes take longer so we will run fewer samples to
164 // keep the benchmark runtime reasonable.
165 const LARGE_BYTE_SIZES: [usize; 3] = [3 * 1024 * 1024, 10 * 1024 * 1024, 30 * 1024 * 1024];
166 
encode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize>167 fn encode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize> {
168     ParameterizedBenchmark::new("encode", do_encode_bench, byte_sizes.iter().cloned())
169         .warm_up_time(std::time::Duration::from_millis(500))
170         .measurement_time(std::time::Duration::from_secs(3))
171         .throughput(|s| Throughput::Bytes(*s as u64))
172         .with_function("encode_display", do_encode_bench_display)
173         .with_function("encode_reuse_buf", do_encode_bench_reuse_buf)
174         .with_function("encode_slice", do_encode_bench_slice)
175         .with_function("encode_reuse_buf_stream", do_encode_bench_stream)
176         .with_function("encode_string_stream", do_encode_bench_string_stream)
177         .with_function(
178             "encode_string_reuse_buf_stream",
179             do_encode_bench_string_reuse_buf_stream,
180         )
181 }
182 
decode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize>183 fn decode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize> {
184     ParameterizedBenchmark::new("decode", do_decode_bench, byte_sizes.iter().cloned())
185         .warm_up_time(std::time::Duration::from_millis(500))
186         .measurement_time(std::time::Duration::from_secs(3))
187         .throughput(|s| Throughput::Bytes(*s as u64))
188         .with_function("decode_reuse_buf", do_decode_bench_reuse_buf)
189         .with_function("decode_slice", do_decode_bench_slice)
190         .with_function("decode_stream", do_decode_bench_stream)
191 }
192 
bench(c: &mut Criterion)193 fn bench(c: &mut Criterion) {
194     c.bench("bench_small_input", encode_benchmarks(&BYTE_SIZES[..]));
195 
196     c.bench(
197         "bench_large_input",
198         encode_benchmarks(&LARGE_BYTE_SIZES[..]).sample_size(10),
199     );
200 
201     c.bench("bench_small_input", decode_benchmarks(&BYTE_SIZES[..]));
202 
203     c.bench(
204         "bench_large_input",
205         decode_benchmarks(&LARGE_BYTE_SIZES[..]).sample_size(10),
206     );
207 }
208 
209 criterion_group!(benches, bench);
210 criterion_main!(benches);
211