From: Eric Biggers Date: Sat, 2 Jul 2016 03:50:30 +0000 (-0500) Subject: Add program to run compression benchmarks X-Git-Tag: v1.10.0~35 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=4169b7d1888f88f2c0180e2c675461acfadaccc0 Add program to run compression benchmarks --- diff --git a/tools/run_compression_benchmarks.c b/tools/run_compression_benchmarks.c new file mode 100644 index 00000000..eed7a9b4 --- /dev/null +++ b/tools/run_compression_benchmarks.c @@ -0,0 +1,371 @@ +/* + * run_compression_benchmarks.c + * + * Program to measure compression ratio and performance of wimlib and WIMGAPI. + */ + +#define _WIN32_WINNT 0x0602 +#include "wimlib.h" +#include "wimgapi_wrapper.h" + +#define ARRAY_LEN(A) (sizeof(A) / sizeof((A)[0])) + +#define VOLUME L"E:\\" +#define INFILE L"in.wim" +#define IN_IMAGE 1 +#define OUTFILE L"out.wim" +#define TMPDIR L"." +#define OUTDIR L"t" + +static void +fatal_wimlib_error(const wchar_t *msg, int err) +{ + fwprintf(stderr, L"Error %ls: wimlib error code %d: %ls\n", msg, + err, wimlib_get_error_string(err)); + exit(1); +} + +static wchar_t * +get_win32_error_string(DWORD err) +{ + static wchar_t buf[1024]; + buf[0] = L'\0'; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, + buf, ARRAY_LEN(buf), NULL); + return buf; +} + +static void +fatal_win32_error(const wchar_t *msg) +{ + DWORD err = GetLastError(); + fwprintf(stderr, L"Error %ls: Win32 error code %u: %ls\n", msg, + err, get_win32_error_string(err)); + exit(1); +} + +static uint64_t start_time_ms; + +static void +sync_volume(void) +{ + wchar_t path[16]; + HANDLE h; + + wsprintf(path, L"\\\\.\\%lc:", VOLUME[0]); + + h = CreateFile(path, GENERIC_WRITE, FILE_SHARE_VALID_FLAGS, + NULL, OPEN_EXISTING, 0, NULL); + + if (!FlushFileBuffers(h)) + fatal_win32_error(L"Unable to sync volume " VOLUME); + + CloseHandle(h); +} + +static void +prefetch_input_wim(void) +{ + WIMStruct *wim; + int ret; + + ret = wimlib_open_wim(INFILE, 0, &wim); + if (ret) + fatal_wimlib_error(L"opening input WIM for prefetch", ret); + + ret = wimlib_verify_wim(wim, 0); + if (ret) + fatal_wimlib_error(L"prefetching input WIM", ret); + + wimlib_free(wim); +} + +static void +begin_test(void) +{ + sync_volume(); + prefetch_input_wim(); + start_time_ms = GetTickCount64(); +} + +static void +verify_output_wim_with_wimlib(void) +{ + WIMStruct *wim; + int ret; + + ret = wimlib_open_wim(OUTFILE, 0, &wim); + if (ret) { + fatal_wimlib_error(L"opening output WIM for verification with " + "wimlib", ret); + } + + ret = wimlib_verify_wim(wim, 0); + if (ret) + fatal_wimlib_error(L"verifying output WIM with wimlib", ret); + + wimlib_free(wim); +} + +static void +verify_output_wim_with_wimgapi(void) +{ + HANDLE hWim; + HANDLE hImage; + + hWim = WIMCreateFile(OUTFILE, WIM_GENERIC_READ, WIM_OPEN_EXISTING, + WIM_FLAG_SOLID, 0, NULL); + if (!hWim) { + fatal_win32_error(L"opening output WIM for verification with " + "WIMGAPI"); + } + WIMSetTemporaryPath(hWim, TMPDIR); + + hImage = WIMLoadImage(hWim, 1); + if (!hImage) { + fatal_win32_error(L"loading WIM image for verification with " + "WIMGAPI"); + } + + CreateDirectory(OUTDIR, NULL); + + if (!WIMApplyImage(hImage, OUTDIR, WIM_FLAG_VERIFY)) + fatal_win32_error(L"verifying output WIM with WIMGAPI"); + + WIMCloseHandle(hImage); + WIMCloseHandle(hWim); +} + + +static void +verify_output_wim(void) +{ + verify_output_wim_with_wimlib(); + verify_output_wim_with_wimgapi(); +} + +static void +end_test(const char *description) +{ + uint64_t end_time_ms = GetTickCount64(); + HANDLE h = CreateFile(OUTFILE, 0, FILE_SHARE_VALID_FLAGS, NULL, + OPEN_EXISTING, 0, NULL); + LARGE_INTEGER compressed_size = {.QuadPart = -1}; + GetFileSizeEx(h, &compressed_size); + CloseHandle(h); + printf("%s: %"PRIu64" in %.1fs\n", description, + compressed_size.QuadPart, + (double)(end_time_ms - start_time_ms) / 1000); + + verify_output_wim(); +} + +static const struct wimlib_test_spec { + const char *description; + enum wimlib_compression_type ctype; + int level; + uint32_t chunk_size; + bool solid; +} wimlib_test_specs[] = { + { + .description = "wimlib, LZMS (solid)", + .ctype = WIMLIB_COMPRESSION_TYPE_LZMS, + .solid = true, + }, + { + .description = "wimlib, LZMS (non-solid)", + .ctype = WIMLIB_COMPRESSION_TYPE_LZMS, + }, + { + .description = "wimlib, LZX (slow)", + .ctype = WIMLIB_COMPRESSION_TYPE_LZX, + .level = 100, + }, + { + .description = "wimlib, LZX (normal)", + .ctype = WIMLIB_COMPRESSION_TYPE_LZX, + }, + { + .description = "wimlib, LZX (quick)", + .ctype = WIMLIB_COMPRESSION_TYPE_LZX, + .level = 20, + }, + { + .description = "wimlib, XPRESS (slow)", + .ctype = WIMLIB_COMPRESSION_TYPE_XPRESS, + .level = 80, + }, + { + .description = "wimlib, XPRESS", + .ctype = WIMLIB_COMPRESSION_TYPE_XPRESS, + }, + { + .description = "wimlib, \"WIMBoot\" (slow)", + .ctype = WIMLIB_COMPRESSION_TYPE_XPRESS, + .level = 80, + .chunk_size = 4096, + }, + { + .description = "wimlib, \"WIMBoot\"", + .ctype = WIMLIB_COMPRESSION_TYPE_XPRESS, + .chunk_size = 4096, + }, + { + .description = "wimlib, None", + .ctype = WIMLIB_COMPRESSION_TYPE_NONE, + }, +}; + +static const struct wimgapi_test_spec { + const char *description; + DWORD compressionType; + bool solid; + bool wimboot; +} wimgapi_test_specs[] = { + { + .description = "WIMGAPI, LZMS (solid)", + .compressionType = WIM_COMPRESS_LZMS, + .solid = true, + }, + { + .description = "WIMGAPI, LZX", + .compressionType = WIM_COMPRESS_LZX, + }, + { + .description = "WIMGAPI, XPRESS", + .compressionType = WIM_COMPRESS_XPRESS, + }, + { + .description = "WIMGAPI, \"WIMBoot\"", + .compressionType = WIM_COMPRESS_XPRESS, + .wimboot = true, + }, + { + .description = "WIMGAPI, None", + .compressionType = WIM_COMPRESS_NONE, + }, +}; + +static void +run_wimlib_test(const struct wimlib_test_spec *testspec) +{ + WIMStruct *in, *out; + int ret; + + ret = wimlib_set_default_compression_level(-1, 0); + if (ret) + fatal_wimlib_error(L"resetting wimlib compression levels", ret); + + begin_test(); + + ret = wimlib_open_wim(INFILE, 0, &in); + if (ret) + fatal_wimlib_error(L"opening input WIM with wimlib", ret); + + if (testspec->level) { + ret = wimlib_set_default_compression_level(testspec->ctype, + testspec->level); + if (ret) { + fatal_wimlib_error(L"setting wimlib compression level", + ret); + } + } + + ret = wimlib_create_new_wim(testspec->ctype, &out); + if (ret) + fatal_wimlib_error(L"creating output WIMStruct", ret); + + if (testspec->solid) { + ret = wimlib_set_output_pack_compression_type(out, + testspec->ctype); + if (ret) { + fatal_wimlib_error(L"setting wimlib solid compression " + "type", ret); + } + } + + if (testspec->chunk_size) { + if (testspec->solid) { + ret = wimlib_set_output_pack_chunk_size(out, + testspec->chunk_size); + } else { + ret = wimlib_set_output_chunk_size(out, + testspec->chunk_size); + } + if (ret) { + fatal_wimlib_error(L"setting wimlib output chunk size", + ret); + } + } + + ret = wimlib_export_image(in, IN_IMAGE, out, NULL, NULL, 0); + if (ret) + fatal_wimlib_error(L"exporting image with wimlib", ret); + + ret = wimlib_write(out, OUTFILE, WIMLIB_ALL_IMAGES, + (testspec->solid ? WIMLIB_WRITE_FLAG_SOLID : 0), 0); + if (ret) + fatal_wimlib_error(L"writing output WIM with wimlib", ret); + + wimlib_free(in); + wimlib_free(out); + + end_test(testspec->description); +} + +static void +run_wimgapi_test(const struct wimgapi_test_spec *testspec) +{ + HANDLE hInWim, hOutWim; + HANDLE hInImage; + DWORD flags = 0; + + begin_test(); + + hInWim = WIMCreateFile(INFILE, WIM_GENERIC_READ, WIM_OPEN_EXISTING, + 0, 0, NULL); + + if (!hInWim) + fatal_win32_error(L"opening input WIM with WIMGAPI"); + + WIMSetTemporaryPath(hInWim, TMPDIR); + + hInImage = WIMLoadImage(hInWim, IN_IMAGE); + if (!hInImage) + fatal_win32_error(L"loading input image with WIMGAPI"); + + if (testspec->solid) + flags |= WIM_FLAG_SOLID; + if (testspec->wimboot) + flags |= WIM_FLAG_WIM_BOOT; + hOutWim = WIMCreateFile(OUTFILE, WIM_GENERIC_WRITE, WIM_CREATE_ALWAYS, + flags, testspec->compressionType, NULL); + if (!hOutWim) + fatal_win32_error(L"opening output WIM with WIMGAPI"); + + WIMSetTemporaryPath(hOutWim, TMPDIR); + + if (!WIMExportImage(hInImage, hOutWim, 0)) + fatal_win32_error(L"exporting image with WIMGAPI"); + + WIMCloseHandle(hOutWim); + WIMCloseHandle(hInImage); + WIMCloseHandle(hInWim); + + end_test(testspec->description); +} + +int +wmain(int argc, wchar_t *argv[]) +{ + if (!SetCurrentDirectory(VOLUME)) + fatal_win32_error(L"changing directory to " VOLUME); + + for (size_t i = 0; i < ARRAY_LEN(wimlib_test_specs); i++) + run_wimlib_test(&wimlib_test_specs[i]); + + for (size_t i = 0; i < ARRAY_LEN(wimgapi_test_specs); i++) + run_wimgapi_test(&wimgapi_test_specs[i]); + + return 0; +} diff --git a/tools/wimgapi_wrapper.h b/tools/wimgapi_wrapper.h new file mode 100644 index 00000000..52a46fc5 --- /dev/null +++ b/tools/wimgapi_wrapper.h @@ -0,0 +1,25 @@ +#define _In_ +#define __in +#define __out +#define __in_opt +#define __out_opt +#define __inout +#define __out_bcount(x) +#define __in_bcount(x) +#define __out_bcount_opt(x) +#define _Out_opt_ +#define _Out_ +#define _Out_writes_bytes_to_(x, y) +#define _Inout_ +#define _In_opt_ +#define _Out_writes_bytes_opt_(x) +#define _Out_writes_bytes_(x) +#define __typefix(x) +#define __deref_out_ecount_z(x) +#define _In_reads_bytes_(x) + +#include +#include +#include "wimgapi.h" + +#define WIM_FLAG_SOLID 0x20000000 /* undocumented */