From eb70926ad6c344a61ecbad5bc326dfaac6fe184b Mon Sep 17 00:00:00 2001 From: Daniel Zheng Date: Tue, 22 Aug 2023 16:49:25 -0700 Subject: [PATCH] Add unit test for cow compressor performance Adding test to measure performance differences between cow compression algorithms + levels. This gives us an easy way to test performance between the tunables without having to run an OTA every time. Ultimately we want this to be separate from cow_api_test so it would be nice to have this be it's own binary. Can add some tests for decompression + compressing from a part of an actual img file too. Test: m cow_benchmark Change-Id: Iba92ae3c0b2ad4ff6f842556b701b223d7d37823 --- .../include/libsnapshot/cow_compress.h | 1 - fs_mgr/libsnapshot/tools/Android.bp | 22 ++ fs_mgr/libsnapshot/tools/cow_benchmark.cpp | 188 ++++++++++++++++++ 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 fs_mgr/libsnapshot/tools/Android.bp create mode 100644 fs_mgr/libsnapshot/tools/cow_benchmark.cpp diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h index 97974c414..cf6561559 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h @@ -17,7 +17,6 @@ #pragma once #include -#include #include "libsnapshot/cow_format.h" namespace android { diff --git a/fs_mgr/libsnapshot/tools/Android.bp b/fs_mgr/libsnapshot/tools/Android.bp new file mode 100644 index 000000000..cfa0cefe7 --- /dev/null +++ b/fs_mgr/libsnapshot/tools/Android.bp @@ -0,0 +1,22 @@ + +cc_binary { + name: "cow_benchmark", + host_supported: true, + defaults: [ + "fs_mgr_defaults", + "libsnapshot_cow_defaults", + ], + + srcs: ["cow_benchmark.cpp"], + + static_libs: [ + "libsnapshot_cow", + ], + + shared_libs: [ + "libbase", + "liblog", + ], + + cflags: ["-Werror"], +} diff --git a/fs_mgr/libsnapshot/tools/cow_benchmark.cpp b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp new file mode 100644 index 000000000..da2b87922 --- /dev/null +++ b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp @@ -0,0 +1,188 @@ + +#include + +#include +#include +#include + +#include +#include + +static const uint32_t BLOCK_SZ = 4096; +static const uint32_t SEED_NUMBER = 10; + +namespace android { +namespace snapshot { + +static std::string CompressionToString(CowCompression& compression) { + std::string output; + switch (compression.algorithm) { + case kCowCompressBrotli: + output.append("brotli"); + break; + case kCowCompressGz: + output.append("gz"); + break; + case kCowCompressLz4: + output.append("lz4"); + break; + case kCowCompressZstd: + output.append("zstd"); + break; + case kCowCompressNone: + return "No Compression"; + } + output.append(" " + std::to_string(compression.compression_level)); + return output; +} + +void OneShotCompressionTest() { + std::cout << "\n-------One Shot Compressor Perf Analysis-------\n"; + + std::vector compression_list = { + {kCowCompressLz4, 0}, {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3}, + {kCowCompressBrotli, 11}, {kCowCompressZstd, 3}, {kCowCompressZstd, 6}, + {kCowCompressZstd, 9}, {kCowCompressZstd, 22}, {kCowCompressGz, 1}, + {kCowCompressGz, 3}, {kCowCompressGz, 6}, {kCowCompressGz, 9}}; + std::vector> compressors; + for (auto i : compression_list) { + compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ)); + } + + // Allocate a buffer of size 8 blocks. + std::array buffer; + + // Generate a random 4k buffer of characters + std::default_random_engine gen(SEED_NUMBER); + std::uniform_int_distribution distribution(0, 10); + for (int i = 0; i < buffer.size(); i++) { + buffer[i] = static_cast(distribution(gen)); + } + + std::vector> latencies; + std::vector> ratios; + + for (size_t i = 0; i < compressors.size(); i++) { + const auto start = std::chrono::steady_clock::now(); + std::basic_string compressed_data = + compressors[i]->Compress(buffer.data(), buffer.size()); + const auto end = std::chrono::steady_clock::now(); + const auto latency = + std::chrono::duration_cast(end - start) / 1000.0; + const double compression_ratio = + static_cast(compressed_data.size()) * 1.00 / buffer.size(); + + std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> " + << latency.count() << "ms " + << " compression ratio ->" << compression_ratio << " \n"; + + latencies.emplace_back( + std::make_pair(latency.count(), CompressionToString(compression_list[i]))); + ratios.emplace_back( + std::make_pair(compression_ratio, CompressionToString(compression_list[i]))); + } + + int best_speed = 0; + int best_ratio = 0; + + for (size_t i = 1; i < latencies.size(); i++) { + if (latencies[i].first < latencies[best_speed].first) { + best_speed = i; + } + if (ratios[i].first < ratios[best_ratio].first) { + best_ratio = i; + } + } + + std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms " + << latencies[best_speed].second << "\n"; + std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second + << "\n"; +} + +void IncrementalCompressionTest() { + std::cout << "\n-------Incremental Compressor Perf Analysis-------\n"; + + std::vector compression_list = { + {kCowCompressLz4, 0}, {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3}, + {kCowCompressBrotli, 11}, {kCowCompressZstd, 3}, {kCowCompressZstd, 6}, + {kCowCompressZstd, 9}, {kCowCompressZstd, 22}, {kCowCompressGz, 1}, + {kCowCompressGz, 3}, {kCowCompressGz, 6}, {kCowCompressGz, 9}}; + std::vector> compressors; + for (auto i : compression_list) { + compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ)); + } + + // Allocate a buffer of size 8 blocks. + std::array buffer; + + // Generate a random 4k buffer of characters + std::default_random_engine gen(SEED_NUMBER); + std::uniform_int_distribution distribution(0, 10); + for (int i = 0; i < buffer.size(); i++) { + buffer[i] = static_cast(distribution(gen)); + } + + std::vector> latencies; + std::vector> ratios; + + for (size_t i = 0; i < compressors.size(); i++) { + std::vector> compressed_data_vec; + int num_blocks = buffer.size() / BLOCK_SZ; + const uint8_t* iter = reinterpret_cast(buffer.data()); + + const auto start = std::chrono::steady_clock::now(); + while (num_blocks > 0) { + std::basic_string compressed_data = compressors[i]->Compress(iter, BLOCK_SZ); + compressed_data_vec.emplace_back(compressed_data); + num_blocks--; + iter += BLOCK_SZ; + } + + const auto end = std::chrono::steady_clock::now(); + const auto latency = + std::chrono::duration_cast(end - start) / 1000.0; + + size_t size = 0; + for (auto& i : compressed_data_vec) { + size += i.size(); + } + const double compression_ratio = size * 1.00 / buffer.size(); + + std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> " + << latency.count() << "ms " + << " compression ratio ->" << compression_ratio << " \n"; + + latencies.emplace_back( + std::make_pair(latency.count(), CompressionToString(compression_list[i]))); + ratios.emplace_back( + std::make_pair(compression_ratio, CompressionToString(compression_list[i]))); + } + + int best_speed = 0; + int best_ratio = 0; + + for (size_t i = 1; i < latencies.size(); i++) { + if (latencies[i].first < latencies[best_speed].first) { + best_speed = i; + } + if (ratios[i].first < ratios[best_ratio].first) { + best_ratio = i; + } + } + + std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms " + << latencies[best_speed].second << "\n"; + std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second + << "\n"; +} + +} // namespace snapshot +} // namespace android + +int main() { + android::snapshot::OneShotCompressionTest(); + android::snapshot::IncrementalCompressionTest(); + + return 0; +} \ No newline at end of file