zwrapbench.c revision 1.1 1 1.1 christos /*
2 1.1 christos * Copyright (c) Meta Platforms, Inc. and affiliates.
3 1.1 christos * All rights reserved.
4 1.1 christos *
5 1.1 christos * This source code is licensed under both the BSD-style license (found in the
6 1.1 christos * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7 1.1 christos * in the COPYING file in the root directory of this source tree).
8 1.1 christos */
9 1.1 christos
10 1.1 christos
11 1.1 christos /* *************************************
12 1.1 christos * Includes
13 1.1 christos ***************************************/
14 1.1 christos #include "util.h" /* Compiler options, UTIL_GetFileSize, UTIL_sleep */
15 1.1 christos #include <stdlib.h> /* malloc, free */
16 1.1 christos #include <string.h> /* memset */
17 1.1 christos #include <stdio.h> /* fprintf, fopen, ftello64 */
18 1.1 christos #include <time.h> /* clock_t, clock, CLOCKS_PER_SEC */
19 1.1 christos #include <ctype.h> /* toupper */
20 1.1 christos #include <errno.h> /* errno */
21 1.1 christos
22 1.1 christos #include "timefn.h" /* UTIL_time_t, UTIL_getTime, UTIL_clockSpanMicro, UTIL_waitForNextTick */
23 1.1 christos #include "mem.h"
24 1.1 christos #define ZSTD_STATIC_LINKING_ONLY
25 1.1 christos #include "zstd.h"
26 1.1 christos #include "datagen.h" /* RDG_genBuffer */
27 1.1 christos #include "xxhash.h"
28 1.1 christos
29 1.1 christos #include "../zstd_zlibwrapper.h"
30 1.1 christos
31 1.1 christos
32 1.1 christos
33 1.1 christos /*-************************************
34 1.1 christos * Tuning parameters
35 1.1 christos **************************************/
36 1.1 christos #ifndef ZSTDCLI_CLEVEL_DEFAULT
37 1.1 christos # define ZSTDCLI_CLEVEL_DEFAULT 3
38 1.1 christos #endif
39 1.1 christos
40 1.1 christos
41 1.1 christos /*-************************************
42 1.1 christos * Constants
43 1.1 christos **************************************/
44 1.1 christos #define COMPRESSOR_NAME "Zstandard wrapper for zlib command line interface"
45 1.1 christos #ifndef ZSTD_VERSION
46 1.1 christos # define ZSTD_VERSION "v" ZSTD_VERSION_STRING
47 1.1 christos #endif
48 1.1 christos #define AUTHOR "Yann Collet"
49 1.1 christos #define WELCOME_MESSAGE "*** %s %i-bits %s, by %s ***\n", COMPRESSOR_NAME, (int)(sizeof(size_t)*8), ZSTD_VERSION, AUTHOR
50 1.1 christos
51 1.1 christos #ifndef ZSTD_GIT_COMMIT
52 1.1 christos # define ZSTD_GIT_COMMIT_STRING ""
53 1.1 christos #else
54 1.1 christos # define ZSTD_GIT_COMMIT_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_GIT_COMMIT)
55 1.1 christos #endif
56 1.1 christos
57 1.1 christos #define NBLOOPS 3
58 1.1 christos #define TIMELOOP_MICROSEC 1*1000000ULL /* 1 second */
59 1.1 christos #define ACTIVEPERIOD_MICROSEC 70*1000000ULL /* 70 seconds */
60 1.1 christos #define COOLPERIOD_SEC 10
61 1.1 christos
62 1.1 christos #define KB *(1 <<10)
63 1.1 christos #define MB *(1 <<20)
64 1.1 christos #define GB *(1U<<30)
65 1.1 christos
66 1.1 christos static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31));
67 1.1 christos
68 1.1 christos static U32 g_compressibilityDefault = 50;
69 1.1 christos
70 1.1 christos
71 1.1 christos /* *************************************
72 1.1 christos * console display
73 1.1 christos ***************************************/
74 1.1 christos #define DEFAULT_DISPLAY_LEVEL 2
75 1.1 christos #define DISPLAY(...) fprintf(displayOut, __VA_ARGS__)
76 1.1 christos #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); }
77 1.1 christos static unsigned g_displayLevel = DEFAULT_DISPLAY_LEVEL; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */
78 1.1 christos static FILE* displayOut;
79 1.1 christos
80 1.1 christos #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \
81 1.1 christos if ((clock() - g_time > refreshRate) || (g_displayLevel>=4)) \
82 1.1 christos { g_time = clock(); DISPLAY(__VA_ARGS__); \
83 1.1 christos if (g_displayLevel>=4) fflush(displayOut); } }
84 1.1 christos static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100;
85 1.1 christos static clock_t g_time = 0;
86 1.1 christos
87 1.1 christos
88 1.1 christos /* *************************************
89 1.1 christos * Exceptions
90 1.1 christos ***************************************/
91 1.1 christos #ifndef DEBUG
92 1.1 christos # define DEBUG 0
93 1.1 christos #endif
94 1.1 christos #define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); }
95 1.1 christos #define EXM_THROW(error, ...) \
96 1.1 christos { \
97 1.1 christos DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \
98 1.1 christos DISPLAYLEVEL(1, "Error %i : ", error); \
99 1.1 christos DISPLAYLEVEL(1, __VA_ARGS__); \
100 1.1 christos DISPLAYLEVEL(1, "\n"); \
101 1.1 christos exit(error); \
102 1.1 christos }
103 1.1 christos
104 1.1 christos
105 1.1 christos /* *************************************
106 1.1 christos * Benchmark Parameters
107 1.1 christos ***************************************/
108 1.1 christos static unsigned g_nbIterations = NBLOOPS;
109 1.1 christos static size_t g_blockSize = 0;
110 1.1 christos int g_additionalParam = 0;
111 1.1 christos
112 1.1 christos static void BMK_setNotificationLevel(unsigned level) { g_displayLevel=level; }
113 1.1 christos
114 1.1 christos static void BMK_setAdditionalParam(int additionalParam) { g_additionalParam=additionalParam; }
115 1.1 christos
116 1.1 christos static void BMK_SetNbIterations(unsigned nbLoops)
117 1.1 christos {
118 1.1 christos g_nbIterations = nbLoops;
119 1.1 christos DISPLAYLEVEL(3, "- test >= %u seconds per compression / decompression -\n", g_nbIterations);
120 1.1 christos }
121 1.1 christos
122 1.1 christos static void BMK_SetBlockSize(size_t blockSize)
123 1.1 christos {
124 1.1 christos g_blockSize = blockSize;
125 1.1 christos DISPLAYLEVEL(2, "using blocks of size %u KB \n", (unsigned)(blockSize>>10));
126 1.1 christos }
127 1.1 christos
128 1.1 christos
129 1.1 christos /* ********************************************************
130 1.1 christos * Bench functions
131 1.1 christos **********************************************************/
132 1.1 christos #undef MIN
133 1.1 christos #undef MAX
134 1.1 christos #define MIN(a,b) ((a)<(b) ? (a) : (b))
135 1.1 christos #define MAX(a,b) ((a)>(b) ? (a) : (b))
136 1.1 christos
137 1.1 christos typedef struct
138 1.1 christos {
139 1.1 christos z_const char* srcPtr;
140 1.1 christos size_t srcSize;
141 1.1 christos char* cPtr;
142 1.1 christos size_t cRoom;
143 1.1 christos size_t cSize;
144 1.1 christos char* resPtr;
145 1.1 christos size_t resSize;
146 1.1 christos } blockParam_t;
147 1.1 christos
148 1.1 christos typedef enum { BMK_ZSTD, BMK_ZSTD_STREAM, BMK_ZLIB, BMK_ZWRAP_ZLIB, BMK_ZWRAP_ZSTD, BMK_ZLIB_REUSE, BMK_ZWRAP_ZLIB_REUSE, BMK_ZWRAP_ZSTD_REUSE } BMK_compressor;
149 1.1 christos
150 1.1 christos
151 1.1 christos static int BMK_benchMem(z_const void* srcBuffer, size_t srcSize,
152 1.1 christos const char* displayName, int cLevel,
153 1.1 christos const size_t* fileSizes, U32 nbFiles,
154 1.1 christos const void* dictBuffer, size_t dictBufferSize, BMK_compressor compressor)
155 1.1 christos {
156 1.1 christos size_t const blockSize = (g_blockSize>=32 ? g_blockSize : srcSize) + (!srcSize) /* avoid div by 0 */ ;
157 1.1 christos size_t const avgSize = MIN(g_blockSize, (srcSize / nbFiles));
158 1.1 christos U32 const maxNbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize) + nbFiles;
159 1.1 christos blockParam_t* const blockTable = (blockParam_t*) malloc(maxNbBlocks * sizeof(blockParam_t));
160 1.1 christos size_t const maxCompressedSize = ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); /* add some room for safety */
161 1.1 christos void* const compressedBuffer = malloc(maxCompressedSize);
162 1.1 christos void* const resultBuffer = malloc(srcSize);
163 1.1 christos ZSTD_CCtx* const ctx = ZSTD_createCCtx();
164 1.1 christos ZSTD_DCtx* const dctx = ZSTD_createDCtx();
165 1.1 christos U32 nbBlocks;
166 1.1 christos
167 1.1 christos /* checks */
168 1.1 christos if (!compressedBuffer || !resultBuffer || !blockTable || !ctx || !dctx)
169 1.1 christos EXM_THROW(31, "allocation error : not enough memory");
170 1.1 christos
171 1.1 christos /* init */
172 1.1 christos if (strlen(displayName)>17) displayName += strlen(displayName)-17; /* can only display 17 characters */
173 1.1 christos
174 1.1 christos /* Init blockTable data */
175 1.1 christos { z_const char* srcPtr = (z_const char*)srcBuffer;
176 1.1 christos char* cPtr = (char*)compressedBuffer;
177 1.1 christos char* resPtr = (char*)resultBuffer;
178 1.1 christos U32 fileNb;
179 1.1 christos for (nbBlocks=0, fileNb=0; fileNb<nbFiles; fileNb++) {
180 1.1 christos size_t remaining = fileSizes[fileNb];
181 1.1 christos U32 const nbBlocksforThisFile = (U32)((remaining + (blockSize-1)) / blockSize);
182 1.1 christos U32 const blockEnd = nbBlocks + nbBlocksforThisFile;
183 1.1 christos for ( ; nbBlocks<blockEnd; nbBlocks++) {
184 1.1 christos size_t const thisBlockSize = MIN(remaining, blockSize);
185 1.1 christos blockTable[nbBlocks].srcPtr = srcPtr;
186 1.1 christos blockTable[nbBlocks].cPtr = cPtr;
187 1.1 christos blockTable[nbBlocks].resPtr = resPtr;
188 1.1 christos blockTable[nbBlocks].srcSize = thisBlockSize;
189 1.1 christos blockTable[nbBlocks].cRoom = ZSTD_compressBound(thisBlockSize);
190 1.1 christos srcPtr += thisBlockSize;
191 1.1 christos cPtr += blockTable[nbBlocks].cRoom;
192 1.1 christos resPtr += thisBlockSize;
193 1.1 christos remaining -= thisBlockSize;
194 1.1 christos } } }
195 1.1 christos
196 1.1 christos /* warming up memory */
197 1.1 christos RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1);
198 1.1 christos
199 1.1 christos /* Bench */
200 1.1 christos { U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL);
201 1.1 christos U64 const crcOrig = XXH64(srcBuffer, srcSize, 0);
202 1.1 christos UTIL_time_t coolTime;
203 1.1 christos U64 const maxTime = (g_nbIterations * TIMELOOP_MICROSEC) + 100;
204 1.1 christos U64 totalCTime=0, totalDTime=0;
205 1.1 christos U32 cCompleted=0, dCompleted=0;
206 1.1 christos # define NB_MARKS 4
207 1.1 christos const char* const marks[NB_MARKS] = { " |", " /", " =", "\\" };
208 1.1 christos U32 markNb = 0;
209 1.1 christos size_t cSize = 0;
210 1.1 christos double ratio = 0.;
211 1.1 christos
212 1.1 christos coolTime = UTIL_getTime();
213 1.1 christos DISPLAYLEVEL(2, "\r%79s\r", "");
214 1.1 christos while (!cCompleted | !dCompleted) {
215 1.1 christos UTIL_time_t clockStart;
216 1.1 christos U64 clockLoop = g_nbIterations ? TIMELOOP_MICROSEC : 1;
217 1.1 christos
218 1.1 christos /* overheat protection */
219 1.1 christos if (UTIL_clockSpanMicro(coolTime) > ACTIVEPERIOD_MICROSEC) {
220 1.1 christos DISPLAYLEVEL(2, "\rcooling down ... \r");
221 1.1 christos UTIL_sleep(COOLPERIOD_SEC);
222 1.1 christos coolTime = UTIL_getTime();
223 1.1 christos }
224 1.1 christos
225 1.1 christos /* Compression */
226 1.1 christos DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (unsigned)srcSize);
227 1.1 christos if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */
228 1.1 christos
229 1.1 christos UTIL_sleepMilli(1); /* give processor time to other processes */
230 1.1 christos UTIL_waitForNextTick();
231 1.1 christos clockStart = UTIL_getTime();
232 1.1 christos
233 1.1 christos if (!cCompleted) { /* still some time to do compression tests */
234 1.1 christos U32 nbLoops = 0;
235 1.1 christos if (compressor == BMK_ZSTD) {
236 1.1 christos ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize);
237 1.1 christos ZSTD_customMem const cmem = { NULL, NULL, NULL };
238 1.1 christos ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dct_auto, zparams.cParams, cmem);
239 1.1 christos if (cdict==NULL) EXM_THROW(1, "ZSTD_createCDict_advanced() allocation failure");
240 1.1 christos
241 1.1 christos do {
242 1.1 christos U32 blockNb;
243 1.1 christos size_t rSize;
244 1.1 christos for (blockNb=0; blockNb<nbBlocks; blockNb++) {
245 1.1 christos if (dictBufferSize) {
246 1.1 christos rSize = ZSTD_compress_usingCDict(ctx,
247 1.1 christos blockTable[blockNb].cPtr, blockTable[blockNb].cRoom,
248 1.1 christos blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize,
249 1.1 christos cdict);
250 1.1 christos } else {
251 1.1 christos rSize = ZSTD_compressCCtx (ctx,
252 1.1 christos blockTable[blockNb].cPtr, blockTable[blockNb].cRoom,
253 1.1 christos blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, cLevel);
254 1.1 christos }
255 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_compress_usingCDict() failed : %s", ZSTD_getErrorName(rSize));
256 1.1 christos blockTable[blockNb].cSize = rSize;
257 1.1 christos }
258 1.1 christos nbLoops++;
259 1.1 christos } while (UTIL_clockSpanMicro(clockStart) < clockLoop);
260 1.1 christos ZSTD_freeCDict(cdict);
261 1.1 christos } else if (compressor == BMK_ZSTD_STREAM) {
262 1.1 christos ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize);
263 1.1 christos ZSTD_inBuffer inBuffer;
264 1.1 christos ZSTD_outBuffer outBuffer;
265 1.1 christos ZSTD_CStream* zbc = ZSTD_createCStream();
266 1.1 christos size_t rSize;
267 1.1 christos ZSTD_CCtx_params* cctxParams = ZSTD_createCCtxParams();
268 1.1 christos
269 1.1 christos if (!cctxParams) EXM_THROW(1, "ZSTD_createCCtxParams() allocation failure");
270 1.1 christos if (zbc == NULL) EXM_THROW(1, "ZSTD_createCStream() allocation failure");
271 1.1 christos
272 1.1 christos { int initErr = 0;
273 1.1 christos initErr |= ZSTD_isError(ZSTD_CCtx_reset(zbc, ZSTD_reset_session_only));
274 1.1 christos initErr |= ZSTD_isError(ZSTD_CCtxParams_init_advanced(cctxParams, zparams));
275 1.1 christos initErr |= ZSTD_isError(ZSTD_CCtx_setParametersUsingCCtxParams(zbc, cctxParams));
276 1.1 christos initErr |= ZSTD_isError(ZSTD_CCtx_setPledgedSrcSize(zbc, avgSize));
277 1.1 christos initErr |= ZSTD_isError(ZSTD_CCtx_loadDictionary(zbc, dictBuffer, dictBufferSize));
278 1.1 christos
279 1.1 christos ZSTD_freeCCtxParams(cctxParams);
280 1.1 christos if (initErr) EXM_THROW(1, "CCtx init failed!");
281 1.1 christos }
282 1.1 christos
283 1.1 christos do {
284 1.1 christos U32 blockNb;
285 1.1 christos for (blockNb=0; blockNb<nbBlocks; blockNb++) {
286 1.1 christos rSize = ZSTD_CCtx_reset(zbc, ZSTD_reset_session_only);
287 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_CCtx_reset() failed : %s", ZSTD_getErrorName(rSize));
288 1.1 christos rSize = ZSTD_CCtx_setPledgedSrcSize(zbc, blockTable[blockNb].srcSize);
289 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_CCtx_setPledgedSrcSize() failed : %s", ZSTD_getErrorName(rSize));
290 1.1 christos inBuffer.src = blockTable[blockNb].srcPtr;
291 1.1 christos inBuffer.size = blockTable[blockNb].srcSize;
292 1.1 christos inBuffer.pos = 0;
293 1.1 christos outBuffer.dst = blockTable[blockNb].cPtr;
294 1.1 christos outBuffer.size = blockTable[blockNb].cRoom;
295 1.1 christos outBuffer.pos = 0;
296 1.1 christos rSize = ZSTD_compressStream(zbc, &outBuffer, &inBuffer);
297 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_compressStream() failed : %s", ZSTD_getErrorName(rSize));
298 1.1 christos rSize = ZSTD_endStream(zbc, &outBuffer);
299 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_endStream() failed : %s", ZSTD_getErrorName(rSize));
300 1.1 christos blockTable[blockNb].cSize = outBuffer.pos;
301 1.1 christos }
302 1.1 christos nbLoops++;
303 1.1 christos } while (UTIL_clockSpanMicro(clockStart) < clockLoop);
304 1.1 christos ZSTD_freeCStream(zbc);
305 1.1 christos } else if (compressor == BMK_ZWRAP_ZLIB_REUSE || compressor == BMK_ZWRAP_ZSTD_REUSE || compressor == BMK_ZLIB_REUSE) {
306 1.1 christos z_stream def;
307 1.1 christos int ret;
308 1.1 christos int useSetDict = (dictBuffer != NULL);
309 1.1 christos if (compressor == BMK_ZLIB_REUSE || compressor == BMK_ZWRAP_ZLIB_REUSE) ZWRAP_useZSTDcompression(0);
310 1.1 christos else ZWRAP_useZSTDcompression(1);
311 1.1 christos def.zalloc = Z_NULL;
312 1.1 christos def.zfree = Z_NULL;
313 1.1 christos def.opaque = Z_NULL;
314 1.1 christos ret = deflateInit(&def, cLevel);
315 1.1 christos if (ret != Z_OK) EXM_THROW(1, "deflateInit failure");
316 1.1 christos /* if (ZWRAP_isUsingZSTDcompression()) {
317 1.1 christos ret = ZWRAP_setPledgedSrcSize(&def, avgSize);
318 1.1 christos if (ret != Z_OK) EXM_THROW(1, "ZWRAP_setPledgedSrcSize failure");
319 1.1 christos } */
320 1.1 christos do {
321 1.1 christos U32 blockNb;
322 1.1 christos for (blockNb=0; blockNb<nbBlocks; blockNb++) {
323 1.1 christos if (ZWRAP_isUsingZSTDcompression())
324 1.1 christos ret = ZWRAP_deflateReset_keepDict(&def); /* reuse dictionary to make compression faster */
325 1.1 christos else
326 1.1 christos ret = deflateReset(&def);
327 1.1 christos if (ret != Z_OK) EXM_THROW(1, "deflateReset failure");
328 1.1 christos if (useSetDict) {
329 1.1 christos ret = deflateSetDictionary(&def, (const z_Bytef*)dictBuffer, dictBufferSize);
330 1.1 christos if (ret != Z_OK) EXM_THROW(1, "deflateSetDictionary failure");
331 1.1 christos if (ZWRAP_isUsingZSTDcompression()) useSetDict = 0; /* zstd doesn't require deflateSetDictionary after ZWRAP_deflateReset_keepDict */
332 1.1 christos }
333 1.1 christos def.next_in = (z_const z_Bytef*) blockTable[blockNb].srcPtr;
334 1.1 christos def.avail_in = (uInt)blockTable[blockNb].srcSize;
335 1.1 christos def.total_in = 0;
336 1.1 christos def.next_out = (z_Bytef*) blockTable[blockNb].cPtr;
337 1.1 christos def.avail_out = (uInt)blockTable[blockNb].cRoom;
338 1.1 christos def.total_out = 0;
339 1.1 christos ret = deflate(&def, Z_FINISH);
340 1.1 christos if (ret != Z_STREAM_END) EXM_THROW(1, "deflate failure ret=%d srcSize=%d" , ret, (int)blockTable[blockNb].srcSize);
341 1.1 christos blockTable[blockNb].cSize = def.total_out;
342 1.1 christos }
343 1.1 christos nbLoops++;
344 1.1 christos } while (UTIL_clockSpanMicro(clockStart) < clockLoop);
345 1.1 christos ret = deflateEnd(&def);
346 1.1 christos if (ret != Z_OK) EXM_THROW(1, "deflateEnd failure");
347 1.1 christos } else {
348 1.1 christos z_stream def;
349 1.1 christos if (compressor == BMK_ZLIB || compressor == BMK_ZWRAP_ZLIB) ZWRAP_useZSTDcompression(0);
350 1.1 christos else ZWRAP_useZSTDcompression(1);
351 1.1 christos do {
352 1.1 christos U32 blockNb;
353 1.1 christos for (blockNb=0; blockNb<nbBlocks; blockNb++) {
354 1.1 christos int ret;
355 1.1 christos def.zalloc = Z_NULL;
356 1.1 christos def.zfree = Z_NULL;
357 1.1 christos def.opaque = Z_NULL;
358 1.1 christos ret = deflateInit(&def, cLevel);
359 1.1 christos if (ret != Z_OK) EXM_THROW(1, "deflateInit failure");
360 1.1 christos if (dictBuffer) {
361 1.1 christos ret = deflateSetDictionary(&def, (const z_Bytef*)dictBuffer, dictBufferSize);
362 1.1 christos if (ret != Z_OK) EXM_THROW(1, "deflateSetDictionary failure");
363 1.1 christos }
364 1.1 christos def.next_in = (z_const z_Bytef*) blockTable[blockNb].srcPtr;
365 1.1 christos def.avail_in = (uInt)blockTable[blockNb].srcSize;
366 1.1 christos def.total_in = 0;
367 1.1 christos def.next_out = (z_Bytef*) blockTable[blockNb].cPtr;
368 1.1 christos def.avail_out = (uInt)blockTable[blockNb].cRoom;
369 1.1 christos def.total_out = 0;
370 1.1 christos ret = deflate(&def, Z_FINISH);
371 1.1 christos if (ret != Z_STREAM_END) EXM_THROW(1, "deflate failure");
372 1.1 christos ret = deflateEnd(&def);
373 1.1 christos if (ret != Z_OK) EXM_THROW(1, "deflateEnd failure");
374 1.1 christos blockTable[blockNb].cSize = def.total_out;
375 1.1 christos }
376 1.1 christos nbLoops++;
377 1.1 christos } while (UTIL_clockSpanMicro(clockStart) < clockLoop);
378 1.1 christos }
379 1.1 christos { U64 const clockSpan = UTIL_clockSpanMicro(clockStart);
380 1.1 christos if (clockSpan < fastestC*nbLoops) fastestC = clockSpan / nbLoops;
381 1.1 christos totalCTime += clockSpan;
382 1.1 christos cCompleted = totalCTime>maxTime;
383 1.1 christos } }
384 1.1 christos
385 1.1 christos cSize = 0;
386 1.1 christos { U32 blockNb; for (blockNb=0; blockNb<nbBlocks; blockNb++) cSize += blockTable[blockNb].cSize; }
387 1.1 christos ratio = (double)srcSize / (double)cSize;
388 1.1 christos markNb = (markNb+1) % NB_MARKS;
389 1.1 christos DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s\r",
390 1.1 christos marks[markNb], displayName, (unsigned)srcSize, (unsigned)cSize, ratio,
391 1.1 christos (double)srcSize / (double)fastestC );
392 1.1 christos
393 1.1 christos (void)fastestD; (void)crcOrig; /* unused when decompression disabled */
394 1.1 christos #if 1
395 1.1 christos /* Decompression */
396 1.1 christos if (!dCompleted) memset(resultBuffer, 0xD6, srcSize); /* warm result buffer */
397 1.1 christos
398 1.1 christos UTIL_sleepMilli(1); /* give processor time to other processes */
399 1.1 christos UTIL_waitForNextTick();
400 1.1 christos clockStart = UTIL_getTime();
401 1.1 christos
402 1.1 christos if (!dCompleted) {
403 1.1 christos U32 nbLoops = 0;
404 1.1 christos if (compressor == BMK_ZSTD) {
405 1.1 christos ZSTD_DDict* ddict = ZSTD_createDDict(dictBuffer, dictBufferSize);
406 1.1 christos if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure");
407 1.1 christos do {
408 1.1 christos unsigned blockNb;
409 1.1 christos for (blockNb=0; blockNb<nbBlocks; blockNb++) {
410 1.1 christos size_t const regenSize = ZSTD_decompress_usingDDict(dctx,
411 1.1 christos blockTable[blockNb].resPtr, blockTable[blockNb].srcSize,
412 1.1 christos blockTable[blockNb].cPtr, blockTable[blockNb].cSize,
413 1.1 christos ddict);
414 1.1 christos if (ZSTD_isError(regenSize)) {
415 1.1 christos DISPLAY("ZSTD_decompress_usingDDict() failed on block %u : %s \n",
416 1.1 christos blockNb, ZSTD_getErrorName(regenSize));
417 1.1 christos clockLoop = 0; /* force immediate test end */
418 1.1 christos break;
419 1.1 christos }
420 1.1 christos blockTable[blockNb].resSize = regenSize;
421 1.1 christos }
422 1.1 christos nbLoops++;
423 1.1 christos } while (UTIL_clockSpanMicro(clockStart) < clockLoop);
424 1.1 christos ZSTD_freeDDict(ddict);
425 1.1 christos } else if (compressor == BMK_ZSTD_STREAM) {
426 1.1 christos ZSTD_inBuffer inBuffer;
427 1.1 christos ZSTD_outBuffer outBuffer;
428 1.1 christos ZSTD_DStream* zbd = ZSTD_createDStream();
429 1.1 christos size_t rSize;
430 1.1 christos if (zbd == NULL) EXM_THROW(1, "ZSTD_createDStream() allocation failure");
431 1.1 christos rSize = ZSTD_DCtx_reset(zbd, ZSTD_reset_session_only);
432 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_DCtx_reset() failed : %s", ZSTD_getErrorName(rSize));
433 1.1 christos rSize = ZSTD_DCtx_loadDictionary(zbd, dictBuffer, dictBufferSize);
434 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_DCtx_loadDictionary() failed : %s", ZSTD_getErrorName(rSize));
435 1.1 christos do {
436 1.1 christos U32 blockNb;
437 1.1 christos for (blockNb=0; blockNb<nbBlocks; blockNb++) {
438 1.1 christos rSize = ZSTD_DCtx_reset(zbd, ZSTD_reset_session_only);
439 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_DCtx_reset() failed : %s", ZSTD_getErrorName(rSize));
440 1.1 christos inBuffer.src = blockTable[blockNb].cPtr;
441 1.1 christos inBuffer.size = blockTable[blockNb].cSize;
442 1.1 christos inBuffer.pos = 0;
443 1.1 christos outBuffer.dst = blockTable[blockNb].resPtr;
444 1.1 christos outBuffer.size = blockTable[blockNb].srcSize;
445 1.1 christos outBuffer.pos = 0;
446 1.1 christos rSize = ZSTD_decompressStream(zbd, &outBuffer, &inBuffer);
447 1.1 christos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_decompressStream() failed : %s", ZSTD_getErrorName(rSize));
448 1.1 christos blockTable[blockNb].resSize = outBuffer.pos;
449 1.1 christos }
450 1.1 christos nbLoops++;
451 1.1 christos } while (UTIL_clockSpanMicro(clockStart) < clockLoop);
452 1.1 christos ZSTD_freeDStream(zbd);
453 1.1 christos } else if (compressor == BMK_ZWRAP_ZLIB_REUSE || compressor == BMK_ZWRAP_ZSTD_REUSE || compressor == BMK_ZLIB_REUSE) {
454 1.1 christos z_stream inf;
455 1.1 christos int ret;
456 1.1 christos if (compressor == BMK_ZLIB_REUSE) ZWRAP_setDecompressionType(ZWRAP_FORCE_ZLIB);
457 1.1 christos else ZWRAP_setDecompressionType(ZWRAP_AUTO);
458 1.1 christos inf.zalloc = Z_NULL;
459 1.1 christos inf.zfree = Z_NULL;
460 1.1 christos inf.opaque = Z_NULL;
461 1.1 christos ret = inflateInit(&inf);
462 1.1 christos if (ret != Z_OK) EXM_THROW(1, "inflateInit failure");
463 1.1 christos do {
464 1.1 christos U32 blockNb;
465 1.1 christos for (blockNb=0; blockNb<nbBlocks; blockNb++) {
466 1.1 christos if (ZWRAP_isUsingZSTDdecompression(&inf))
467 1.1 christos ret = ZWRAP_inflateReset_keepDict(&inf); /* reuse dictionary to make decompression faster; inflate will return Z_NEED_DICT only for the first time */
468 1.1 christos else
469 1.1 christos ret = inflateReset(&inf);
470 1.1 christos if (ret != Z_OK) EXM_THROW(1, "inflateReset failure");
471 1.1 christos inf.next_in = (z_const z_Bytef*) blockTable[blockNb].cPtr;
472 1.1 christos inf.avail_in = (uInt)blockTable[blockNb].cSize;
473 1.1 christos inf.total_in = 0;
474 1.1 christos inf.next_out = (z_Bytef*) blockTable[blockNb].resPtr;
475 1.1 christos inf.avail_out = (uInt)blockTable[blockNb].srcSize;
476 1.1 christos inf.total_out = 0;
477 1.1 christos ret = inflate(&inf, Z_FINISH);
478 1.1 christos if (ret == Z_NEED_DICT) {
479 1.1 christos ret = inflateSetDictionary(&inf, (const z_Bytef*)dictBuffer, dictBufferSize);
480 1.1 christos if (ret != Z_OK) EXM_THROW(1, "inflateSetDictionary failure");
481 1.1 christos ret = inflate(&inf, Z_FINISH);
482 1.1 christos }
483 1.1 christos if (ret != Z_STREAM_END) EXM_THROW(1, "inflate failure");
484 1.1 christos blockTable[blockNb].resSize = inf.total_out;
485 1.1 christos }
486 1.1 christos nbLoops++;
487 1.1 christos } while (UTIL_clockSpanMicro(clockStart) < clockLoop);
488 1.1 christos ret = inflateEnd(&inf);
489 1.1 christos if (ret != Z_OK) EXM_THROW(1, "inflateEnd failure");
490 1.1 christos } else {
491 1.1 christos z_stream inf;
492 1.1 christos if (compressor == BMK_ZLIB) ZWRAP_setDecompressionType(ZWRAP_FORCE_ZLIB);
493 1.1 christos else ZWRAP_setDecompressionType(ZWRAP_AUTO);
494 1.1 christos do {
495 1.1 christos U32 blockNb;
496 1.1 christos for (blockNb=0; blockNb<nbBlocks; blockNb++) {
497 1.1 christos int ret;
498 1.1 christos inf.zalloc = Z_NULL;
499 1.1 christos inf.zfree = Z_NULL;
500 1.1 christos inf.opaque = Z_NULL;
501 1.1 christos ret = inflateInit(&inf);
502 1.1 christos if (ret != Z_OK) EXM_THROW(1, "inflateInit failure");
503 1.1 christos inf.next_in = (z_const z_Bytef*) blockTable[blockNb].cPtr;
504 1.1 christos inf.avail_in = (uInt)blockTable[blockNb].cSize;
505 1.1 christos inf.total_in = 0;
506 1.1 christos inf.next_out = (z_Bytef*) blockTable[blockNb].resPtr;
507 1.1 christos inf.avail_out = (uInt)blockTable[blockNb].srcSize;
508 1.1 christos inf.total_out = 0;
509 1.1 christos ret = inflate(&inf, Z_FINISH);
510 1.1 christos if (ret == Z_NEED_DICT) {
511 1.1 christos ret = inflateSetDictionary(&inf, (const z_Bytef*) dictBuffer, dictBufferSize);
512 1.1 christos if (ret != Z_OK) EXM_THROW(1, "inflateSetDictionary failure");
513 1.1 christos ret = inflate(&inf, Z_FINISH);
514 1.1 christos }
515 1.1 christos if (ret != Z_STREAM_END) EXM_THROW(1, "inflate failure");
516 1.1 christos ret = inflateEnd(&inf);
517 1.1 christos if (ret != Z_OK) EXM_THROW(1, "inflateEnd failure");
518 1.1 christos blockTable[blockNb].resSize = inf.total_out;
519 1.1 christos }
520 1.1 christos nbLoops++;
521 1.1 christos } while (UTIL_clockSpanMicro(clockStart) < clockLoop);
522 1.1 christos }
523 1.1 christos { U64 const clockSpan = UTIL_clockSpanMicro(clockStart);
524 1.1 christos if (clockSpan < fastestD*nbLoops) fastestD = clockSpan / nbLoops;
525 1.1 christos totalDTime += clockSpan;
526 1.1 christos dCompleted = totalDTime>maxTime;
527 1.1 christos } }
528 1.1 christos
529 1.1 christos markNb = (markNb+1) % NB_MARKS;
530 1.1 christos DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s ,%6.1f MB/s\r",
531 1.1 christos marks[markNb], displayName, (unsigned)srcSize, (unsigned)cSize, ratio,
532 1.1 christos (double)srcSize / (double)fastestC,
533 1.1 christos (double)srcSize / (double)fastestD );
534 1.1 christos
535 1.1 christos /* CRC Checking */
536 1.1 christos { U64 const crcCheck = XXH64(resultBuffer, srcSize, 0);
537 1.1 christos if (crcOrig!=crcCheck) {
538 1.1 christos size_t u;
539 1.1 christos DISPLAY("!!! WARNING !!! %14s : Invalid Checksum : %x != %x \n", displayName, (unsigned)crcOrig, (unsigned)crcCheck);
540 1.1 christos for (u=0; u<srcSize; u++) {
541 1.1 christos if (((const BYTE*)srcBuffer)[u] != ((const BYTE*)resultBuffer)[u]) {
542 1.1 christos unsigned segNb, bNb, pos;
543 1.1 christos size_t bacc = 0;
544 1.1 christos DISPLAY("Decoding error at pos %u ", (unsigned)u);
545 1.1 christos for (segNb = 0; segNb < nbBlocks; segNb++) {
546 1.1 christos if (bacc + blockTable[segNb].srcSize > u) break;
547 1.1 christos bacc += blockTable[segNb].srcSize;
548 1.1 christos }
549 1.1 christos pos = (U32)(u - bacc);
550 1.1 christos bNb = pos / (128 KB);
551 1.1 christos DISPLAY("(block %u, sub %u, pos %u) \n", segNb, bNb, pos);
552 1.1 christos break;
553 1.1 christos }
554 1.1 christos if (u==srcSize-1) { /* should never happen */
555 1.1 christos DISPLAY("no difference detected\n");
556 1.1 christos } }
557 1.1 christos break;
558 1.1 christos } } /* CRC Checking */
559 1.1 christos #endif
560 1.1 christos } /* for (testNb = 1; testNb <= (g_nbIterations + !g_nbIterations); testNb++) */
561 1.1 christos
562 1.1 christos if (g_displayLevel == 1) {
563 1.1 christos double cSpeed = (double)srcSize / (double)fastestC;
564 1.1 christos double dSpeed = (double)srcSize / (double)fastestD;
565 1.1 christos if (g_additionalParam)
566 1.1 christos DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName, g_additionalParam);
567 1.1 christos else
568 1.1 christos DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName);
569 1.1 christos }
570 1.1 christos DISPLAYLEVEL(2, "%2i#\n", cLevel);
571 1.1 christos } /* Bench */
572 1.1 christos
573 1.1 christos /* clean up */
574 1.1 christos free(blockTable);
575 1.1 christos free(compressedBuffer);
576 1.1 christos free(resultBuffer);
577 1.1 christos ZSTD_freeCCtx(ctx);
578 1.1 christos ZSTD_freeDCtx(dctx);
579 1.1 christos return 0;
580 1.1 christos }
581 1.1 christos
582 1.1 christos
583 1.1 christos static size_t BMK_findMaxMem(U64 requiredMem)
584 1.1 christos {
585 1.1 christos size_t const step = 64 MB;
586 1.1 christos BYTE* testmem = NULL;
587 1.1 christos
588 1.1 christos requiredMem = (((requiredMem >> 26) + 1) << 26);
589 1.1 christos requiredMem += step;
590 1.1 christos if (requiredMem > maxMemory) requiredMem = maxMemory;
591 1.1 christos
592 1.1 christos do {
593 1.1 christos testmem = (BYTE*)malloc((size_t)requiredMem);
594 1.1 christos requiredMem -= step;
595 1.1 christos } while (!testmem && requiredMem); /* do not allocate zero bytes */
596 1.1 christos
597 1.1 christos free(testmem);
598 1.1 christos return (size_t)(requiredMem+1); /* avoid zero */
599 1.1 christos }
600 1.1 christos
601 1.1 christos static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize,
602 1.1 christos const char* displayName, int cLevel, int cLevelLast,
603 1.1 christos const size_t* fileSizes, unsigned nbFiles,
604 1.1 christos const void* dictBuffer, size_t dictBufferSize)
605 1.1 christos {
606 1.1 christos int l;
607 1.1 christos
608 1.1 christos const char* pch = strrchr(displayName, '\\'); /* Windows */
609 1.1 christos if (!pch) pch = strrchr(displayName, '/'); /* Linux */
610 1.1 christos if (pch) displayName = pch+1;
611 1.1 christos
612 1.1 christos SET_REALTIME_PRIORITY;
613 1.1 christos
614 1.1 christos if (g_displayLevel == 1 && !g_additionalParam)
615 1.1 christos DISPLAY("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n",
616 1.1 christos ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING,
617 1.1 christos (unsigned)benchedSize, g_nbIterations, (unsigned)(g_blockSize>>10));
618 1.1 christos
619 1.1 christos if (cLevelLast < cLevel) cLevelLast = cLevel;
620 1.1 christos
621 1.1 christos DISPLAY("benchmarking zstd %s (using ZSTD_CStream)\n", ZSTD_VERSION_STRING);
622 1.1 christos for (l=cLevel; l <= cLevelLast; l++) {
623 1.1 christos BMK_benchMem(srcBuffer, benchedSize,
624 1.1 christos displayName, l,
625 1.1 christos fileSizes, nbFiles,
626 1.1 christos dictBuffer, dictBufferSize, BMK_ZSTD_STREAM);
627 1.1 christos }
628 1.1 christos
629 1.1 christos DISPLAY("benchmarking zstd %s (using ZSTD_CCtx)\n", ZSTD_VERSION_STRING);
630 1.1 christos for (l=cLevel; l <= cLevelLast; l++) {
631 1.1 christos BMK_benchMem(srcBuffer, benchedSize,
632 1.1 christos displayName, l,
633 1.1 christos fileSizes, nbFiles,
634 1.1 christos dictBuffer, dictBufferSize, BMK_ZSTD);
635 1.1 christos }
636 1.1 christos
637 1.1 christos DISPLAY("benchmarking zstd %s (using zlibWrapper)\n", ZSTD_VERSION_STRING);
638 1.1 christos for (l=cLevel; l <= cLevelLast; l++) {
639 1.1 christos BMK_benchMem(srcBuffer, benchedSize,
640 1.1 christos displayName, l,
641 1.1 christos fileSizes, nbFiles,
642 1.1 christos dictBuffer, dictBufferSize, BMK_ZWRAP_ZSTD_REUSE);
643 1.1 christos }
644 1.1 christos
645 1.1 christos DISPLAY("benchmarking zstd %s (zlibWrapper not reusing a context)\n", ZSTD_VERSION_STRING);
646 1.1 christos for (l=cLevel; l <= cLevelLast; l++) {
647 1.1 christos BMK_benchMem(srcBuffer, benchedSize,
648 1.1 christos displayName, l,
649 1.1 christos fileSizes, nbFiles,
650 1.1 christos dictBuffer, dictBufferSize, BMK_ZWRAP_ZSTD);
651 1.1 christos }
652 1.1 christos
653 1.1 christos
654 1.1 christos if (cLevelLast > Z_BEST_COMPRESSION) cLevelLast = Z_BEST_COMPRESSION;
655 1.1 christos
656 1.1 christos DISPLAY("\n");
657 1.1 christos DISPLAY("benchmarking zlib %s\n", ZLIB_VERSION);
658 1.1 christos for (l=cLevel; l <= cLevelLast; l++) {
659 1.1 christos BMK_benchMem(srcBuffer, benchedSize,
660 1.1 christos displayName, l,
661 1.1 christos fileSizes, nbFiles,
662 1.1 christos dictBuffer, dictBufferSize, BMK_ZLIB_REUSE);
663 1.1 christos }
664 1.1 christos
665 1.1 christos DISPLAY("benchmarking zlib %s (zlib not reusing a context)\n", ZLIB_VERSION);
666 1.1 christos for (l=cLevel; l <= cLevelLast; l++) {
667 1.1 christos BMK_benchMem(srcBuffer, benchedSize,
668 1.1 christos displayName, l,
669 1.1 christos fileSizes, nbFiles,
670 1.1 christos dictBuffer, dictBufferSize, BMK_ZLIB);
671 1.1 christos }
672 1.1 christos
673 1.1 christos DISPLAY("benchmarking zlib %s (using zlibWrapper)\n", ZLIB_VERSION);
674 1.1 christos for (l=cLevel; l <= cLevelLast; l++) {
675 1.1 christos BMK_benchMem(srcBuffer, benchedSize,
676 1.1 christos displayName, l,
677 1.1 christos fileSizes, nbFiles,
678 1.1 christos dictBuffer, dictBufferSize, BMK_ZWRAP_ZLIB_REUSE);
679 1.1 christos }
680 1.1 christos
681 1.1 christos DISPLAY("benchmarking zlib %s (zlibWrapper not reusing a context)\n", ZLIB_VERSION);
682 1.1 christos for (l=cLevel; l <= cLevelLast; l++) {
683 1.1 christos BMK_benchMem(srcBuffer, benchedSize,
684 1.1 christos displayName, l,
685 1.1 christos fileSizes, nbFiles,
686 1.1 christos dictBuffer, dictBufferSize, BMK_ZWRAP_ZLIB);
687 1.1 christos }
688 1.1 christos }
689 1.1 christos
690 1.1 christos
691 1.1 christos /*! BMK_loadFiles() :
692 1.1 christos Loads `buffer` with content of files listed within `fileNamesTable`.
693 1.1 christos At most, fills `buffer` entirely */
694 1.1 christos static void BMK_loadFiles(void* buffer, size_t bufferSize,
695 1.1 christos size_t* fileSizes,
696 1.1 christos const char** fileNamesTable, unsigned nbFiles)
697 1.1 christos {
698 1.1 christos size_t pos = 0, totalSize = 0;
699 1.1 christos unsigned n;
700 1.1 christos for (n=0; n<nbFiles; n++) {
701 1.1 christos FILE* f;
702 1.1 christos U64 fileSize = UTIL_getFileSize(fileNamesTable[n]);
703 1.1 christos if (UTIL_isDirectory(fileNamesTable[n])) {
704 1.1 christos DISPLAYLEVEL(2, "Ignoring %s directory... \n", fileNamesTable[n]);
705 1.1 christos fileSizes[n] = 0;
706 1.1 christos continue;
707 1.1 christos }
708 1.1 christos if (fileSize == UTIL_FILESIZE_UNKNOWN) {
709 1.1 christos DISPLAYLEVEL(2, "Cannot determine size of %s ... \n", fileNamesTable[n]);
710 1.1 christos fileSizes[n] = 0;
711 1.1 christos continue;
712 1.1 christos }
713 1.1 christos f = fopen(fileNamesTable[n], "rb");
714 1.1 christos if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]);
715 1.1 christos DISPLAYUPDATE(2, "Loading %s... \r", fileNamesTable[n]);
716 1.1 christos if (fileSize > bufferSize-pos) fileSize = bufferSize-pos, nbFiles=n; /* buffer too small - stop after this file */
717 1.1 christos { size_t const readSize = fread(((char*)buffer)+pos, 1, (size_t)fileSize, f);
718 1.1 christos if (readSize != (size_t)fileSize) EXM_THROW(11, "could not read %s", fileNamesTable[n]);
719 1.1 christos pos += readSize; }
720 1.1 christos fileSizes[n] = (size_t)fileSize;
721 1.1 christos totalSize += (size_t)fileSize;
722 1.1 christos fclose(f);
723 1.1 christos }
724 1.1 christos
725 1.1 christos if (totalSize == 0) EXM_THROW(12, "no data to bench");
726 1.1 christos }
727 1.1 christos
728 1.1 christos static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles,
729 1.1 christos const char* dictFileName, int cLevel, int cLevelLast)
730 1.1 christos {
731 1.1 christos void* srcBuffer;
732 1.1 christos size_t benchedSize;
733 1.1 christos void* dictBuffer = NULL;
734 1.1 christos size_t dictBufferSize = 0;
735 1.1 christos size_t* fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t));
736 1.1 christos U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles);
737 1.1 christos char mfName[20] = {0};
738 1.1 christos
739 1.1 christos if (!fileSizes) EXM_THROW(12, "not enough memory for fileSizes");
740 1.1 christos
741 1.1 christos /* Load dictionary */
742 1.1 christos if (dictFileName != NULL) {
743 1.1 christos U64 const dictFileSize = UTIL_getFileSize(dictFileName);
744 1.1 christos if (dictFileSize > 64 MB)
745 1.1 christos EXM_THROW(10, "dictionary file %s too large", dictFileName);
746 1.1 christos dictBufferSize = (size_t)dictFileSize;
747 1.1 christos dictBuffer = malloc(dictBufferSize);
748 1.1 christos if (dictBuffer==NULL)
749 1.1 christos EXM_THROW(11, "not enough memory for dictionary (%u bytes)", (unsigned)dictBufferSize);
750 1.1 christos BMK_loadFiles(dictBuffer, dictBufferSize, fileSizes, &dictFileName, 1);
751 1.1 christos }
752 1.1 christos
753 1.1 christos /* Memory allocation & restrictions */
754 1.1 christos benchedSize = BMK_findMaxMem(totalSizeToLoad * 3) / 3;
755 1.1 christos if ((U64)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad;
756 1.1 christos if (benchedSize < totalSizeToLoad)
757 1.1 christos DISPLAY("Not enough memory; testing %u MB only...\n", (unsigned)(benchedSize >> 20));
758 1.1 christos srcBuffer = malloc(benchedSize + !benchedSize);
759 1.1 christos if (!srcBuffer) EXM_THROW(12, "not enough memory");
760 1.1 christos
761 1.1 christos /* Load input buffer */
762 1.1 christos BMK_loadFiles(srcBuffer, benchedSize, fileSizes, fileNamesTable, nbFiles);
763 1.1 christos
764 1.1 christos /* Bench */
765 1.1 christos snprintf (mfName, sizeof(mfName), " %u files", nbFiles);
766 1.1 christos { const char* displayName = (nbFiles > 1) ? mfName : fileNamesTable[0];
767 1.1 christos BMK_benchCLevel(srcBuffer, benchedSize,
768 1.1 christos displayName, cLevel, cLevelLast,
769 1.1 christos fileSizes, nbFiles,
770 1.1 christos dictBuffer, dictBufferSize);
771 1.1 christos }
772 1.1 christos
773 1.1 christos /* clean up */
774 1.1 christos free(srcBuffer);
775 1.1 christos free(dictBuffer);
776 1.1 christos free(fileSizes);
777 1.1 christos }
778 1.1 christos
779 1.1 christos
780 1.1 christos static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility)
781 1.1 christos {
782 1.1 christos char name[20] = {0};
783 1.1 christos size_t benchedSize = 10000000;
784 1.1 christos void* const srcBuffer = malloc(benchedSize);
785 1.1 christos
786 1.1 christos /* Memory allocation */
787 1.1 christos if (!srcBuffer) EXM_THROW(21, "not enough memory");
788 1.1 christos
789 1.1 christos /* Fill input buffer */
790 1.1 christos RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0);
791 1.1 christos
792 1.1 christos /* Bench */
793 1.1 christos snprintf (name, sizeof(name), "Synthetic %2u%%", (unsigned)(compressibility*100));
794 1.1 christos BMK_benchCLevel(srcBuffer, benchedSize, name, cLevel, cLevelLast, &benchedSize, 1, NULL, 0);
795 1.1 christos
796 1.1 christos /* clean up */
797 1.1 christos free(srcBuffer);
798 1.1 christos }
799 1.1 christos
800 1.1 christos
801 1.1 christos static int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles,
802 1.1 christos const char* dictFileName, int cLevel, int cLevelLast)
803 1.1 christos {
804 1.1 christos double const compressibility = (double)g_compressibilityDefault / 100;
805 1.1 christos
806 1.1 christos if (nbFiles == 0)
807 1.1 christos BMK_syntheticTest(cLevel, cLevelLast, compressibility);
808 1.1 christos else
809 1.1 christos BMK_benchFileTable(fileNamesTable, nbFiles, dictFileName, cLevel, cLevelLast);
810 1.1 christos return 0;
811 1.1 christos }
812 1.1 christos
813 1.1 christos
814 1.1 christos
815 1.1 christos
816 1.1 christos /*-************************************
817 1.1 christos * Command Line
818 1.1 christos **************************************/
819 1.1 christos static int usage(const char* programName)
820 1.1 christos {
821 1.1 christos DISPLAY(WELCOME_MESSAGE);
822 1.1 christos DISPLAY( "Usage :\n");
823 1.1 christos DISPLAY( " %s [args] [FILE(s)] [-o file]\n", programName);
824 1.1 christos DISPLAY( "\n");
825 1.1 christos DISPLAY( "FILE : a filename\n");
826 1.1 christos DISPLAY( " with no FILE, or when FILE is - , read standard input\n");
827 1.1 christos DISPLAY( "Arguments :\n");
828 1.1 christos DISPLAY( " -D file: use `file` as Dictionary \n");
829 1.1 christos DISPLAY( " -h/-H : display help/long help and exit\n");
830 1.1 christos DISPLAY( " -V : display Version number and exit\n");
831 1.1 christos DISPLAY( " -v : verbose mode; specify multiple times to increase log level (default:%d)\n", DEFAULT_DISPLAY_LEVEL);
832 1.1 christos DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n");
833 1.1 christos #ifdef UTIL_HAS_CREATEFILELIST
834 1.1 christos DISPLAY( " -r : operate recursively on directories\n");
835 1.1 christos #endif
836 1.1 christos DISPLAY( "\n");
837 1.1 christos DISPLAY( "Benchmark arguments :\n");
838 1.1 christos DISPLAY( " -b# : benchmark file(s), using # compression level (default : %d) \n", ZSTDCLI_CLEVEL_DEFAULT);
839 1.1 christos DISPLAY( " -e# : test all compression levels from -bX to # (default: %d)\n", ZSTDCLI_CLEVEL_DEFAULT);
840 1.1 christos DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s)\n");
841 1.1 christos DISPLAY( " -B# : cut file into independent chunks of size # (default: no chunking)\n");
842 1.1 christos return 0;
843 1.1 christos }
844 1.1 christos
845 1.1 christos static int badusage(const char* programName)
846 1.1 christos {
847 1.1 christos DISPLAYLEVEL(1, "Incorrect parameters\n");
848 1.1 christos if (g_displayLevel >= 1) usage(programName);
849 1.1 christos return 1;
850 1.1 christos }
851 1.1 christos
852 1.1 christos static void waitEnter(void)
853 1.1 christos {
854 1.1 christos int unused;
855 1.1 christos DISPLAY("Press enter to continue...\n");
856 1.1 christos unused = getchar();
857 1.1 christos (void)unused;
858 1.1 christos }
859 1.1 christos
860 1.1 christos /*! readU32FromChar() :
861 1.1 christos @return : unsigned integer value reach from input in `char` format
862 1.1 christos Will also modify `*stringPtr`, advancing it to position where it stopped reading.
863 1.1 christos Note : this function can overflow if digit string > MAX_UINT */
864 1.1 christos static unsigned readU32FromChar(const char** stringPtr)
865 1.1 christos {
866 1.1 christos unsigned result = 0;
867 1.1 christos while ((**stringPtr >='0') && (**stringPtr <='9'))
868 1.1 christos result *= 10, result += (unsigned)(**stringPtr - '0'), (*stringPtr)++ ;
869 1.1 christos return result;
870 1.1 christos }
871 1.1 christos
872 1.1 christos
873 1.1 christos #define CLEAN_RETURN(i) { operationResult = (i); goto _end; }
874 1.1 christos
875 1.1 christos int main(int argCount, char** argv)
876 1.1 christos {
877 1.1 christos int argNb,
878 1.1 christos main_pause=0,
879 1.1 christos nextEntryIsDictionary=0,
880 1.1 christos operationResult=0,
881 1.1 christos nextArgumentIsFile=0;
882 1.1 christos int cLevel = ZSTDCLI_CLEVEL_DEFAULT;
883 1.1 christos int cLevelLast = 1;
884 1.1 christos unsigned recursive = 0;
885 1.1 christos FileNamesTable* filenames = UTIL_allocateFileNamesTable((size_t)argCount);
886 1.1 christos const char* programName = argv[0];
887 1.1 christos const char* dictFileName = NULL;
888 1.1 christos char* dynNameSpace = NULL;
889 1.1 christos
890 1.1 christos /* init */
891 1.1 christos if (filenames==NULL) { DISPLAY("zstd: %s \n", strerror(errno)); exit(1); }
892 1.1 christos displayOut = stderr;
893 1.1 christos
894 1.1 christos /* Pick out program name from path. Don't rely on stdlib because of conflicting behavior */
895 1.1 christos { size_t pos;
896 1.1 christos for (pos = strlen(programName); pos > 0; pos--) { if (programName[pos] == '/') { pos++; break; } }
897 1.1 christos programName += pos;
898 1.1 christos }
899 1.1 christos
900 1.1 christos /* command switches */
901 1.1 christos for(argNb=1; argNb<argCount; argNb++) {
902 1.1 christos const char* argument = argv[argNb];
903 1.1 christos if(!argument) continue; /* Protection if argument empty */
904 1.1 christos
905 1.1 christos if (nextArgumentIsFile==0) {
906 1.1 christos
907 1.1 christos /* long commands (--long-word) */
908 1.1 christos if (!strcmp(argument, "--")) { nextArgumentIsFile=1; continue; }
909 1.1 christos if (!strcmp(argument, "--version")) { displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); }
910 1.1 christos if (!strcmp(argument, "--help")) { displayOut=stdout; CLEAN_RETURN(usage(programName)); }
911 1.1 christos if (!strcmp(argument, "--verbose")) { g_displayLevel++; continue; }
912 1.1 christos if (!strcmp(argument, "--quiet")) { g_displayLevel--; continue; }
913 1.1 christos
914 1.1 christos /* Decode commands (note : aggregated commands are allowed) */
915 1.1 christos if (argument[0]=='-') {
916 1.1 christos argument++;
917 1.1 christos
918 1.1 christos while (argument[0]!=0) {
919 1.1 christos switch(argument[0])
920 1.1 christos {
921 1.1 christos /* Display help */
922 1.1 christos case 'V': displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); /* Version Only */
923 1.1 christos case 'H':
924 1.1 christos case 'h': displayOut=stdout; CLEAN_RETURN(usage(programName));
925 1.1 christos
926 1.1 christos /* Use file content as dictionary */
927 1.1 christos case 'D': nextEntryIsDictionary = 1; argument++; break;
928 1.1 christos
929 1.1 christos /* Verbose mode */
930 1.1 christos case 'v': g_displayLevel++; argument++; break;
931 1.1 christos
932 1.1 christos /* Quiet mode */
933 1.1 christos case 'q': g_displayLevel--; argument++; break;
934 1.1 christos
935 1.1 christos #ifdef UTIL_HAS_CREATEFILELIST
936 1.1 christos /* recursive */
937 1.1 christos case 'r': recursive=1; argument++; break;
938 1.1 christos #endif
939 1.1 christos
940 1.1 christos /* Benchmark */
941 1.1 christos case 'b':
942 1.1 christos /* first compression Level */
943 1.1 christos argument++;
944 1.1 christos cLevel = (int)readU32FromChar(&argument);
945 1.1 christos break;
946 1.1 christos
947 1.1 christos /* range bench (benchmark only) */
948 1.1 christos case 'e':
949 1.1 christos /* last compression Level */
950 1.1 christos argument++;
951 1.1 christos cLevelLast = (int)readU32FromChar(&argument);
952 1.1 christos break;
953 1.1 christos
954 1.1 christos /* Modify Nb Iterations (benchmark only) */
955 1.1 christos case 'i':
956 1.1 christos argument++;
957 1.1 christos { U32 const iters = readU32FromChar(&argument);
958 1.1 christos BMK_setNotificationLevel(g_displayLevel);
959 1.1 christos BMK_SetNbIterations(iters);
960 1.1 christos }
961 1.1 christos break;
962 1.1 christos
963 1.1 christos /* cut input into blocks (benchmark only) */
964 1.1 christos case 'B':
965 1.1 christos argument++;
966 1.1 christos { size_t bSize = readU32FromChar(&argument);
967 1.1 christos if (toupper(*argument)=='K') bSize<<=10, argument++; /* allows using KB notation */
968 1.1 christos if (toupper(*argument)=='M') bSize<<=20, argument++;
969 1.1 christos if (toupper(*argument)=='B') argument++;
970 1.1 christos BMK_setNotificationLevel(g_displayLevel);
971 1.1 christos BMK_SetBlockSize(bSize);
972 1.1 christos }
973 1.1 christos break;
974 1.1 christos
975 1.1 christos /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */
976 1.1 christos case 'p': argument++;
977 1.1 christos if ((*argument>='0') && (*argument<='9')) {
978 1.1 christos BMK_setAdditionalParam((int)readU32FromChar(&argument));
979 1.1 christos } else
980 1.1 christos main_pause=1;
981 1.1 christos break;
982 1.1 christos /* unknown command */
983 1.1 christos default : CLEAN_RETURN(badusage(programName));
984 1.1 christos }
985 1.1 christos }
986 1.1 christos continue;
987 1.1 christos } /* if (argument[0]=='-') */
988 1.1 christos
989 1.1 christos } /* if (nextArgumentIsAFile==0) */
990 1.1 christos
991 1.1 christos if (nextEntryIsDictionary) {
992 1.1 christos nextEntryIsDictionary = 0;
993 1.1 christos dictFileName = argument;
994 1.1 christos continue;
995 1.1 christos }
996 1.1 christos
997 1.1 christos /* add filename to list */
998 1.1 christos UTIL_refFilename(filenames, argument);
999 1.1 christos }
1000 1.1 christos
1001 1.1 christos /* Welcome message (if verbose) */
1002 1.1 christos DISPLAYLEVEL(3, WELCOME_MESSAGE);
1003 1.1 christos
1004 1.1 christos #ifdef UTIL_HAS_CREATEFILELIST
1005 1.1 christos if (recursive) {
1006 1.1 christos UTIL_expandFNT(&filenames, 1);
1007 1.1 christos }
1008 1.1 christos #endif
1009 1.1 christos
1010 1.1 christos BMK_setNotificationLevel(g_displayLevel);
1011 1.1 christos BMK_benchFiles(filenames->fileNames, (unsigned)filenames->tableSize, dictFileName, cLevel, cLevelLast);
1012 1.1 christos
1013 1.1 christos _end:
1014 1.1 christos if (main_pause) waitEnter();
1015 1.1 christos free(dynNameSpace);
1016 1.1 christos UTIL_freeFileNamesTable(filenames);
1017 1.1 christos return operationResult;
1018 1.1 christos }
1019