]> wimlib.net Git - wimlib/blob - tools/run_compression_benchmarks.c
ntfs-3g_apply.c: note version in which ntfs_attr_pwrite() was fixed
[wimlib] / tools / run_compression_benchmarks.c
1 /*
2  * run_compression_benchmarks.c
3  *
4  * Program to measure compression ratio and performance of wimlib and WIMGAPI.
5  */
6
7 #define _WIN32_WINNT 0x0602
8 #include "wimlib.h"
9 #include "wimgapi_wrapper.h"
10
11 #define ARRAY_LEN(A)    (sizeof(A) / sizeof((A)[0]))
12
13 #define VOLUME          L"E:\\"
14 #define INFILE          L"in.wim"
15 #define IN_IMAGE        1
16 #define OUTFILE         L"out.wim"
17 #define TMPDIR          L"."
18 #define OUTDIR          L"t"
19
20 static void
21 fatal_wimlib_error(const wchar_t *msg, int err)
22 {
23         fwprintf(stderr, L"Error %ls: wimlib error code %d: %ls\n", msg,
24                  err, wimlib_get_error_string(err));
25         exit(1);
26 }
27
28 static wchar_t *
29 get_win32_error_string(DWORD err)
30 {
31         static wchar_t buf[1024];
32         buf[0] = L'\0';
33         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0,
34                       buf, ARRAY_LEN(buf), NULL);
35         return buf;
36 }
37
38 static void
39 fatal_win32_error(const wchar_t *msg)
40 {
41         DWORD err = GetLastError();
42         fwprintf(stderr, L"Error %ls: Win32 error code %u: %ls\n", msg,
43                  err, get_win32_error_string(err));
44         exit(1);
45 }
46
47 static uint64_t start_time_ms;
48
49 static void
50 sync_volume(void)
51 {
52         wchar_t path[16];
53         HANDLE h;
54
55         wsprintf(path, L"\\\\.\\%lc:", VOLUME[0]);
56
57         h = CreateFile(path, GENERIC_WRITE, FILE_SHARE_VALID_FLAGS,
58                        NULL, OPEN_EXISTING, 0, NULL);
59
60         if (!FlushFileBuffers(h))
61                 fatal_win32_error(L"Unable to sync volume " VOLUME);
62
63         CloseHandle(h);
64 }
65
66 static void
67 prefetch_input_wim(void)
68 {
69         WIMStruct *wim;
70         int ret;
71
72         ret = wimlib_open_wim(INFILE, 0, &wim);
73         if (ret)
74                 fatal_wimlib_error(L"opening input WIM for prefetch", ret);
75
76         ret = wimlib_verify_wim(wim, 0);
77         if (ret)
78                 fatal_wimlib_error(L"prefetching input WIM", ret);
79
80         wimlib_free(wim);
81 }
82
83 static void
84 begin_test(void)
85 {
86         sync_volume();
87         prefetch_input_wim();
88         start_time_ms = GetTickCount64();
89 }
90
91 static void
92 verify_output_wim_with_wimlib(void)
93 {
94         WIMStruct *wim;
95         int ret;
96
97         ret = wimlib_open_wim(OUTFILE, 0, &wim);
98         if (ret) {
99                 fatal_wimlib_error(L"opening output WIM for verification with "
100                                    "wimlib", ret);
101         }
102
103         ret = wimlib_verify_wim(wim, 0);
104         if (ret)
105                 fatal_wimlib_error(L"verifying output WIM with wimlib", ret);
106
107         wimlib_free(wim);
108 }
109
110 static void
111 verify_output_wim_with_wimgapi(void)
112 {
113         HANDLE hWim;
114         HANDLE hImage;
115
116         hWim = WIMCreateFile(OUTFILE, WIM_GENERIC_READ, WIM_OPEN_EXISTING,
117                              WIM_FLAG_SOLID, 0, NULL);
118         if (!hWim) {
119                 fatal_win32_error(L"opening output WIM for verification with "
120                                   "WIMGAPI");
121         }
122         WIMSetTemporaryPath(hWim, TMPDIR);
123
124         hImage = WIMLoadImage(hWim, 1);
125         if (!hImage) {
126                 fatal_win32_error(L"loading WIM image for verification with "
127                                   "WIMGAPI");
128         }
129
130         CreateDirectory(OUTDIR, NULL);
131
132         if (!WIMApplyImage(hImage, OUTDIR, WIM_FLAG_VERIFY))
133                 fatal_win32_error(L"verifying output WIM with WIMGAPI");
134
135         WIMCloseHandle(hImage);
136         WIMCloseHandle(hWim);
137 }
138
139
140 static void
141 verify_output_wim(void)
142 {
143         verify_output_wim_with_wimlib();
144         verify_output_wim_with_wimgapi();
145 }
146
147 static void
148 end_test(const char *description)
149 {
150         uint64_t end_time_ms = GetTickCount64();
151         HANDLE h = CreateFile(OUTFILE, 0, FILE_SHARE_VALID_FLAGS, NULL,
152                               OPEN_EXISTING, 0, NULL);
153         LARGE_INTEGER compressed_size = {.QuadPart = -1};
154         GetFileSizeEx(h, &compressed_size);
155         CloseHandle(h);
156         printf("%s: %"PRIu64" in %.1fs\n", description,
157                compressed_size.QuadPart,
158                (double)(end_time_ms - start_time_ms) / 1000);
159
160         verify_output_wim();
161 }
162
163 static const struct wimlib_test_spec {
164         const char *description;
165         enum wimlib_compression_type ctype;
166         int level;
167         uint32_t chunk_size;
168         bool solid;
169 } wimlib_test_specs[] = {
170         {
171                 .description = "wimlib, LZMS (solid)",
172                 .ctype = WIMLIB_COMPRESSION_TYPE_LZMS,
173                 .solid = true,
174         },
175         {
176                 .description = "wimlib, LZMS (non-solid)",
177                 .ctype = WIMLIB_COMPRESSION_TYPE_LZMS,
178         },
179         {
180                 .description = "wimlib, LZX (slow)",
181                 .ctype = WIMLIB_COMPRESSION_TYPE_LZX,
182                 .level = 100,
183         },
184         {
185                 .description = "wimlib, LZX (normal)",
186                 .ctype = WIMLIB_COMPRESSION_TYPE_LZX,
187         },
188         {
189                 .description = "wimlib, LZX (quick)",
190                 .ctype = WIMLIB_COMPRESSION_TYPE_LZX,
191                 .level = 20,
192         },
193         {
194                 .description = "wimlib, XPRESS (slow)",
195                 .ctype = WIMLIB_COMPRESSION_TYPE_XPRESS,
196                 .level = 80,
197         },
198         {
199                 .description = "wimlib, XPRESS",
200                 .ctype = WIMLIB_COMPRESSION_TYPE_XPRESS,
201         },
202         {
203                 .description = "wimlib, \"WIMBoot\" (slow)",
204                 .ctype = WIMLIB_COMPRESSION_TYPE_XPRESS,
205                 .level = 80,
206                 .chunk_size = 4096,
207         },
208         {
209                 .description = "wimlib, \"WIMBoot\"",
210                 .ctype = WIMLIB_COMPRESSION_TYPE_XPRESS,
211                 .chunk_size = 4096,
212         },
213         {
214                 .description = "wimlib, None",
215                 .ctype = WIMLIB_COMPRESSION_TYPE_NONE,
216         },
217 };
218
219 static const struct wimgapi_test_spec {
220         const char *description;
221         DWORD compressionType;
222         bool solid;
223         bool wimboot;
224 } wimgapi_test_specs[] = {
225         {
226                 .description = "WIMGAPI, LZMS (solid)",
227                 .compressionType = WIM_COMPRESS_LZMS,
228                 .solid = true,
229         },
230         {
231                 .description = "WIMGAPI, LZX",
232                 .compressionType = WIM_COMPRESS_LZX,
233         },
234         {
235                 .description = "WIMGAPI, XPRESS",
236                 .compressionType = WIM_COMPRESS_XPRESS,
237         },
238         {
239                 .description = "WIMGAPI, \"WIMBoot\"",
240                 .compressionType = WIM_COMPRESS_XPRESS,
241                 .wimboot = true,
242         },
243         {
244                 .description = "WIMGAPI, None",
245                 .compressionType = WIM_COMPRESS_NONE,
246         },
247 };
248
249 static void
250 run_wimlib_test(const struct wimlib_test_spec *testspec)
251 {
252         WIMStruct *in, *out;
253         int ret;
254
255         ret = wimlib_set_default_compression_level(-1, 0);
256         if (ret)
257                 fatal_wimlib_error(L"resetting wimlib compression levels", ret);
258
259         begin_test();
260
261         ret = wimlib_open_wim(INFILE, 0, &in);
262         if (ret)
263                 fatal_wimlib_error(L"opening input WIM with wimlib", ret);
264
265         if (testspec->level) {
266                 ret = wimlib_set_default_compression_level(testspec->ctype,
267                                                            testspec->level);
268                 if (ret) {
269                         fatal_wimlib_error(L"setting wimlib compression level",
270                                            ret);
271                 }
272         }
273
274         ret = wimlib_create_new_wim(testspec->ctype, &out);
275         if (ret)
276                 fatal_wimlib_error(L"creating output WIMStruct", ret);
277
278         if (testspec->solid) {
279                 ret = wimlib_set_output_pack_compression_type(out,
280                                                               testspec->ctype);
281                 if (ret) {
282                         fatal_wimlib_error(L"setting wimlib solid compression "
283                                            "type", ret);
284                 }
285         }
286
287         if (testspec->chunk_size) {
288                 if (testspec->solid) {
289                         ret = wimlib_set_output_pack_chunk_size(out,
290                                                         testspec->chunk_size);
291                 } else {
292                         ret = wimlib_set_output_chunk_size(out,
293                                                            testspec->chunk_size);
294                 }
295                 if (ret) {
296                         fatal_wimlib_error(L"setting wimlib output chunk size",
297                                            ret);
298                 }
299         }
300
301         ret = wimlib_export_image(in, IN_IMAGE, out, NULL, NULL, 0);
302         if (ret)
303                 fatal_wimlib_error(L"exporting image with wimlib", ret);
304
305         ret = wimlib_write(out, OUTFILE, WIMLIB_ALL_IMAGES,
306                            (testspec->solid ? WIMLIB_WRITE_FLAG_SOLID : 0), 0);
307         if (ret)
308                 fatal_wimlib_error(L"writing output WIM with wimlib", ret);
309
310         wimlib_free(in);
311         wimlib_free(out);
312
313         end_test(testspec->description);
314 }
315
316 static void
317 run_wimgapi_test(const struct wimgapi_test_spec *testspec)
318 {
319         HANDLE hInWim, hOutWim;
320         HANDLE hInImage;
321         DWORD flags = 0;
322
323         begin_test();
324
325         hInWim = WIMCreateFile(INFILE, WIM_GENERIC_READ, WIM_OPEN_EXISTING,
326                                0, 0, NULL);
327
328         if (!hInWim)
329                 fatal_win32_error(L"opening input WIM with WIMGAPI");
330
331         WIMSetTemporaryPath(hInWim, TMPDIR);
332
333         hInImage = WIMLoadImage(hInWim, IN_IMAGE);
334         if (!hInImage)
335                 fatal_win32_error(L"loading input image with WIMGAPI");
336
337         if (testspec->solid)
338                 flags |= WIM_FLAG_SOLID;
339         if (testspec->wimboot)
340                 flags |= WIM_FLAG_WIM_BOOT;
341         hOutWim = WIMCreateFile(OUTFILE, WIM_GENERIC_WRITE, WIM_CREATE_ALWAYS,
342                                 flags, testspec->compressionType, NULL);
343         if (!hOutWim)
344                 fatal_win32_error(L"opening output WIM with WIMGAPI");
345
346         WIMSetTemporaryPath(hOutWim, TMPDIR);
347
348         if (!WIMExportImage(hInImage, hOutWim, 0))
349                 fatal_win32_error(L"exporting image with WIMGAPI");
350
351         WIMCloseHandle(hOutWim);
352         WIMCloseHandle(hInImage);
353         WIMCloseHandle(hInWim);
354
355         end_test(testspec->description);
356 }
357
358 int
359 wmain(int argc, wchar_t *argv[])
360 {
361         if (!SetCurrentDirectory(VOLUME))
362                 fatal_win32_error(L"changing directory to " VOLUME);
363
364         for (size_t i = 0; i < ARRAY_LEN(wimlib_test_specs); i++)
365                 run_wimlib_test(&wimlib_test_specs[i]);
366
367         for (size_t i = 0; i < ARRAY_LEN(wimgapi_test_specs); i++)
368                 run_wimgapi_test(&wimgapi_test_specs[i]);
369
370         return 0;
371 }