// Copyright 2020 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include "benchmark/benchmark.h" #include "snappy-internal.h" #include "snappy-sinksource.h" #include "snappy-test.h" #include "snappy.h" #include "snappy_test_data.h" namespace snappy { namespace { void FilesAndLevels(benchmark::internal::Benchmark* benchmark) { for (int i = 0; i < ARRAYSIZE(kTestDataFiles); ++i) { for (int level = snappy::CompressionOptions::MinCompressionLevel(); level <= snappy::CompressionOptions::MaxCompressionLevel(); ++level) { benchmark->ArgPair(i, level); } } } void BM_UFlat(benchmark::State& state) { // Pick file to process based on state.range(0). int file_index = state.range(0); CHECK_GE(file_index, 0); CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); std::string contents = ReadTestDataFile(kTestDataFiles[file_index].filename, kTestDataFiles[file_index].size_limit); std::string zcontents; snappy::Compress( contents.data(), contents.size(), &zcontents, snappy::CompressionOptions{/*level=*/static_cast(state.range(1))}); char* dst = new char[contents.size()]; for (auto s : state) { CHECK(snappy::RawUncompress(zcontents.data(), zcontents.size(), dst)); benchmark::DoNotOptimize(dst); } state.SetBytesProcessed(static_cast(state.iterations()) * static_cast(contents.size())); state.SetLabel(kTestDataFiles[file_index].label); delete[] dst; } BENCHMARK(BM_UFlat)->Apply(FilesAndLevels); struct SourceFiles { SourceFiles() { for (int i = 0; i < kFiles; i++) { std::string contents = ReadTestDataFile(kTestDataFiles[i].filename, kTestDataFiles[i].size_limit); max_size = std::max(max_size, contents.size()); sizes[i] = contents.size(); snappy::Compress(contents.data(), contents.size(), &zcontents[i]); } } static constexpr int kFiles = ARRAYSIZE(kTestDataFiles); std::string zcontents[kFiles]; size_t sizes[kFiles]; size_t max_size = 0; }; void BM_UFlatMedley(benchmark::State& state) { static const SourceFiles* const source = new SourceFiles(); std::vector dst(source->max_size); for (auto s : state) { for (int i = 0; i < SourceFiles::kFiles; i++) { CHECK(snappy::RawUncompress(source->zcontents[i].data(), source->zcontents[i].size(), dst.data())); benchmark::DoNotOptimize(dst); } } int64_t source_sizes = 0; for (int i = 0; i < SourceFiles::kFiles; i++) { source_sizes += static_cast(source->sizes[i]); } state.SetBytesProcessed(static_cast(state.iterations()) * source_sizes); } BENCHMARK(BM_UFlatMedley); void BM_UValidate(benchmark::State& state) { // Pick file to process based on state.range(0). int file_index = state.range(0); CHECK_GE(file_index, 0); CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); std::string contents = ReadTestDataFile(kTestDataFiles[file_index].filename, kTestDataFiles[file_index].size_limit); std::string zcontents; snappy::Compress( contents.data(), contents.size(), &zcontents, snappy::CompressionOptions{/*level=*/static_cast(state.range(1))}); for (auto s : state) { CHECK(snappy::IsValidCompressedBuffer(zcontents.data(), zcontents.size())); } state.SetBytesProcessed(static_cast(state.iterations()) * static_cast(contents.size())); state.SetLabel(kTestDataFiles[file_index].label); } BENCHMARK(BM_UValidate)->Apply(FilesAndLevels); void BM_UValidateMedley(benchmark::State& state) { static const SourceFiles* const source = new SourceFiles(); for (auto s : state) { for (int i = 0; i < SourceFiles::kFiles; i++) { CHECK(snappy::IsValidCompressedBuffer(source->zcontents[i].data(), source->zcontents[i].size())); } } int64_t source_sizes = 0; for (int i = 0; i < SourceFiles::kFiles; i++) { source_sizes += static_cast(source->sizes[i]); } state.SetBytesProcessed(static_cast(state.iterations()) * source_sizes); } BENCHMARK(BM_UValidateMedley); void BM_UIOVecSource(benchmark::State& state) { // Pick file to process based on state.range(0). int file_index = state.range(0); int level = state.range(1); CHECK_GE(file_index, 0); CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); std::string contents = ReadTestDataFile(kTestDataFiles[file_index].filename, kTestDataFiles[file_index].size_limit); // Create `iovec`s of the `contents`. const int kNumEntries = 10; struct iovec iov[kNumEntries]; size_t used_so_far = 0; for (int i = 0; i < kNumEntries; ++i) { iov[i].iov_base = const_cast(contents.data()) + used_so_far; if (used_so_far == contents.size()) { iov[i].iov_len = 0; continue; } if (i == kNumEntries - 1) { iov[i].iov_len = contents.size() - used_so_far; } else { iov[i].iov_len = contents.size() / kNumEntries; } used_so_far += iov[i].iov_len; } char* dst = new char[snappy::MaxCompressedLength(contents.size())]; size_t zsize = 0; for (auto s : state) { snappy::RawCompressFromIOVec(iov, contents.size(), dst, &zsize, snappy::CompressionOptions{/*level=*/level}); benchmark::DoNotOptimize(iov); } state.SetBytesProcessed(static_cast(state.iterations()) * static_cast(contents.size())); const double compression_ratio = static_cast(zsize) / std::max(1, contents.size()); state.SetLabel(StrFormat("%s (%.2f %%)", kTestDataFiles[file_index].label, 100.0 * compression_ratio)); VLOG(0) << StrFormat("compression for %s: %d -> %d bytes", kTestDataFiles[file_index].label, contents.size(), zsize); delete[] dst; } BENCHMARK(BM_UIOVecSource)->Apply(FilesAndLevels); void BM_UIOVecSink(benchmark::State& state) { // Pick file to process based on state.range(0). int file_index = state.range(0); CHECK_GE(file_index, 0); CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); std::string contents = ReadTestDataFile(kTestDataFiles[file_index].filename, kTestDataFiles[file_index].size_limit); std::string zcontents; snappy::Compress(contents.data(), contents.size(), &zcontents); // Uncompress into an iovec containing ten entries. const int kNumEntries = 10; struct iovec iov[kNumEntries]; char* dst = new char[contents.size()]; size_t used_so_far = 0; for (int i = 0; i < kNumEntries; ++i) { iov[i].iov_base = dst + used_so_far; if (used_so_far == contents.size()) { iov[i].iov_len = 0; continue; } if (i == kNumEntries - 1) { iov[i].iov_len = contents.size() - used_so_far; } else { iov[i].iov_len = contents.size() / kNumEntries; } used_so_far += iov[i].iov_len; } for (auto s : state) { CHECK(snappy::RawUncompressToIOVec(zcontents.data(), zcontents.size(), iov, kNumEntries)); benchmark::DoNotOptimize(iov); } state.SetBytesProcessed(static_cast(state.iterations()) * static_cast(contents.size())); state.SetLabel(kTestDataFiles[file_index].label); delete[] dst; } BENCHMARK(BM_UIOVecSink)->DenseRange(0, 4); void BM_UFlatSink(benchmark::State& state) { // Pick file to process based on state.range(0). int file_index = state.range(0); CHECK_GE(file_index, 0); CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); std::string contents = ReadTestDataFile(kTestDataFiles[file_index].filename, kTestDataFiles[file_index].size_limit); std::string zcontents; snappy::Compress( contents.data(), contents.size(), &zcontents, snappy::CompressionOptions{/*level=*/static_cast(state.range(1))}); char* dst = new char[contents.size()]; for (auto s : state) { snappy::ByteArraySource source(zcontents.data(), zcontents.size()); snappy::UncheckedByteArraySink sink(dst); CHECK(snappy::Uncompress(&source, &sink)); benchmark::DoNotOptimize(sink); } state.SetBytesProcessed(static_cast(state.iterations()) * static_cast(contents.size())); state.SetLabel(kTestDataFiles[file_index].label); std::string s(dst, contents.size()); CHECK_EQ(contents, s); delete[] dst; } BENCHMARK(BM_UFlatSink)->Apply(FilesAndLevels); void BM_ZFlat(benchmark::State& state) { // Pick file to process based on state.range(0). int file_index = state.range(0); int level = state.range(1); CHECK_GE(file_index, 0); CHECK_LT(file_index, ARRAYSIZE(kTestDataFiles)); std::string contents = ReadTestDataFile(kTestDataFiles[file_index].filename, kTestDataFiles[file_index].size_limit); char* dst = new char[snappy::MaxCompressedLength(contents.size())]; size_t zsize = 0; for (auto s : state) { snappy::RawCompress(contents.data(), contents.size(), dst, &zsize, snappy::CompressionOptions{/*level=*/level}); benchmark::DoNotOptimize(dst); } state.SetBytesProcessed(static_cast(state.iterations()) * static_cast(contents.size())); const double compression_ratio = static_cast(zsize) / std::max(1, contents.size()); state.SetLabel(StrFormat("%s (%.2f %%)", kTestDataFiles[file_index].label, 100.0 * compression_ratio)); VLOG(0) << StrFormat("compression for %s: %d -> %d bytes", kTestDataFiles[file_index].label, contents.size(), zsize); delete[] dst; } BENCHMARK(BM_ZFlat)->Apply(FilesAndLevels); void BM_ZFlatAll(benchmark::State& state) { const int num_files = ARRAYSIZE(kTestDataFiles); int level = state.range(0); std::vector contents(num_files); std::vector dst(num_files); int64_t total_contents_size = 0; for (int i = 0; i < num_files; ++i) { contents[i] = ReadTestDataFile(kTestDataFiles[i].filename, kTestDataFiles[i].size_limit); dst[i] = new char[snappy::MaxCompressedLength(contents[i].size())]; total_contents_size += contents[i].size(); } size_t zsize = 0; for (auto s : state) { for (int i = 0; i < num_files; ++i) { snappy::RawCompress(contents[i].data(), contents[i].size(), dst[i], &zsize, snappy::CompressionOptions{/*level=*/level}); benchmark::DoNotOptimize(dst); } } state.SetBytesProcessed(static_cast(state.iterations()) * total_contents_size); for (char* dst_item : dst) { delete[] dst_item; } state.SetLabel(StrFormat("%d kTestDataFiles", num_files)); } BENCHMARK(BM_ZFlatAll)->DenseRange(1, 2); void BM_ZFlatIncreasingTableSize(benchmark::State& state) { CHECK_GT(ARRAYSIZE(kTestDataFiles), 0); int level = state.range(0); const std::string base_content = ReadTestDataFile( kTestDataFiles[0].filename, kTestDataFiles[0].size_limit); std::vector contents; std::vector dst; int64_t total_contents_size = 0; for (int table_bits = kMinHashTableBits; table_bits <= kMaxHashTableBits; ++table_bits) { std::string content = base_content; content.resize(1 << table_bits); dst.push_back(new char[snappy::MaxCompressedLength(content.size())]); total_contents_size += content.size(); contents.push_back(std::move(content)); } size_t zsize = 0; for (auto s : state) { for (size_t i = 0; i < contents.size(); ++i) { snappy::RawCompress(contents[i].data(), contents[i].size(), dst[i], &zsize, snappy::CompressionOptions{/*level=*/level}); benchmark::DoNotOptimize(dst); } } state.SetBytesProcessed(static_cast(state.iterations()) * total_contents_size); for (char* dst_item : dst) { delete[] dst_item; } state.SetLabel(StrFormat("%d tables", contents.size())); } BENCHMARK(BM_ZFlatIncreasingTableSize)->DenseRange(1, 2); } // namespace } // namespace snappy