Skip to content
Snippets Groups Projects
benchmark.cpp 13.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include "benchmark.h"
    
    static void write_compression_report(const std::vector<BenchmarkRecord> &results, const std::string &reportFile)
    {
        std::ofstream csvFile = std::ofstream(reportFile, std::ios::out);
        always_assert(csvFile.is_open());
    
        csvFile << std::fixed << std::setprecision(5);
        // CSV header.
        csvFile << "filename;subblock;pixel;width;height;compression;level;originalSize;compressedSize;compressedZ;compressionRatio;compressionRatioZ;compressionTime;compressionTimeZ" << std::endl;
        const char sep = ';';
    
        for (const BenchmarkRecord &record : results)
        {
            csvFile << record.fileName << sep << record.subblockId << sep << record.pixelType << sep << record.width << sep << record.height << sep << record.compressionMethod
                    << sep << record.compressionLevel << sep << record.originalSize << sep << record.compressedSize << sep << record.zOrderCompressedSize
                    << sep << record.compressionRatio << sep << record.zOrderCompressionRatio << sep << record.compressionTime
                    << sep << record.zOrderCompressionTime << std::endl;
        }
    }
    
    static void compression_thread_work(const ByteArray &data, CompressionMethod method, int compressionLevel, CompressionResult &result, const char *info)
    {
        auto compResult = test_compression_method(data, method, compressionLevel);
        result = compResult;
        printf("Completed: %s\n", info);
    }
    
    static std::vector<BenchmarkRecord> benchmark_continuos_compression_one_level(const ByteArray &data, const ByteArray &zOrderedData, int compressionLevel)
    {
        // CompressionMethod_GZIP
        CompressionResult gzipResult = {};
        CompressionResult gzipZResult = {};
    
        // CompressionMethod_LZMA
        CompressionResult lzmaResult = {};
        CompressionResult lzmaZResult = {};
    
        // CompressionMethod_BZIP2
        CompressionResult bzip2Result = {};
        CompressionResult bzip2ZResult = {};
    
        std::vector<std::thread> workers;
        workers.resize(6);
        // We know that lzma is slowest, let's run all three in threads.
        workers[0] = std::thread(compression_thread_work, std::ref(data), CompressionMethod_GZIP, compressionLevel, std::ref(gzipResult), "Gzip normal order");
        workers[1] = std::thread(compression_thread_work, std::ref(zOrderedData), CompressionMethod_GZIP, compressionLevel, std::ref(gzipZResult), "Gzip Z order");
    
        workers[2] = std::thread(compression_thread_work, std::ref(data), CompressionMethod_LZMA, compressionLevel, std::ref(lzmaResult), "LZMA normal order");
        workers[3] = std::thread(compression_thread_work, std::ref(zOrderedData), CompressionMethod_LZMA, compressionLevel, std::ref(lzmaZResult), "LZMA Z order");
    
        workers[4] = std::thread(compression_thread_work, std::ref(data), CompressionMethod_BZIP2, compressionLevel, std::ref(bzip2Result), "Bzip2 normal order");
        workers[5] = std::thread(compression_thread_work, std::ref(zOrderedData), CompressionMethod_BZIP2, compressionLevel, std::ref(bzip2ZResult), "Bzip2 Z order");
    
        for (size_t i = 0; i < workers.size(); i++)
        {
            workers[i].join();
        }
        printf("All threads completed.\n");
    
        BenchmarkRecord gzipRecord(gzipResult, gzipZResult);
        gzipRecord.compressionLevel = compressionLevel;
        gzipRecord.compressionMethod = GZIP_NAME;
    
        BenchmarkRecord lzmaRecord(lzmaResult, lzmaZResult);
        lzmaRecord.compressionLevel = compressionLevel;
        lzmaRecord.compressionMethod = LZMA_NAME;
    
        BenchmarkRecord bzip2Record(bzip2Result, bzip2ZResult);
        bzip2Record.compressionLevel = compressionLevel;
        bzip2Record.compressionMethod = BZIP2_NAME;
    
        std::vector<BenchmarkRecord> results;
        results.resize(3);
        results[0] = gzipRecord;
        results[1] = lzmaRecord;
        results[2] = bzip2Record;
        return results;
    }
    
    void benchmark_continuos_compression(CziFile &cziFile, const std::string &reportFile, bool verbose, int level)
    {
        always_assert(cziFile.subBlockDirectory.entries.size() > 0);
        const int minCompressionLevel = 1;
        const int maxCompressionLevel = 9;
    
        std::string fName = fs_wrapper::get_filename(cziFile.fileName);
        auto entry = cziFile.subBlockDirectory.entries[0];
    
        ByteArray data = cziFile.get_continuous_image_data(false);
        ByteArray zOrderData = cziFile.get_continuous_image_data(true);
    
        std::vector<BenchmarkRecord> results;
        if (level != -1)
        {
            results = benchmark_continuos_compression_one_level(data, zOrderData, level);
            for (auto &&result : results)
            {
                result.fileName = fName.c_str();
                result.subblockId = 999;
                result.pixelType = cziFile.pixel_type_str(entry.pixelType);
                result.width = 0;
                result.height = 0;
            }
    
            write_compression_report(results, reportFile);
        }
        else
        {
    
            int levelDone = 0;
    #pragma omp parallel for
            for (int compressionLevel = minCompressionLevel; compressionLevel <= maxCompressionLevel; compressionLevel++)
            {
                auto levelResults = benchmark_continuos_compression_one_level(data, zOrderData, compressionLevel);
    #pragma omp critical
                {
                    results.insert(results.end(), levelResults.begin(), levelResults.end());
                    printf("\rFinished compression level %i/%i of normal order.", ++levelDone, maxCompressionLevel);
                    fflush(stdout);
                }
            }
            if (verbose)
                printf("\n");
        }
    
        for (auto &&result : results)
        {
            result.fileName = fName.c_str();
            result.subblockId = 999;
            result.pixelType = cziFile.pixel_type_str(entry.pixelType);
            result.width = 0;
            result.height = 0;
        }
    
        if (verbose)
            printf("\nWriting report file...\n");
    
        write_compression_report(results, reportFile);
    
        printf("\nFinished benchmark, results are written in: %s\n", reportFile.c_str());
    }
    
    void benchmark_compression(CziFile &cziFile, const std::string &reportFile, bool verbose, int level)
    {
        std::vector<BenchmarkRecord> benchmarkResults;
        const int minCompressionLevel = 1;
        const int maxCompressionLevel = 9;
        std::string fName = fs_wrapper::get_filename(cziFile.fileName);
    
        int sbCount = (int)cziFile.subBlockDirectory.entries.size();
        for (size_t subblockId = 0; subblockId < cziFile.subBlockDirectory.entries.size(); subblockId++)
        {
    
            DirectoryEntryDV subblock = cziFile.subBlockDirectory.entries[subblockId];
    
            const char *pt = cziFile.pixel_type_str(subblock.pixelType);
    
            ByteArray data = cziFile.get_image_data(subblockId, false);
            ByteArray dataInZOrder = cziFile.get_image_data(subblockId, true);
    
            if (verbose)
            {
                printf("\rProcessing Subblock %i/%i", (int)subblockId + 1, sbCount);
                fflush(stdout);
            }
            int fromCL = (level == -1) ? minCompressionLevel : level;
            int toCL = (level == -1) ? maxCompressionLevel : level;
    #pragma omp parallel for
            for (int compressionLevel = fromCL; compressionLevel <= toCL; compressionLevel++)
            {
    
                // CompressionMethod_GZIP
                {
                    CompressionResult gzipResult = test_compression_method(data, CompressionMethod_GZIP, compressionLevel);
                    CompressionResult gzipZOrderedResult = test_compression_method(dataInZOrder, CompressionMethod_GZIP, compressionLevel);
    
                    BenchmarkRecord gzipRecord = BenchmarkRecord(gzipResult, gzipZOrderedResult);
                    gzipRecord.set_metadata(fName.c_str(), subblockId, pt, subblock.width, subblock.height, "GZIP", compressionLevel);
    
    #pragma omp critical
                    {
                        benchmarkResults.push_back(gzipRecord);
                    }
                }
    
                // CompressionMethod_LZMA
                {
    
                    CompressionResult lzmaResult = test_compression_method(data, CompressionMethod_LZMA, compressionLevel);
                    CompressionResult lzmaZOrderedResult = test_compression_method(dataInZOrder, CompressionMethod_LZMA, compressionLevel);
    
                    BenchmarkRecord lzmaRecord = BenchmarkRecord(lzmaResult, lzmaZOrderedResult);
                    lzmaRecord.set_metadata(fName.c_str(), subblockId, pt, subblock.width, subblock.height, "LZMA2", compressionLevel);
    #pragma omp critical
                    {
                        benchmarkResults.push_back(lzmaRecord);
                    }
                }
    
                // CompressionMethod_BZIP2
                {
                    CompressionResult bzip2Result = test_compression_method(data, CompressionMethod_BZIP2, compressionLevel);
                    CompressionResult bzip2ZOrderedResult = test_compression_method(dataInZOrder, CompressionMethod_BZIP2, compressionLevel);
    
                    BenchmarkRecord bzip2Record = BenchmarkRecord(bzip2Result, bzip2ZOrderedResult);
                    bzip2Record.set_metadata(fName.c_str(), subblockId, pt, subblock.width, subblock.height, "BZIP2", compressionLevel);
    #pragma omp critical
                    {
                        benchmarkResults.push_back(bzip2Record);
                    }
                }
            }
        }
    
        if (verbose)
            printf("\nWriting report file...\n");
        write_compression_report(benchmarkResults, reportFile);
    
        printf("\nFinished benchmark, results are written in: %s\n", reportFile.c_str());
    }
    
    void frame_difference_benchmark(CziFile &cziFile, const std::string &reportFile, bool verbose, int level, CompressionMethod cm)
    {
        // NOTE: This benchmark works only for 16 bit pixels!
        printf("Compression method %s with compression level %i\n", compression_method_str(cm), level);
    
        auto framesByChannels = cziFile.get_subblocks_grouped_by_channels();
        uint mappedIsBetter = 0;
    
        uint iter = 0;
        uint iterCount = cziFile.subBlockDirectory.entryCount;
    
        for (const std::pair<uint, std::vector<uint>> &channelGroup : framesByChannels)
        {
            printf_if(verbose, "Starting channel %u\n", channelGroup.first);
    
    #pragma omp parallel for
            for (size_t i = 1; i < channelGroup.second.size(); i++)
            {
                uint prevFrameId = channelGroup.second[i - 1];
                uint currFrameId = channelGroup.second[i];
    
                DirectoryEntryDV prevEntry = cziFile.subBlockDirectory.entries[prevFrameId];
                DirectoryEntryDV currEntry = cziFile.subBlockDirectory.entries[currFrameId];
    
                DimensionEntryDV1 prevDim = prevEntry.get_dimension(Dimension_Z);
                DimensionEntryDV1 currDim = currEntry.get_dimension(Dimension_Z);
    
                always_assert(!prevDim.isEmpty && !currDim.isEmpty);
    
                printf_if(verbose, "Prev frame Z: %i\n", prevDim.start);
                printf_if(verbose, "Curr frame Z: %i\n", currDim.start);
    
                // This assertion will fail if pixel type isn't Gray16
                always_assert(prevEntry.pixelType == PixelType_Gray16);
                always_assert(currEntry.pixelType == PixelType_Gray16);
    
                auto prevFrameData = cziFile.get_image_data(prevFrameId, false);
                auto currentFrameData = cziFile.get_image_data(currFrameId, false);
                always_assert(prevFrameData.size() == currentFrameData.size());
    
                std::vector<ushort> prevFrameValues = bytes_to_ushort_array(prevFrameData);
                std::vector<ushort> currentFrameValues = bytes_to_ushort_array(currentFrameData);
                always_assert(prevFrameValues.size() == currentFrameValues.size());
    
                std::vector<int> diffArray = vecUtil::diff_vectors<ushort, int>(prevFrameValues, currentFrameValues);
    
                auto minMax = vecUtil::find_min_max(diffArray);
                long maxMappedValue = (minMax.first < 0) ? (abs(minMax.first) + minMax.second) : (minMax.second);
                bool canBeMappedToUShort = maxMappedValue < USHORT_MAX;
    
                ByteArray diffArrayBytes = int_array_to_bytes(diffArray);
    
                CompressionResult frameCompression = test_compression_method(currentFrameData, cm, level);
                CompressionResult diffCompression = test_compression_method(diffArrayBytes, cm, level);
                CompressionResult mappedCompression = {};
    
                printf_if(verbose, "========================\nFrame [%u vs %u]\n", prevFrameId, currFrameId);
                printf_if(verbose, "               %10s  %10s %10s\n", "SizeRtio", "CmpRtio", "Size");
                printf_if(verbose, "    raw frame: %10.5f  %10.5f %10lu\n", frameCompression.ratioToOriginalSize, frameCompression.compressionRatio, frameCompression.compressedSize);
                printf_if(verbose, "     int diff: %10.5f  %10.5f %10lu\n", diffCompression.ratioToOriginalSize, diffCompression.compressionRatio, diffCompression.compressedSize);
    
                if (canBeMappedToUShort)
                {
                    TypeMapper<int, ushort> typeMapper;
                    std::vector<ushort> mappedDiffArray = typeMapper.map(diffArray);
                    ByteArray mappedArrayBytes = ushort_array_to_bytes(mappedDiffArray);
                    mappedCompression = test_compression_method(mappedArrayBytes, cm, 6);
    
                    printf_if(verbose, "  ushort diff: %10.5f  %10.5f %10lu\n", mappedCompression.ratioToOriginalSize, mappedCompression.compressionRatio, mappedCompression.compressedSize);
                }
    
                if (diffCompression.compressedSize < frameCompression.compressedSize || mappedCompression.compressedSize < frameCompression.compressedSize)
                {
                    printf_if(verbose, GREEN "Difference is better than frame by %lu B\n" RESET, frameCompression.compressedSize - mappedCompression.compressedSize);
    #pragma omp critical
                    {
                        ++mappedIsBetter;
                    }
                }
                else
                {
                    printf_if(verbose, RED "Difference is worse than frame by %lu B\n" RESET, mappedCompression.compressedSize - frameCompression.compressedSize);
                }
    
                if (!verbose)
                {
    #pragma omp critical
                    {
                        printf("\rFinished %u/%u", ++iter, iterCount);
                        fflush(stdout);
                    }
                }
            }
        }
        printf("\rFinished %u/%u\n", iterCount, iterCount);
        printf("%u/%u frames are better compressed by difference\n", mappedIsBetter, cziFile.subBlockDirectory.entryCount);
    }