1 | // Copyright 2020 Google Inc. All Rights Reserved. |
2 | // |
3 | // Redistribution and use in source and binary forms, with or without |
4 | // modification, are permitted provided that the following conditions are |
5 | // met: |
6 | // |
7 | // * Redistributions of source code must retain the above copyright |
8 | // notice, this list of conditions and the following disclaimer. |
9 | // * Redistributions in binary form must reproduce the above |
10 | // copyright notice, this list of conditions and the following disclaimer |
11 | // in the documentation and/or other materials provided with the |
12 | // distribution. |
13 | // * Neither the name of Google Inc. nor the names of its |
14 | // contributors may be used to endorse or promote products derived from |
15 | // this software without specific prior written permission. |
16 | // |
17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
21 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
22 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
23 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
27 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | |
29 | #include <cstddef> |
30 | #include <cstdint> |
31 | #include <string> |
32 | #include <vector> |
33 | |
34 | #include "snappy-test.h" |
35 | |
36 | #include "benchmark/benchmark.h" |
37 | |
38 | #include "snappy-internal.h" |
39 | #include "snappy-sinksource.h" |
40 | #include "snappy.h" |
41 | #include "snappy_test_data.h" |
42 | |
43 | namespace snappy { |
44 | |
45 | namespace { |
46 | |
47 | void BM_UFlat(benchmark::State& state) { |
48 | // Pick file to process based on state.range(0). |
49 | int file_index = state.range(0); |
50 | |
51 | CHECK_GE(file_index, 0); |
52 | CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); |
53 | std::string contents = |
54 | ReadTestDataFile(kTestDataFiles[file_index].filename, |
55 | kTestDataFiles[file_index].size_limit); |
56 | |
57 | std::string zcontents; |
58 | snappy::Compress(contents.data(), contents.size(), &zcontents); |
59 | char* dst = new char[contents.size()]; |
60 | |
61 | for (auto s : state) { |
62 | CHECK(snappy::RawUncompress(zcontents.data(), zcontents.size(), dst)); |
63 | benchmark::DoNotOptimize(dst); |
64 | } |
65 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
66 | static_cast<int64_t>(contents.size())); |
67 | state.SetLabel(kTestDataFiles[file_index].label); |
68 | |
69 | delete[] dst; |
70 | } |
71 | BENCHMARK(BM_UFlat)->DenseRange(0, ARRAYSIZE(kTestDataFiles) - 1); |
72 | |
73 | struct SourceFiles { |
74 | SourceFiles() { |
75 | for (int i = 0; i < kFiles; i++) { |
76 | std::string contents = ReadTestDataFile(kTestDataFiles[i].filename, |
77 | kTestDataFiles[i].size_limit); |
78 | max_size = std::max(max_size, contents.size()); |
79 | sizes[i] = contents.size(); |
80 | snappy::Compress(contents.data(), contents.size(), &zcontents[i]); |
81 | } |
82 | } |
83 | static constexpr int kFiles = ARRAYSIZE(kTestDataFiles); |
84 | std::string zcontents[kFiles]; |
85 | size_t sizes[kFiles]; |
86 | size_t max_size = 0; |
87 | }; |
88 | |
89 | void BM_UFlatMedley(benchmark::State& state) { |
90 | static const SourceFiles* const source = new SourceFiles(); |
91 | |
92 | std::vector<char> dst(source->max_size); |
93 | |
94 | for (auto s : state) { |
95 | for (int i = 0; i < SourceFiles::kFiles; i++) { |
96 | CHECK(snappy::RawUncompress(source->zcontents[i].data(), |
97 | source->zcontents[i].size(), dst.data())); |
98 | benchmark::DoNotOptimize(dst); |
99 | } |
100 | } |
101 | |
102 | int64_t source_sizes = 0; |
103 | for (int i = 0; i < SourceFiles::kFiles; i++) { |
104 | source_sizes += static_cast<int64_t>(source->sizes[i]); |
105 | } |
106 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
107 | source_sizes); |
108 | } |
109 | BENCHMARK(BM_UFlatMedley); |
110 | |
111 | void BM_UValidate(benchmark::State& state) { |
112 | // Pick file to process based on state.range(0). |
113 | int file_index = state.range(0); |
114 | |
115 | CHECK_GE(file_index, 0); |
116 | CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); |
117 | std::string contents = |
118 | ReadTestDataFile(kTestDataFiles[file_index].filename, |
119 | kTestDataFiles[file_index].size_limit); |
120 | |
121 | std::string zcontents; |
122 | snappy::Compress(contents.data(), contents.size(), &zcontents); |
123 | |
124 | for (auto s : state) { |
125 | CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size())); |
126 | } |
127 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
128 | static_cast<int64_t>(contents.size())); |
129 | state.SetLabel(kTestDataFiles[file_index].label); |
130 | } |
131 | BENCHMARK(BM_UValidate)->DenseRange(0, ARRAYSIZE(kTestDataFiles) - 1); |
132 | |
133 | void BM_UValidateMedley(benchmark::State& state) { |
134 | static const SourceFiles* const source = new SourceFiles(); |
135 | |
136 | for (auto s : state) { |
137 | for (int i = 0; i < SourceFiles::kFiles; i++) { |
138 | CHECK(snappy::IsValidCompressedBuffer(source->zcontents[i].data(), |
139 | source->zcontents[i].size())); |
140 | } |
141 | } |
142 | |
143 | int64_t source_sizes = 0; |
144 | for (int i = 0; i < SourceFiles::kFiles; i++) { |
145 | source_sizes += static_cast<int64_t>(source->sizes[i]); |
146 | } |
147 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
148 | source_sizes); |
149 | } |
150 | BENCHMARK(BM_UValidateMedley); |
151 | |
152 | void BM_UIOVecSource(benchmark::State& state) { |
153 | // Pick file to process based on state.range(0). |
154 | int file_index = state.range(0); |
155 | |
156 | CHECK_GE(file_index, 0); |
157 | CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); |
158 | std::string contents = |
159 | ReadTestDataFile(kTestDataFiles[file_index].filename, |
160 | kTestDataFiles[file_index].size_limit); |
161 | |
162 | // Create `iovec`s of the `contents`. |
163 | const int kNumEntries = 10; |
164 | struct iovec iov[kNumEntries]; |
165 | size_t used_so_far = 0; |
166 | for (int i = 0; i < kNumEntries; ++i) { |
167 | iov[i].iov_base = const_cast<char*>(contents.data()) + used_so_far; |
168 | if (used_so_far == contents.size()) { |
169 | iov[i].iov_len = 0; |
170 | continue; |
171 | } |
172 | if (i == kNumEntries - 1) { |
173 | iov[i].iov_len = contents.size() - used_so_far; |
174 | } else { |
175 | iov[i].iov_len = contents.size() / kNumEntries; |
176 | } |
177 | used_so_far += iov[i].iov_len; |
178 | } |
179 | |
180 | char* dst = new char[snappy::MaxCompressedLength(contents.size())]; |
181 | size_t zsize = 0; |
182 | for (auto s : state) { |
183 | snappy::RawCompressFromIOVec(iov, contents.size(), dst, &zsize); |
184 | benchmark::DoNotOptimize(iov); |
185 | } |
186 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
187 | static_cast<int64_t>(contents.size())); |
188 | const double compression_ratio = |
189 | static_cast<double>(zsize) / std::max<size_t>(1, contents.size()); |
190 | state.SetLabel(StrFormat("%s (%.2f %%)" , kTestDataFiles[file_index].label, |
191 | 100.0 * compression_ratio)); |
192 | VLOG(0) << StrFormat("compression for %s: %d -> %d bytes" , |
193 | kTestDataFiles[file_index].label, contents.size(), |
194 | zsize); |
195 | |
196 | delete[] dst; |
197 | } |
198 | BENCHMARK(BM_UIOVecSource)->DenseRange(0, ARRAYSIZE(kTestDataFiles) - 1); |
199 | |
200 | void BM_UIOVecSink(benchmark::State& state) { |
201 | // Pick file to process based on state.range(0). |
202 | int file_index = state.range(0); |
203 | |
204 | CHECK_GE(file_index, 0); |
205 | CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); |
206 | std::string contents = |
207 | ReadTestDataFile(kTestDataFiles[file_index].filename, |
208 | kTestDataFiles[file_index].size_limit); |
209 | |
210 | std::string zcontents; |
211 | snappy::Compress(contents.data(), contents.size(), &zcontents); |
212 | |
213 | // Uncompress into an iovec containing ten entries. |
214 | const int kNumEntries = 10; |
215 | struct iovec iov[kNumEntries]; |
216 | char *dst = new char[contents.size()]; |
217 | size_t used_so_far = 0; |
218 | for (int i = 0; i < kNumEntries; ++i) { |
219 | iov[i].iov_base = dst + used_so_far; |
220 | if (used_so_far == contents.size()) { |
221 | iov[i].iov_len = 0; |
222 | continue; |
223 | } |
224 | |
225 | if (i == kNumEntries - 1) { |
226 | iov[i].iov_len = contents.size() - used_so_far; |
227 | } else { |
228 | iov[i].iov_len = contents.size() / kNumEntries; |
229 | } |
230 | used_so_far += iov[i].iov_len; |
231 | } |
232 | |
233 | for (auto s : state) { |
234 | CHECK(snappy::RawUncompressToIOVec(zcontents.data(), zcontents.size(), iov, |
235 | kNumEntries)); |
236 | benchmark::DoNotOptimize(iov); |
237 | } |
238 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
239 | static_cast<int64_t>(contents.size())); |
240 | state.SetLabel(kTestDataFiles[file_index].label); |
241 | |
242 | delete[] dst; |
243 | } |
244 | BENCHMARK(BM_UIOVecSink)->DenseRange(0, 4); |
245 | |
246 | void BM_UFlatSink(benchmark::State& state) { |
247 | // Pick file to process based on state.range(0). |
248 | int file_index = state.range(0); |
249 | |
250 | CHECK_GE(file_index, 0); |
251 | CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); |
252 | std::string contents = |
253 | ReadTestDataFile(kTestDataFiles[file_index].filename, |
254 | kTestDataFiles[file_index].size_limit); |
255 | |
256 | std::string zcontents; |
257 | snappy::Compress(contents.data(), contents.size(), &zcontents); |
258 | char* dst = new char[contents.size()]; |
259 | |
260 | for (auto s : state) { |
261 | snappy::ByteArraySource source(zcontents.data(), zcontents.size()); |
262 | snappy::UncheckedByteArraySink sink(dst); |
263 | CHECK(snappy::Uncompress(&source, &sink)); |
264 | benchmark::DoNotOptimize(sink); |
265 | } |
266 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
267 | static_cast<int64_t>(contents.size())); |
268 | state.SetLabel(kTestDataFiles[file_index].label); |
269 | |
270 | std::string s(dst, contents.size()); |
271 | CHECK_EQ(contents, s); |
272 | |
273 | delete[] dst; |
274 | } |
275 | |
276 | BENCHMARK(BM_UFlatSink)->DenseRange(0, ARRAYSIZE(kTestDataFiles) - 1); |
277 | |
278 | void BM_ZFlat(benchmark::State& state) { |
279 | // Pick file to process based on state.range(0). |
280 | int file_index = state.range(0); |
281 | |
282 | CHECK_GE(file_index, 0); |
283 | CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); |
284 | std::string contents = |
285 | ReadTestDataFile(kTestDataFiles[file_index].filename, |
286 | kTestDataFiles[file_index].size_limit); |
287 | char* dst = new char[snappy::MaxCompressedLength(contents.size())]; |
288 | |
289 | size_t zsize = 0; |
290 | for (auto s : state) { |
291 | snappy::RawCompress(contents.data(), contents.size(), dst, &zsize); |
292 | benchmark::DoNotOptimize(dst); |
293 | } |
294 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
295 | static_cast<int64_t>(contents.size())); |
296 | const double compression_ratio = |
297 | static_cast<double>(zsize) / std::max<size_t>(1, contents.size()); |
298 | state.SetLabel(StrFormat("%s (%.2f %%)" , kTestDataFiles[file_index].label, |
299 | 100.0 * compression_ratio)); |
300 | VLOG(0) << StrFormat("compression for %s: %d -> %d bytes" , |
301 | kTestDataFiles[file_index].label, contents.size(), |
302 | zsize); |
303 | delete[] dst; |
304 | } |
305 | BENCHMARK(BM_ZFlat)->DenseRange(0, ARRAYSIZE(kTestDataFiles) - 1); |
306 | |
307 | void BM_ZFlatAll(benchmark::State& state) { |
308 | const int num_files = ARRAYSIZE(kTestDataFiles); |
309 | |
310 | std::vector<std::string> contents(num_files); |
311 | std::vector<char*> dst(num_files); |
312 | |
313 | int64_t total_contents_size = 0; |
314 | for (int i = 0; i < num_files; ++i) { |
315 | contents[i] = ReadTestDataFile(kTestDataFiles[i].filename, |
316 | kTestDataFiles[i].size_limit); |
317 | dst[i] = new char[snappy::MaxCompressedLength(contents[i].size())]; |
318 | total_contents_size += contents[i].size(); |
319 | } |
320 | |
321 | size_t zsize = 0; |
322 | for (auto s : state) { |
323 | for (int i = 0; i < num_files; ++i) { |
324 | snappy::RawCompress(contents[i].data(), contents[i].size(), dst[i], |
325 | &zsize); |
326 | benchmark::DoNotOptimize(dst); |
327 | } |
328 | } |
329 | |
330 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
331 | total_contents_size); |
332 | |
333 | for (char* dst_item : dst) { |
334 | delete[] dst_item; |
335 | } |
336 | state.SetLabel(StrFormat("%d kTestDataFiles" , num_files)); |
337 | } |
338 | BENCHMARK(BM_ZFlatAll); |
339 | |
340 | void BM_ZFlatIncreasingTableSize(benchmark::State& state) { |
341 | CHECK_GT(ARRAYSIZE(kTestDataFiles), 0); |
342 | const std::string base_content = ReadTestDataFile( |
343 | kTestDataFiles[0].filename, kTestDataFiles[0].size_limit); |
344 | |
345 | std::vector<std::string> contents; |
346 | std::vector<char*> dst; |
347 | int64_t total_contents_size = 0; |
348 | for (int table_bits = kMinHashTableBits; table_bits <= kMaxHashTableBits; |
349 | ++table_bits) { |
350 | std::string content = base_content; |
351 | content.resize(1 << table_bits); |
352 | dst.push_back(new char[snappy::MaxCompressedLength(content.size())]); |
353 | total_contents_size += content.size(); |
354 | contents.push_back(std::move(content)); |
355 | } |
356 | |
357 | size_t zsize = 0; |
358 | for (auto s : state) { |
359 | for (size_t i = 0; i < contents.size(); ++i) { |
360 | snappy::RawCompress(contents[i].data(), contents[i].size(), dst[i], |
361 | &zsize); |
362 | benchmark::DoNotOptimize(dst); |
363 | } |
364 | } |
365 | |
366 | state.SetBytesProcessed(static_cast<int64_t>(state.iterations()) * |
367 | total_contents_size); |
368 | |
369 | for (char* dst_item : dst) { |
370 | delete[] dst_item; |
371 | } |
372 | state.SetLabel(StrFormat("%d tables" , contents.size())); |
373 | } |
374 | BENCHMARK(BM_ZFlatIncreasingTableSize); |
375 | |
376 | } // namespace |
377 | |
378 | } // namespace snappy |
379 | |