DirectStorage

Форк
0
/
GpuDecompressionBenchmark.cpp 
687 строк · 20.9 Кб
1
//
2
// Copyright (c) Microsoft. All rights reserved.
3
// This code is licensed under the MIT License (MIT).
4
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
5
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
6
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
7
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
8
//
9

10
#define NOMINMAX
11

12
#include "CustomDecompression.h"
13

14
#include <dstorage.h>
15
#include <dxgi1_4.h>
16
#include <winrt/base.h>
17
#include <winrt/windows.applicationmodel.datatransfer.h>
18

19
#include <chrono>
20
#include <filesystem>
21
#include <fstream>
22
#include <iostream>
23
#include <sstream>
24

25
using winrt::check_hresult;
26
using winrt::com_ptr;
27

28
void SetClipboardText(std::wstring const& str);
29

30
struct handle_closer
31
{
32
    void operator()(HANDLE h) noexcept
33
    {
34
        assert(h != INVALID_HANDLE_VALUE);
35
        if (h)
36
        {
37
            CloseHandle(h);
38
        }
39
    }
40
};
41
using ScopedHandle = std::unique_ptr<void, handle_closer>;
42

43
void ShowHelpText()
44
{
45
    std::cout << "Compresses a file, saves it to disk, and then loads & decompresses using DirectStorage." << std::endl
46
              << std::endl;
47
    std::cout << "USAGE: GpuDecompressionBenchmark <path> [chunk size in MiB]" << std::endl << std::endl;
48
    std::cout << "       Default chunk size is 16." << std::endl;
49
}
50

51
struct ChunkMetadata
52
{
53
    uint32_t Offset;
54
    uint32_t CompressedSize;
55
    uint32_t UncompressedSize;
56
};
57

58
struct Metadata
59
{
60
    uint32_t UncompressedSize;
61
    uint32_t CompressedSize;
62
    uint32_t LargestCompressedChunkSize;
63
    std::vector<ChunkMetadata> Chunks;
64
};
65

