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