Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb70926a authored by Daniel Zheng's avatar Daniel Zheng
Browse files

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
parent a2bac909
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
#pragma once

#include <memory>
#include <string_view>
#include "libsnapshot/cow_format.h"

namespace android {
+22 −0
Original line number Diff line number Diff line

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"],
}
+188 −0
Original line number Diff line number Diff line

#include <memory>

#include <array>
#include <iostream>
#include <random>

#include <libsnapshot/cow_compress.h>
#include <libsnapshot/cow_format.h>

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<CowCompression> 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<std::unique_ptr<ICompressor>> compressors;
    for (auto i : compression_list) {
        compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
    }

    // Allocate a buffer of size 8 blocks.
    std::array<char, 32768> buffer;

    // Generate a random 4k buffer of characters
    std::default_random_engine gen(SEED_NUMBER);
    std::uniform_int_distribution<int> distribution(0, 10);
    for (int i = 0; i < buffer.size(); i++) {
        buffer[i] = static_cast<char>(distribution(gen));
    }

    std::vector<std::pair<double, std::string>> latencies;
    std::vector<std::pair<double, std::string>> ratios;

    for (size_t i = 0; i < compressors.size(); i++) {
        const auto start = std::chrono::steady_clock::now();
        std::basic_string<uint8_t> compressed_data =
                compressors[i]->Compress(buffer.data(), buffer.size());
        const auto end = std::chrono::steady_clock::now();
        const auto latency =
                std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
        const double compression_ratio =
                static_cast<uint16_t>(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<CowCompression> 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<std::unique_ptr<ICompressor>> compressors;
    for (auto i : compression_list) {
        compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
    }

    // Allocate a buffer of size 8 blocks.
    std::array<char, 32768> buffer;

    // Generate a random 4k buffer of characters
    std::default_random_engine gen(SEED_NUMBER);
    std::uniform_int_distribution<int> distribution(0, 10);
    for (int i = 0; i < buffer.size(); i++) {
        buffer[i] = static_cast<char>(distribution(gen));
    }

    std::vector<std::pair<double, std::string>> latencies;
    std::vector<std::pair<double, std::string>> ratios;

    for (size_t i = 0; i < compressors.size(); i++) {
        std::vector<std::basic_string<uint8_t>> compressed_data_vec;
        int num_blocks = buffer.size() / BLOCK_SZ;
        const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer.data());

        const auto start = std::chrono::steady_clock::now();
        while (num_blocks > 0) {
            std::basic_string<uint8_t> 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<std::chrono::nanoseconds>(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