66
Metadata GenerateUncompressedMetadata(wchar_t const* filename, uint32_t chunkSizeBytes)
67
{
68
    ScopedHandle inHandle(
69
        CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
70
    winrt::check_bool(inHandle.get());
71

72
    DWORD size = GetFileSize(inHandle.get(), nullptr);
73

74
    Metadata metadata;
75
    metadata.UncompressedSize = size;
76
    metadata.CompressedSize = size;
77
    metadata.LargestCompressedChunkSize = chunkSizeBytes;
78

79
    uint32_t offset = 0;
80

81
    while (offset < size)
82
    {
83
        uint32_t chunkSize = std::min<uint32_t>(size - offset, chunkSizeBytes);
84

85
        metadata.Chunks.push_back({offset, chunkSize, chunkSize});
86
        offset += chunkSize;
87
    }
88

89
    return metadata;
90
}
91

92
com_ptr<IDStorageCompressionCodec> GetCodec(DSTORAGE_COMPRESSION_FORMAT format)
93
{
94
    com_ptr<IDStorageCompressionCodec> codec;
95
    switch (format)
96
    {
97
    case DSTORAGE_COMPRESSION_FORMAT_GDEFLATE:
98
        check_hresult(DStorageCreateCompressionCodec(format, 0, IID_PPV_ARGS(codec.put())));
99
        break;
100

101
#if USE_ZLIB
102
    case DSTORAGE_CUSTOM_COMPRESSION_0:
103
        codec = winrt::make<ZLibCodec>();
104
        break;
105
#endif
106

107
    default:
108
        std::terminate();
109
    }
110

111
    return codec;
112
}
113

114
Metadata Compress(
115
    DSTORAGE_COMPRESSION_FORMAT format,
116
    const wchar_t* originalFilename,
117
    const wchar_t* compressedFilename,
118
    uint32_t chunkSizeBytes)
119
{
120
    ScopedHandle inHandle(CreateFile(
121
        originalFilename,
122
        GENERIC_READ,
123
        FILE_SHARE_READ,
124
        nullptr,
125
        OPEN_EXISTING,
126
        FILE_ATTRIBUTE_NORMAL,
127
        nullptr));
128
    winrt::check_bool(inHandle.get());
129

130
    DWORD size = GetFileSize(inHandle.get(), nullptr);
131

132
    ScopedHandle inMapping(CreateFileMapping(inHandle.get(), nullptr, PAGE_READONLY, 0, 0, nullptr));
133
    winrt::check_bool(inMapping.get());
134

135
    uint8_t* srcData = reinterpret_cast<uint8_t*>(MapViewOfFile(inMapping.get(), FILE_MAP_READ, 0, 0, size));
136
    winrt::check_bool(srcData);
137

138
    ScopedHandle outHandle(CreateFile(
139
        compressedFilename,
140
        GENERIC_WRITE,
141
        FILE_SHARE_WRITE,
142
        nullptr,
143
        CREATE_ALWAYS,
144
        FILE_ATTRIBUTE_NORMAL,
145
        nullptr));
146
    winrt::check_bool(outHandle.get());
147

148
    uint32_t numChunks = (size + chunkSizeBytes - 1) / chunkSizeBytes;
149

150
    std::wcout << "Compressing " << originalFilename << " to " << compressedFilename << " in " << numChunks << "x"
151
               << chunkSizeBytes / 1024 / 1024 << " MiB chunks" << std::endl;
152

153
    using Chunk = std::vector<uint8_t>;
154

155
    std::vector<Chunk> chunks;
156
    std::vector<uint32_t> chunkOffsets;
157

158
    chunks.resize(numChunks);
159
    for (uint32_t i = 0; i < numChunks; ++i)
160
    {
161
        uint32_t thisChunkOffset = i * chunkSizeBytes;
162
        chunkOffsets.push_back(thisChunkOffset);
163
    }
164

165
    std::atomic<size_t> nextChunk = 0;
166

167
    std::vector<std::thread> threads;
168
    threads.reserve(std::thread::hardware_concurrency());
169

170
    for (unsigned int i = 0; i < std::thread::hardware_concurrency(); ++i)
171
    {
172
        threads.emplace_back(
173
            [&]()
174
            {
175
                // Each thread needs its own instance of the codec
176
                com_ptr<IDStorageCompressionCodec> codec = GetCodec(format);
177

178
                while (true)
179
                {
180
                    size_t chunkIndex = nextChunk.fetch_add(1);
181
                    if (chunkIndex >= numChunks)
182
                        return;
183

184
                    size_t thisChunkOffset = chunkIndex * chunkSizeBytes;
185
                    size_t thisChunkSize = std::min<size_t>(size - thisChunkOffset, chunkSizeBytes);
186

187
                    Chunk chunk(codec->CompressBufferBound(thisChunkSize));
188

189
                    uint8_t* uncompressedStart = srcData + thisChunkOffset;
190

191
                    size_t compressedSize = 0;
192
                    check_hresult(codec->CompressBuffer(
193
                        uncompressedStart,
194
                        thisChunkSize,
195
                        DSTORAGE_COMPRESSION_BEST_RATIO,
196
                        chunk.data(),
197
                        chunk.size(),
198
                        &compressedSize));
199
                    chunk.resize(compressedSize);
200

201
                    chunks[chunkIndex] = std::move(chunk);
202
                }
203
            });
204
    }
205

206
    size_t lastNextChunk = std::numeric_limits<size_t>::max();
207

208
    do
209
    {
210
        Sleep(250);
211
        if (nextChunk != lastNextChunk)
212
        {
213
            lastNextChunk = nextChunk;
214
            std::cout << "   " << std::min<size_t>(numChunks, lastNextChunk + 1) << " / " << numChunks << "   \r";
215
            std::cout.flush();
216
        }
217
    } while (lastNextChunk < numChunks);
218

219
    for (auto& thread : threads)
220
    {
221
        thread.join();
222
    }
223

224
    uint32_t totalCompressedSize = 0;
225
    uint32_t offset = 0;
226

227
    Metadata metadata;
228
    metadata.UncompressedSize = size;
229
    metadata.LargestCompressedChunkSize = 0;
230

231
    for (uint32_t i = 0; i < numChunks; ++i)
232
    {
233
        winrt::check_bool(
234
            WriteFile(outHandle.get(), chunks[i].data(), static_cast<DWORD>(chunks[i].size()), nullptr, nullptr));
235

236
        uint32_t thisChunkOffset = i * chunkSizeBytes;
237
        uint32_t thisChunkSize = std::min<uint32_t>(size - thisChunkOffset, chunkSizeBytes);
238

239
        ChunkMetadata chunkMetadata{};
240
        chunkMetadata.Offset = offset;
241
        chunkMetadata.CompressedSize = static_cast<uint32_t>(chunks[i].size());
242
        chunkMetadata.UncompressedSize = thisChunkSize;
243
        metadata.Chunks.push_back(chunkMetadata);
244

245
        totalCompressedSize += chunkMetadata.CompressedSize;
246
        offset += chunkMetadata.CompressedSize;
247

248
        metadata.LargestCompressedChunkSize =
249
            std::max(metadata.LargestCompressedChunkSize, chunkMetadata.CompressedSize);
250
    }
251

252
    outHandle.reset();
253

254
    metadata.CompressedSize = totalCompressedSize;
255

256
    std::cout << "Total: " << size << " --> " << totalCompressedSize << " bytes (" << totalCompressedSize * 100.0 / size
257
              << "%)     " << std::endl;
258

259
    return metadata;
260
}
261

262
static uint64_t GetProcessCycleTime()
263
{
264
    ULONG64 cycleTime;
265

266
    winrt::check_bool(QueryProcessCycleTime(GetCurrentProcess(), &cycleTime));
267

268
    return cycleTime;
269
}
270

271
struct TestResult
272
{
273
    double Bandwidth;
274
    uint64_t ProcessCycles;
275
};
276

277
TestResult RunTest(
278
    IDStorageFactory* factory,
279
    uint32_t stagingSizeMiB,
280
    wchar_t const* sourceFilename,
281
    DSTORAGE_COMPRESSION_FORMAT compressionFormat,
282
    Metadata const& metadata,
283
    int numRuns)
284
{
285
    com_ptr<IDStorageFile> file;
286

287
    HRESULT hr = factory->OpenFile(sourceFilename, IID_PPV_ARGS(file.put()));
288
    if (FAILED(hr))
289
    {
290
        std::wcout << L"The file '" << sourceFilename << L"' could not be opened. HRESULT=0x" << std::hex << hr
291
                   << std::endl;
292
        std::abort();
293
    }
294

295
    // The staging buffer size must be set before any queues are created.
296
    std::cout << "  " << stagingSizeMiB << " MiB staging buffer: ";
297
    uint32_t stagingBufferSizeBytes = stagingSizeMiB * 1024 * 1024;
298
    check_hresult(factory->SetStagingBufferSize(stagingBufferSizeBytes));
299

300
    if (metadata.LargestCompressedChunkSize > stagingBufferSizeBytes)
301
    {
302
        std::cout << " SKIPPED! " << std::endl;
303
        return {0, 0};
304
    }
305

306
    com_ptr<ID3D12Device> device;
307
    check_hresult(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS(&device)));
308

309
    // Create a DirectStorage queue which will be used to load data into a
310
    // buffer on the GPU.
311
    DSTORAGE_QUEUE_DESC queueDesc{};
312
    queueDesc.Capacity = DSTORAGE_MAX_QUEUE_CAPACITY;
313
    queueDesc.Priority = DSTORAGE_PRIORITY_NORMAL;
314
    queueDesc.SourceType = DSTORAGE_REQUEST_SOURCE_FILE;
315
    queueDesc.Device = device.get();
316

317
    com_ptr<IDStorageQueue> queue;
318
    check_hresult(factory->CreateQueue(&queueDesc, IID_PPV_ARGS(queue.put())));
319

320
    // Create the ID3D12Resource buffer which will be populated with the file's contents
321
    D3D12_HEAP_PROPERTIES bufferHeapProps = {};
322
    bufferHeapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
323

324
    D3D12_RESOURCE_DESC bufferDesc = {};
325
    bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
326
    bufferDesc.Width = metadata.UncompressedSize;
327
    bufferDesc.Height = 1;
328
    bufferDesc.DepthOrArraySize = 1;
329
    bufferDesc.MipLevels = 1;
330
    bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
331
    bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
332
    bufferDesc.SampleDesc.Count = 1;
333

334
    com_ptr<ID3D12Resource> bufferResource;
335
    check_hresult(device->CreateCommittedResource(
336
        &bufferHeapProps,
337
        D3D12_HEAP_FLAG_NONE,
338
        &bufferDesc,
339
        D3D12_RESOURCE_STATE_COMMON,
340
        nullptr,
341
        IID_PPV_ARGS(bufferResource.put())));
342

343
    // Configure a fence to be signaled when the request is completed
344
    com_ptr<ID3D12Fence> fence;
345
    check_hresult(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(fence.put())));
