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