benchmarks.rs 6.4 KB

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