346

347
    ScopedHandle fenceEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr));
348
    uint64_t fenceValue = 1;
349

350
    double meanBandwidth = 0;
351
    uint64_t meanCycleTime = 0;
352

353
    for (int i = 0; i < numRuns; ++i)
354
    {
355
        check_hresult(fence->SetEventOnCompletion(fenceValue, fenceEvent.get()));
356

357
        // Enqueue requests to load each compressed chunk.
358
        uint32_t destOffset = 0;
359
        for (auto const& chunk : metadata.Chunks)
360
        {
361
            DSTORAGE_REQUEST request = {};
362
            request.Options.SourceType = DSTORAGE_REQUEST_SOURCE_FILE;
363
            request.Options.DestinationType = DSTORAGE_REQUEST_DESTINATION_BUFFER;
364
            request.Options.CompressionFormat = compressionFormat;
365
            request.Source.File.Source = file.get();
366
            request.Source.File.Offset = chunk.Offset;
367
            request.Source.File.Size = chunk.CompressedSize;
368
            request.UncompressedSize = chunk.UncompressedSize;
369
            request.Destination.Buffer.Resource = bufferResource.get();
370
            request.Destination.Buffer.Offset = destOffset;
371
            request.Destination.Buffer.Size = chunk.UncompressedSize;
372
            queue->EnqueueRequest(&request);
373
            destOffset += request.UncompressedSize;
374
        }
375

376
        // Signal the fence when done
377
        queue->EnqueueSignal(fence.get(), fenceValue);
378

379
        auto startTime = std::chrono::high_resolution_clock::now();
380
        auto startCycleTime = GetProcessCycleTime();
381

382
        // Tell DirectStorage to start executing all queued items.
383
        queue->Submit();
384

385
        // Wait for the submitted work to complete
386
        WaitForSingleObject(fenceEvent.get(), INFINITE);
387

388
        auto endCycleTime = GetProcessCycleTime();
389
        auto endTime = std::chrono::high_resolution_clock::now();
390

391
        if (fence->GetCompletedValue() == (uint64_t)-1)
392
        {
393
            // Device removed!  Give DirectStorage a chance to detect the error.
394
            Sleep(5);
395
        }
396

397
        // If an error was detected the first failure record
398
        // can be retrieved to get more details.
399
        DSTORAGE_ERROR_RECORD errorRecord{};
400
        queue->RetrieveErrorRecord(&errorRecord);
401
        if (FAILED(errorRecord.FirstFailure.HResult))
402
        {
403
            //
404
            // errorRecord.FailureCount - The number of failed requests in the queue since the last
405
            //                            RetrieveErrorRecord call.
406
            // errorRecord.FirstFailure - Detailed record about the first failed command in the enqueue order.
407
            //
408
            std::cout << "The DirectStorage request failed! HRESULT=0x" << std::hex << errorRecord.FirstFailure.HResult
409
                      << std::endl;
410

411
            if (errorRecord.FirstFailure.CommandType == DSTORAGE_COMMAND_TYPE_REQUEST)
412
            {
413
                auto& r = errorRecord.FirstFailure.Request.Request;
414

415
                std::cout << std::dec << "   " << r.Source.File.Offset << "   " << r.Source.File.Size << std::endl;
416
            }
417
            std::terminate();
418
        }
419
        else
420
        {
421
            auto duration = endTime - startTime;
422

423
            using dseconds = std::chrono::duration<double>;
424

425
            double durationInSeconds = std::chrono::duration_cast<dseconds>(duration).count();
426
            double bandwidth = (metadata.UncompressedSize / durationInSeconds) / 1000.0 / 1000.0 / 1000.0;
427
            meanBandwidth += bandwidth;
428

429
            meanCycleTime += (endCycleTime - startCycleTime);
430

431
            std::cout << ".";
432
        }
433
        ++fenceValue;
434
    }
