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 }