1 //
2 // Copyright (C) 2023 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 #include <memory>
17 
18 #include <array>
19 #include <iostream>
20 #include <random>
21 
22 #include <libsnapshot/cow_compress.h>
23 #include <libsnapshot/cow_format.h>
24 
25 static const uint32_t BLOCK_SZ = 4096;
26 static const uint32_t SEED_NUMBER = 10;
27 
28 namespace android {
29 namespace snapshot {
30 
CompressionToString(CowCompression & compression)31 static std::string CompressionToString(CowCompression& compression) {
32     std::string output;
33     switch (compression.algorithm) {
34         case kCowCompressBrotli:
35             output.append("brotli");
36             break;
37         case kCowCompressGz:
38             output.append("gz");
39             break;
40         case kCowCompressLz4:
41             output.append("lz4");
42             break;
43         case kCowCompressZstd:
44             output.append("zstd");
45             break;
46         case kCowCompressNone:
47             return "No Compression";
48     }
49     output.append(" " + std::to_string(compression.compression_level));
50     return output;
51 }
52 
OneShotCompressionTest()53 void OneShotCompressionTest() {
54     std::cout << "\n-------One Shot Compressor Perf Analysis-------\n";
55 
56     std::vector<CowCompression> compression_list = {
57             {kCowCompressLz4, 0},     {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},
58             {kCowCompressBrotli, 11}, {kCowCompressZstd, 3},   {kCowCompressZstd, 6},
59             {kCowCompressZstd, 9},    {kCowCompressZstd, 22},  {kCowCompressGz, 1},
60             {kCowCompressGz, 3},      {kCowCompressGz, 6},     {kCowCompressGz, 9}};
61     std::vector<std::unique_ptr<ICompressor>> compressors;
62     for (auto i : compression_list) {
63         compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
64     }
65 
66     // Allocate a buffer of size 8 blocks.
67     std::array<char, 32768> buffer;
68 
69     // Generate a random 4k buffer of characters
70     std::default_random_engine gen(SEED_NUMBER);
71     std::uniform_int_distribution<int> distribution(0, 10);
72     for (int i = 0; i < buffer.size(); i++) {
73         buffer[i] = static_cast<char>(distribution(gen));
74     }
75 
76     std::vector<std::pair<double, std::string>> latencies;
77     std::vector<std::pair<double, std::string>> ratios;
78 
79     for (size_t i = 0; i < compressors.size(); i++) {
80         const auto start = std::chrono::steady_clock::now();
81         std::vector<uint8_t> compressed_data =
82                 compressors[i]->Compress(buffer.data(), buffer.size());
83         const auto end = std::chrono::steady_clock::now();
84         const auto latency =
85                 std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
86         const double compression_ratio =
87                 static_cast<uint16_t>(compressed_data.size()) * 1.00 / buffer.size();
88 
89         std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> "
90                   << latency.count() << "ms "
91                   << " compression ratio ->" << compression_ratio << " \n";
92 
93         latencies.emplace_back(
94                 std::make_pair(latency.count(), CompressionToString(compression_list[i])));
95         ratios.emplace_back(
96                 std::make_pair(compression_ratio, CompressionToString(compression_list[i])));
97     }
98 
99     int best_speed = 0;
100     int best_ratio = 0;
101 
102     for (size_t i = 1; i < latencies.size(); i++) {
103         if (latencies[i].first < latencies[best_speed].first) {
104             best_speed = i;
105         }
106         if (ratios[i].first < ratios[best_ratio].first) {
107             best_ratio = i;
108         }
109     }
110 
111     std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms "
112               << latencies[best_speed].second << "\n";
113     std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second
114               << "\n";
115 }
116 
IncrementalCompressionTest()117 void IncrementalCompressionTest() {
118     std::cout << "\n-------Incremental Compressor Perf Analysis-------\n";
119 
120     std::vector<CowCompression> compression_list = {
121             {kCowCompressLz4, 0},     {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},
122             {kCowCompressBrotli, 11}, {kCowCompressZstd, 3},   {kCowCompressZstd, 6},
123             {kCowCompressZstd, 9},    {kCowCompressZstd, 22},  {kCowCompressGz, 1},
124             {kCowCompressGz, 3},      {kCowCompressGz, 6},     {kCowCompressGz, 9}};
125     std::vector<std::unique_ptr<ICompressor>> compressors;
126     for (auto i : compression_list) {
127         compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
128     }
129 
130     // Allocate a buffer of size 8 blocks.
131     std::array<char, 32768> buffer;
132 
133     // Generate a random 4k buffer of characters
134     std::default_random_engine gen(SEED_NUMBER);
135     std::uniform_int_distribution<int> distribution(0, 10);
136     for (int i = 0; i < buffer.size(); i++) {
137         buffer[i] = static_cast<char>(distribution(gen));
138     }
139 
140     std::vector<std::pair<double, std::string>> latencies;
141     std::vector<std::pair<double, std::string>> ratios;
142 
143     for (size_t i = 0; i < compressors.size(); i++) {
144         std::vector<std::vector<uint8_t>> compressed_data_vec;
145         int num_blocks = buffer.size() / BLOCK_SZ;
146         const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer.data());
147 
148         const auto start = std::chrono::steady_clock::now();
149         while (num_blocks > 0) {
150             std::vector<uint8_t> compressed_data = compressors[i]->Compress(iter, BLOCK_SZ);
151             compressed_data_vec.emplace_back(compressed_data);
152             num_blocks--;
153             iter += BLOCK_SZ;
154         }
155 
156         const auto end = std::chrono::steady_clock::now();
157         const auto latency =
158                 std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
159 
160         size_t size = 0;
161         for (auto& i : compressed_data_vec) {
162             size += i.size();
163         }
164         const double compression_ratio = size * 1.00 / buffer.size();
165 
166         std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> "
167                   << latency.count() << "ms "
168                   << " compression ratio ->" << compression_ratio << " \n";
169 
170         latencies.emplace_back(
171                 std::make_pair(latency.count(), CompressionToString(compression_list[i])));
172         ratios.emplace_back(
173                 std::make_pair(compression_ratio, CompressionToString(compression_list[i])));
174     }
175 
176     int best_speed = 0;
177     int best_ratio = 0;
178 
179     for (size_t i = 1; i < latencies.size(); i++) {
180         if (latencies[i].first < latencies[best_speed].first) {
181             best_speed = i;
182         }
183         if (ratios[i].first < ratios[best_ratio].first) {
184             best_ratio = i;
185         }
186     }
187 
188     std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms "
189               << latencies[best_speed].second << "\n";
190     std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second
191               << "\n";
192 }
193 
194 }  // namespace snapshot
195 }  // namespace android
196 
main()197 int main() {
198     android::snapshot::OneShotCompressionTest();
199     android::snapshot::IncrementalCompressionTest();
200 
201     return 0;
202 }