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/deflate_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 deflate_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 std::string doDeflateBeast(string_view const & in)68 doDeflateBeast(string_view const& in) 69 { 70 z_params zs; 71 deflate_stream ds; 72 ds.reset( 73 Z_DEFAULT_COMPRESSION, 74 15, 75 4, 76 Strategy::normal); 77 std::string out; 78 out.resize(deflate_upper_bound(in.size())); 79 zs.next_in = in.data(); 80 zs.avail_in = in.size(); 81 zs.next_out = &out[0]; 82 zs.avail_out = out.size(); 83 error_code ec; 84 ds.write(zs, Flush::full, ec); 85 BEAST_EXPECTS(! ec, ec.message()); 86 out.resize(zs.total_out); 87 return out; 88 } 89 90 std::string doDeflateZLib(string_view const & in)91 doDeflateZLib(string_view const& in) 92 { 93 int result; 94 z_stream zs; 95 memset(&zs, 0, sizeof(zs)); 96 result = deflateInit2( 97 &zs, 98 Z_DEFAULT_COMPRESSION, 99 Z_DEFLATED, 100 -15, 101 4, 102 Z_DEFAULT_STRATEGY 103 ); 104 if(result != Z_OK) 105 throw std::logic_error("deflateInit2 failed"); 106 std::string out; 107 out.resize(deflateBound(&zs, 108 static_cast<uLong>(in.size()))); 109 zs.next_in = (Bytef*)in.data(); 110 zs.avail_in = static_cast<uInt>(in.size()); 111 zs.next_out = (Bytef*)&out[0]; 112 zs.avail_out = static_cast<uInt>(out.size()); 113 result = deflate(&zs, Z_FULL_FLUSH); 114 if(result != Z_OK) 115 throw std::logic_error("deflate failed"); 116 out.resize(zs.total_out); 117 deflateEnd(&zs); 118 return out; 119 } 120 121 void doCorpus(std::size_t size,std::size_t repeat)122 doCorpus( 123 std::size_t size, 124 std::size_t repeat) 125 { 126 std::size_t constexpr trials = 3; 127 auto const c1 = corpus1(size); 128 auto const c2 = corpus2(size); 129 log << 130 std::left << std::setw(10) << (std::to_string(size) + "B") << 131 std::right << std::setw(12) << "Beast" << " " << 132 std::right << std::setw(12) << "ZLib" << 133 std::endl; 134 for(std::size_t i = 0; i < trials; ++i) 135 { 136 test::timer t; 137 log << std::left << std::setw(10) << "corpus1"; 138 std::string out1; 139 for(std::size_t j = 0; j < repeat; ++j) 140 out1 = doDeflateBeast(c1); 141 auto const t1 = 142 test::throughput(t.elapsed(), size * repeat); 143 log << std::right << std::setw(12) << t1 << " B/s "; 144 std::string out2; 145 for(std::size_t j = 0; j < repeat; ++j) 146 out2 = doDeflateZLib(c1); 147 BEAST_EXPECT(out1 == out2); 148 auto const t2 = 149 test::throughput(t.elapsed(), size * repeat); 150 log << std::right << std::setw(12) << t2 << " B/s"; 151 log << std::right << std::setw(12) << 152 unsigned(double(t1)*100/t2-100) << "%"; 153 log << std::endl; 154 } 155 for(std::size_t i = 0; i < trials; ++i) 156 { 157 test::timer t; 158 log << std::left << std::setw(10) << "corpus2"; 159 std::string out1; 160 for(std::size_t j = 0; j < repeat; ++j) 161 out1 = doDeflateBeast(c2); 162 auto const t1 = 163 test::throughput(t.elapsed(), size * repeat); 164 log << std::right << std::setw(12) << t1 << " B/s "; 165 std::string out2; 166 for(std::size_t j = 0; j < repeat; ++j) 167 out2 = doDeflateZLib(c2); 168 BEAST_EXPECT(out1 == out2); 169 auto const t2 = 170 test::throughput(t.elapsed(), size * repeat); 171 log << std::right << std::setw(12) << t2 << " B/s"; 172 log << std::right << std::setw(12) << 173 unsigned(double(t1)*100/t2-100) << "%"; 174 log << std::endl; 175 } 176 log << std::endl; 177 } 178 179 void doBench()180 doBench() 181 { 182 doCorpus( 16 * 1024, 512); 183 doCorpus( 1024 * 1024, 8); 184 doCorpus(8 * 1024 * 1024, 1); 185 } 186 187 void run()188 run() override 189 { 190 doBench(); 191 pass(); 192 } 193 }; 194 195 BEAST_DEFINE_TESTSUITE(beast,zlib,deflate_stream); 196 197 } // zlib 198 } // beast 199 } // boost 200