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