435

436
    meanBandwidth /= numRuns;
437
    meanCycleTime /= numRuns;
438

439
    std::cout << "  " << meanBandwidth << " GB/s"
440
              << " mean cycle time: " << std::dec << meanCycleTime << std::endl;
441

442
    return {meanBandwidth, meanCycleTime};
443
}
444

445
int wmain(int argc, wchar_t* argv[])
446
{
447
    enum class TestCase
448
    {
449
        Uncompressed,
450
#if USE_ZLIB
451
        CpuZLib,
452
#endif
453
        CpuGDeflate,
454
        GpuGDeflate
455
    };
456

457
    TestCase testCases[] =
458
    { TestCase::Uncompressed,
459
#if USE_ZLIB
460
      TestCase::CpuZLib,
461
#endif
462
      TestCase::CpuGDeflate,
463
      TestCase::GpuGDeflate };
464

465
    if (argc < 2)
466
    {
467
        ShowHelpText();
468
        return -1;
469
    }
470

471
    const wchar_t* originalFilename = argv[1];
472
    std::wstring gdeflateFilename = std::wstring(originalFilename) + L".gdeflate";
473

474
#if USE_ZLIB
475
    std::wstring zlibFilename = std::wstring(originalFilename) + L".zlib";
476
#endif
477

478
    uint32_t chunkSizeMiB = 16;
479
    if (argc > 2)
480
    {
481
        chunkSizeMiB = _wtoi(argv[2]);
482
        if (chunkSizeMiB == 0)
483
        {
484
            ShowHelpText();
485
            std::wcout << std::endl << L"Invalid chunk size: " << argv[2] << std::endl;
486
            return -1;
487
        }
488
    }
489
    uint32_t chunkSizeBytes = chunkSizeMiB * 1024 * 1024;
490

491
    Metadata uncompressedMetadata = GenerateUncompressedMetadata(originalFilename, chunkSizeBytes);
492
    Metadata gdeflateMetadata =
493
        Compress(DSTORAGE_COMPRESSION_FORMAT_GDEFLATE, originalFilename, gdeflateFilename.c_str(), chunkSizeBytes);
494

495
#if USE_ZLIB
496
    Metadata zlibMetadata =
497
        Compress(DSTORAGE_CUSTOM_COMPRESSION_0, originalFilename, zlibFilename.c_str(), chunkSizeBytes);
498
#endif
499

500
    constexpr uint32_t MAX_STAGING_BUFFER_SIZE = 1024;
501

502
    struct Result
503
    {
504
        TestCase TestCase;
505
        uint32_t StagingBufferSizeMiB;
506
        TestResult Data;
507
    };
508

509
    std::vector<Result> results;
510

511
    for (TestCase testCase : testCases)
512
    {
513
        DSTORAGE_COMPRESSION_FORMAT compressionFormat;
514
        DSTORAGE_CONFIGURATION config{};
515
        int numRuns = 0;
516
        Metadata* metadata = nullptr;
517
        wchar_t const* filename = nullptr;
518

519
        switch (testCase)
520
        {
521
        case TestCase::Uncompressed:
522
            compressionFormat = DSTORAGE_COMPRESSION_FORMAT_NONE;
523
            numRuns = 10;
524
            metadata = &uncompressedMetadata;
525
            filename = originalFilename;
526
            std::cout << "Uncompressed:" << std::endl;
527
            break;
528

529
#if USE_ZLIB
530
        case TestCase::CpuZLib:
531
            compressionFormat = DSTORAGE_CUSTOM_COMPRESSION_0;
532
            numRuns = 2;
533
            metadata = &zlibMetadata;
534
            filename = zlibFilename.c_str();
535
            std::cout << "ZLib:" << std::endl;
536
            break;
537
#endif
538

539
        case TestCase::CpuGDeflate:
540
            compressionFormat = DSTORAGE_COMPRESSION_FORMAT_GDEFLATE;
541
            numRuns = 2;
542

543
            // When forcing the CPU implementation of GDEFLATE we need to go
544
            // through the custom decompression path so we can ensure that
545
            // GDEFLATE doesn't try and decompress directly to an upload heap.
546
            config.NumBuiltInCpuDecompressionThreads = DSTORAGE_DISABLE_BUILTIN_CPU_DECOMPRESSION;
547
            config.DisableGpuDecompression = true;
548

549
            metadata = &gdeflateMetadata;
550
            filename = gdeflateFilename.c_str();
551
            std::cout << "CPU GDEFLATE:" << std::endl;
552
            break;
553

554
        case TestCase::GpuGDeflate:
555
            compressionFormat = DSTORAGE_COMPRESSION_FORMAT_GDEFLATE;
556
            numRuns = 10;
557
            metadata = &gdeflateMetadata;
558
            filename = gdeflateFilename.c_str();
559
            std::cout << "GPU GDEFLATE:" << std::endl;
560
            break;
561

562
        default:
563
            std::terminate();
564
        }
565

566
        check_hresult(DStorageSetConfiguration(&config));
567

568
        com_ptr<IDStorageFactory> factory;
569
        check_hresult(DStorageGetFactory(IID_PPV_ARGS(factory.put())));
570

571
        factory->SetDebugFlags(DSTORAGE_DEBUG_SHOW_ERRORS | DSTORAGE_DEBUG_BREAK_ON_ERROR);
572

573
        CustomDecompression customDecompression(factory.get(), std::thread::hardware_concurrency());
574

575
        for (uint32_t stagingSizeMiB = 1; stagingSizeMiB <= MAX_STAGING_BUFFER_SIZE; stagingSizeMiB *= 2)
576
        {
577
            if (stagingSizeMiB < chunkSizeMiB)
578
                continue;
579

580
            TestResult data = RunTest(factory.get(), stagingSizeMiB, filename, compressionFormat, *metadata, numRuns);
581

582
            results.push_back({testCase, stagingSizeMiB, data});
583
        }
584
    }
585

586
    std::cout << "\n\n";
587

588
    std::wstringstream bandwidth;
589
    std::wstringstream cycles;
590

591
    std::wstring header =
592
        L"\"Staging Buffer Size MiB\"\t\"Uncompressed\"\t\"ZLib\"\t\"CPU GDEFLATE\"\t\"GPU GDEFLATE\"";
593
    bandwidth << header << std::endl;
594
    cycles << header << std::endl;
595

596
    for (uint32_t stagingBufferSize = 1; stagingBufferSize <= MAX_STAGING_BUFFER_SIZE; stagingBufferSize *= 2)
597
    {
598
        std::wstringstream bandwidthRow;
599
        std::wstringstream cyclesRow;
600

601
        bandwidthRow << stagingBufferSize << "\t";
602
        cyclesRow << stagingBufferSize << "\t";
603

604
        constexpr bool showEmptyRows = true;
605

606
        bool foundOne = false;
607

608
        for (auto& testCase : testCases)
609
        {
610
            auto it = std::find_if(
611
                results.begin(),
612
                results.end(),
613
                [&](Result const& r) { return r.TestCase == testCase && r.StagingBufferSizeMiB == stagingBufferSize; });
614

615
            if (it == results.end())
616
            {
617
                bandwidthRow << L"\t";
618
                cyclesRow << L"\t";
619
            }
620
            else
621
            {
622
                bandwidthRow << it->Data.Bandwidth << L"\t";
623
                cyclesRow << it->Data.ProcessCycles << L"\t";
624
                foundOne = true;
625
            }
626
        }
627

628
        if (showEmptyRows || foundOne)
629
        {
630
            bandwidth << bandwidthRow.str() << std::endl;
631
            cycles << cyclesRow.str() << std::endl;
632
        }
633
    }
634

635
    std::wstringstream combined;
636
    combined << "Bandwidth" << std::endl
637
             << bandwidth.str() << std::endl
638
             << std::endl
639
             << "Cycles" << std::endl
640
             << cycles.str() << std::endl;
641

642
    combined << std::endl << "Compression" << std::endl;
643
    combined << "Case\tSize\tRatio" << std::endl;
644

645
    auto ratioLine = [&](char const* name, Metadata const& metadata)
646
    {
647
        combined << name << "\t" << metadata.CompressedSize << "\t"
648
                 << static_cast<double>(metadata.CompressedSize) / static_cast<double>(metadata.UncompressedSize)
649
                 << std::endl;
650
    };
651

652
    ratioLine("Uncompressed", uncompressedMetadata);
653
#if USE_ZLIB
654
    ratioLine("ZLib", zlibMetadata);
655
#else
656
    combined << "ZLib" << "\tn/a\tn/a" << std::endl;
657
#endif
658
    ratioLine("GDEFLATE", gdeflateMetadata);
659

660
    combined << std::endl;
661

662
    std::wcout << combined.str();
663

664
    try
665
    {
666
        SetClipboardText(combined.str());
667
        std::wcout << "\nThese results have been copied to the clipboard, ready to paste into Excel." << std::endl;
668
        return 0;
669
    }
670
    catch (...)
671
    {
672
        std::wcout << "\nFailed to copy results to clipboard. Sorry." << std::endl;
673
    }
674

675
    return 0;
676
}
677

678
void SetClipboardText(std::wstring const& str)
679
{
680
    using namespace winrt::Windows::ApplicationModel::DataTransfer;
681

682
    DataPackage dataPackage;
683
    dataPackage.SetText(str);
684

685
    Clipboard::SetContent(dataPackage);
686
    Clipboard::Flush();
687
}
688

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.