inflate_stream.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. //
  2. // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // Official repository: https://github.com/boostorg/beast
  8. //
  9. #include <boost/beast/core/string.hpp>
  10. #include <boost/beast/zlib/inflate_stream.hpp>
  11. #include <boost/beast/test/throughput.hpp>
  12. #include <boost/beast/_experimental/unit_test/dstream.hpp>
  13. #include <boost/beast/_experimental/unit_test/suite.hpp>
  14. #include <iomanip>
  15. #include <random>
  16. #include <string>
  17. #include "zlib-1.2.11/zlib.h"
  18. namespace boost {
  19. namespace beast {
  20. namespace zlib {
  21. class inflate_stream_test : public beast::unit_test::suite
  22. {
  23. public:
  24. // Lots of repeats, limited char range
  25. static
  26. std::string
  27. corpus1(std::size_t n)
  28. {
  29. static std::string const alphabet{
  30. "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  31. };
  32. std::string s;
  33. s.reserve(n + 5);
  34. std::mt19937 g;
  35. std::uniform_int_distribution<std::size_t> d0{
  36. 0, alphabet.size() - 1};
  37. std::uniform_int_distribution<std::size_t> d1{
  38. 1, 5};
  39. while(s.size() < n)
  40. {
  41. auto const rep = d1(g);
  42. auto const ch = alphabet[d0(g)];
  43. s.insert(s.end(), rep, ch);
  44. }
  45. s.resize(n);
  46. return s;
  47. }
  48. // Random data
  49. static
  50. std::string
  51. corpus2(std::size_t n)
  52. {
  53. std::string s;
  54. s.reserve(n);
  55. std::mt19937 g;
  56. std::uniform_int_distribution<std::uint32_t> d0{0, 255};
  57. while(n--)
  58. s.push_back(static_cast<char>(d0(g)));
  59. return s;
  60. }
  61. static
  62. std::string
  63. compress(string_view const& in)
  64. {
  65. int result;
  66. z_stream zs;
  67. memset(&zs, 0, sizeof(zs));
  68. result = deflateInit2(
  69. &zs,
  70. Z_DEFAULT_COMPRESSION,
  71. Z_DEFLATED,
  72. -15,
  73. 4,
  74. Z_DEFAULT_STRATEGY);
  75. if(result != Z_OK)
  76. throw std::logic_error("deflateInit2 failed");
  77. zs.next_in = (Bytef*)in.data();
  78. zs.avail_in = static_cast<uInt>(in.size());
  79. std::string out;
  80. out.resize(deflateBound(&zs,
  81. static_cast<uLong>(in.size())));
  82. zs.next_in = (Bytef*)in.data();
  83. zs.avail_in = static_cast<uInt>(in.size());
  84. zs.next_out = (Bytef*)&out[0];
  85. zs.avail_out = static_cast<uInt>(out.size());
  86. result = deflate(&zs, Z_FULL_FLUSH);
  87. if(result != Z_OK)
  88. throw std::logic_error("deflate failed");
  89. out.resize(zs.total_out);
  90. deflateEnd(&zs);
  91. return out;
  92. }
  93. std::string
  94. doInflateBeast(string_view const& in)
  95. {
  96. z_params zs;
  97. std::string out;
  98. inflate_stream is;
  99. zs.next_in = &in[0];
  100. zs.avail_in = in.size();
  101. out.resize(in.size());
  102. zs.next_out = &out[0];
  103. zs.avail_out = out.size();
  104. for(;;)
  105. {
  106. error_code ec;
  107. is.write(zs, Flush::sync, ec);
  108. if(ec)
  109. throw std::logic_error("inflate_stream failed");
  110. if(zs.avail_out > 0)
  111. break;
  112. out.resize(2 * zs.total_out);
  113. zs.next_out = &out[zs.total_out];
  114. zs.avail_out = out.size() - zs.total_out;
  115. }
  116. out.resize(zs.total_out);
  117. return out;
  118. }
  119. std::string
  120. doInflateZLib(string_view const& in)
  121. {
  122. int result;
  123. z_stream zs;
  124. std::string out;
  125. memset(&zs, 0, sizeof(zs));
  126. result = inflateInit2(&zs, -15);
  127. zs.next_in = (Bytef*)in.data();
  128. zs.avail_in = static_cast<uInt>(in.size());
  129. out.resize(in.size());
  130. zs.next_out = (Bytef*)&out[0];
  131. zs.avail_out = static_cast<uInt>(out.size());
  132. for(;;)
  133. {
  134. result = inflate(&zs, Z_SYNC_FLUSH);
  135. if( result == Z_NEED_DICT ||
  136. result == Z_DATA_ERROR ||
  137. result == Z_MEM_ERROR)
  138. {
  139. throw std::logic_error("inflate failed");
  140. }
  141. if(zs.avail_out > 0)
  142. break;
  143. if(result == Z_STREAM_END)
  144. break;
  145. out.resize(2 * zs.total_out);
  146. zs.next_out = (Bytef*)&out[zs.total_out];
  147. zs.avail_out = static_cast<uInt>(
  148. out.size() - zs.total_out);
  149. }
  150. out.resize(zs.total_out);
  151. inflateEnd(&zs);
  152. return out;
  153. }
  154. void
  155. doCorpus(
  156. std::size_t size,
  157. std::size_t repeat)
  158. {
  159. std::size_t constexpr trials = 3;
  160. std::uint64_t constexpr scale = 16;
  161. auto const c1 = corpus1(size);
  162. auto const c2 = corpus2(size * scale);
  163. auto const in1 = compress(c1);
  164. auto const in2 = compress(c2);
  165. log <<
  166. std::left << std::setw(10) << (std::to_string(size) + "B") <<
  167. std::right << std::setw(12) << "Beast" << " " <<
  168. std::right << std::setw(12) << "ZLib" <<
  169. std::endl;
  170. for(std::size_t i = 0; i < trials; ++i)
  171. {
  172. test::timer t;
  173. log << std::left << std::setw(10) << "corpus1";
  174. std::string out;
  175. for(std::size_t j = 0; j < repeat; ++j)
  176. out = doInflateBeast(in1);
  177. BEAST_EXPECT(out == c1);
  178. auto const t1 =
  179. test::throughput(t.elapsed(), size * repeat);
  180. log << std::right << std::setw(12) << t1 << " B/s ";
  181. for(std::size_t j = 0; j < repeat; ++j)
  182. out = doInflateZLib(in1);
  183. BEAST_EXPECT(out == c1);
  184. auto const t2 =
  185. test::throughput(t.elapsed(), size * repeat);
  186. log << std::right << std::setw(12) << t2 << " B/s";
  187. log << std::right << std::setw(12) <<
  188. unsigned(double(t1)*100/t2-100) << "%";
  189. log << std::endl;
  190. }
  191. for(std::size_t i = 0; i < trials; ++i)
  192. {
  193. test::timer t;
  194. log << std::left << std::setw(10) << "corpus2";
  195. std::string out;
  196. for(std::size_t j = 0; j < repeat; ++j)
  197. out = doInflateBeast(in2);
  198. BEAST_EXPECT(out == c2);
  199. auto const t1 =
  200. test::throughput(t.elapsed(), size * scale * repeat);
  201. log << std::right << std::setw(12) << t1 << " B/s ";
  202. for(std::size_t j = 0; j < repeat; ++j)
  203. out = doInflateZLib(in2);
  204. BEAST_EXPECT(out == c2);
  205. auto const t2 =
  206. test::throughput(t.elapsed(), size * scale * repeat);
  207. log << std::right << std::setw(12) << t2 << " B/s";
  208. log << std::right << std::setw(12) <<
  209. unsigned(double(t1)*100/t2-100) << "%";
  210. log << std::endl;
  211. }
  212. log << std::endl;
  213. }
  214. void
  215. doBench()
  216. {
  217. doCorpus( 1 * 1024 * 1024, 64);
  218. doCorpus( 4 * 1024 * 1024, 16);
  219. doCorpus( 16 * 1024 * 1024, 8);
  220. }
  221. void
  222. run() override
  223. {
  224. doBench();
  225. pass();
  226. }
  227. };
  228. BEAST_DEFINE_TESTSUITE(beast,zlib,inflate_stream);
  229. } // zlib
  230. } // beast
  231. } // boost