Add program to run compression benchmarks
authorEric Biggers <ebiggers3@gmail.com>
Sat, 2 Jul 2016 03:50:30 +0000 (22:50 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 2 Jul 2016 15:04:13 +0000 (10:04 -0500)
tools/run_compression_benchmarks.c [new file with mode: 0644]
tools/wimgapi_wrapper.h [new file with mode: 0644]

diff --git a/tools/run_compression_benchmarks.c b/tools/run_compression_benchmarks.c
new file mode 100644 (file)
index 0000000..eed7a9b
--- /dev/null
@@ -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 (file)
index 0000000..52a46fc
--- /dev/null
@@ -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 <stdio.h>
+#include <windows.h>
+#include "wimgapi.h"
+
+#define WIM_FLAG_SOLID 0x20000000 /* undocumented */