]> git.proxmox.com Git - ceph.git/blob - ceph/src/zstd/tests/paramgrill.c
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / zstd / tests / paramgrill.c
1 /**
2 * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
8 */
9
10
11 /*-************************************
12 * Dependencies
13 **************************************/
14 #include "util.h" /* Compiler options, UTIL_GetFileSize */
15 #include <stdlib.h> /* malloc */
16 #include <stdio.h> /* fprintf, fopen, ftello64 */
17 #include <string.h> /* strcmp */
18 #include <math.h> /* log */
19 #include <time.h> /* clock_t */
20
21 #include "mem.h"
22 #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_estimateCCtxSize */
23 #include "zstd.h"
24 #include "datagen.h"
25 #include "xxhash.h"
26
27
28 /*-************************************
29 * Constants
30 **************************************/
31 #define PROGRAM_DESCRIPTION "ZSTD parameters tester"
32 #define AUTHOR "Yann Collet"
33 #define WELCOME_MESSAGE "*** %s %s %i-bits, by %s (%s) ***\n", PROGRAM_DESCRIPTION, ZSTD_VERSION_STRING, (int)(sizeof(void*)*8), AUTHOR, __DATE__
34
35
36 #define KB *(1<<10)
37 #define MB *(1<<20)
38 #define GB *(1ULL<<30)
39
40 #define NBLOOPS 2
41 #define TIMELOOP (2 * CLOCKS_PER_SEC)
42
43 #define NB_LEVELS_TRACKED 30
44
45 static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31));
46
47 #define COMPRESSIBILITY_DEFAULT 0.50
48 static const size_t sampleSize = 10000000;
49
50 static const U32 g_grillDuration_s = 60000; /* about 16 hours */
51 static const clock_t g_maxParamTime = 15 * CLOCKS_PER_SEC;
52 static const clock_t g_maxVariationTime = 60 * CLOCKS_PER_SEC;
53 static const int g_maxNbVariations = 64;
54
55
56 /*-************************************
57 * Macros
58 **************************************/
59 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
60
61
62 /*-************************************
63 * Benchmark Parameters
64 **************************************/
65 static U32 g_nbIterations = NBLOOPS;
66 static double g_compressibility = COMPRESSIBILITY_DEFAULT;
67 static U32 g_blockSize = 0;
68 static U32 g_rand = 1;
69 static U32 g_singleRun = 0;
70 static U32 g_target = 0;
71 static U32 g_noSeed = 0;
72 static ZSTD_compressionParameters g_params = { 0, 0, 0, 0, 0, 0, ZSTD_greedy };
73
74 void BMK_SetNbIterations(int nbLoops)
75 {
76 g_nbIterations = nbLoops;
77 DISPLAY("- %u iterations -\n", g_nbIterations);
78 }
79
80
81 /*-*******************************************************
82 * Private functions
83 *********************************************************/
84
85 static clock_t BMK_clockSpan(clock_t cStart) { return clock() - cStart; } /* works even if overflow ; max span ~ 30 mn */
86
87 static U32 BMK_timeSpan(time_t tStart) { return (U32)difftime(time(NULL), tStart); } /* accuracy in seconds only, span can be multiple years */
88
89
90 static size_t BMK_findMaxMem(U64 requiredMem)
91 {
92 size_t const step = 64 MB;
93 void* testmem = NULL;
94
95 requiredMem = (((requiredMem >> 26) + 1) << 26);
96 if (requiredMem > maxMemory) requiredMem = maxMemory;
97
98 requiredMem += 2*step;
99 while (!testmem) {
100 requiredMem -= step;
101 testmem = malloc ((size_t)requiredMem);
102 }
103
104 free (testmem);
105 return (size_t) (requiredMem - step);
106 }
107
108
109 # define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r)))
110 U32 FUZ_rand(U32* src)
111 {
112 const U32 prime1 = 2654435761U;
113 const U32 prime2 = 2246822519U;
114 U32 rand32 = *src;
115 rand32 *= prime1;
116 rand32 += prime2;
117 rand32 = FUZ_rotl32(rand32, 13);
118 *src = rand32;
119 return rand32 >> 5;
120 }
121
122
123 /*-*******************************************************
124 * Bench functions
125 *********************************************************/
126 typedef struct {
127 size_t cSize;
128 double cSpeed;
129 double dSpeed;
130 } BMK_result_t;
131
132 typedef struct
133 {
134 const char* srcPtr;
135 size_t srcSize;
136 char* cPtr;
137 size_t cRoom;
138 size_t cSize;
139 char* resPtr;
140 size_t resSize;
141 } blockParam_t;
142
143
144 #define MIN(a,b) ( (a) < (b) ? (a) : (b) )
145
146 static size_t BMK_benchParam(BMK_result_t* resultPtr,
147 const void* srcBuffer, size_t srcSize,
148 ZSTD_CCtx* ctx,
149 const ZSTD_compressionParameters cParams)
150 {
151 const size_t blockSize = g_blockSize ? g_blockSize : srcSize;
152 const U32 nbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize);
153 blockParam_t* const blockTable = (blockParam_t*) malloc(nbBlocks * sizeof(blockParam_t));
154 const size_t maxCompressedSize = (size_t)nbBlocks * ZSTD_compressBound(blockSize);
155 void* const compressedBuffer = malloc(maxCompressedSize);
156 void* const resultBuffer = malloc(srcSize);
157 ZSTD_parameters params;
158 U32 Wlog = cParams.windowLog;
159 U32 Clog = cParams.chainLog;
160 U32 Hlog = cParams.hashLog;
161 U32 Slog = cParams.searchLog;
162 U32 Slength = cParams.searchLength;
163 U32 Tlength = cParams.targetLength;
164 ZSTD_strategy strat = cParams.strategy;
165 char name[30] = { 0 };
166 U64 crcOrig;
167
168 /* Memory allocation & restrictions */
169 snprintf(name, 30, "Sw%02uc%02uh%02us%02ul%1ut%03uS%1u", Wlog, Clog, Hlog, Slog, Slength, Tlength, strat);
170 if (!compressedBuffer || !resultBuffer || !blockTable) {
171 DISPLAY("\nError: not enough memory!\n");
172 free(compressedBuffer);
173 free(resultBuffer);
174 free(blockTable);
175 return 12;
176 }
177
178 /* Calculating input Checksum */
179 crcOrig = XXH64(srcBuffer, srcSize, 0);
180
181 /* Init blockTable data */
182 {
183 U32 i;
184 size_t remaining = srcSize;
185 const char* srcPtr = (const char*)srcBuffer;
186 char* cPtr = (char*)compressedBuffer;
187 char* resPtr = (char*)resultBuffer;
188 for (i=0; i<nbBlocks; i++) {
189 size_t thisBlockSize = MIN(remaining, blockSize);
190 blockTable[i].srcPtr = srcPtr;
191 blockTable[i].cPtr = cPtr;
192 blockTable[i].resPtr = resPtr;
193 blockTable[i].srcSize = thisBlockSize;
194 blockTable[i].cRoom = ZSTD_compressBound(thisBlockSize);
195 srcPtr += thisBlockSize;
196 cPtr += blockTable[i].cRoom;
197 resPtr += thisBlockSize;
198 remaining -= thisBlockSize;
199 } }
200
201 /* warmimg up memory */
202 RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.10, 1);
203
204 /* Bench */
205 { U32 loopNb;
206 size_t cSize = 0;
207 double fastestC = 100000000., fastestD = 100000000.;
208 double ratio = 0.;
209 U64 crcCheck = 0;
210 clock_t const benchStart = clock();
211
212 DISPLAY("\r%79s\r", "");
213 memset(&params, 0, sizeof(params));
214 params.cParams = cParams;
215 for (loopNb = 1; loopNb <= g_nbIterations; loopNb++) {
216 int nbLoops;
217 U32 blockNb;
218 clock_t roundStart, roundClock;
219
220 { clock_t const benchTime = BMK_clockSpan(benchStart);
221 if (benchTime > g_maxParamTime) break; }
222
223 /* Compression */
224 DISPLAY("\r%1u-%s : %9u ->", loopNb, name, (U32)srcSize);
225 memset(compressedBuffer, 0xE5, maxCompressedSize);
226
227 nbLoops = 0;
228 roundStart = clock();
229 while (clock() == roundStart);
230 roundStart = clock();
231 while (BMK_clockSpan(roundStart) < TIMELOOP) {
232 for (blockNb=0; blockNb<nbBlocks; blockNb++)
233 blockTable[blockNb].cSize = ZSTD_compress_advanced(ctx,
234 blockTable[blockNb].cPtr, blockTable[blockNb].cRoom,
235 blockTable[blockNb].srcPtr, blockTable[blockNb].srcSize,
236 NULL, 0,
237 params);
238 nbLoops++;
239 }
240 roundClock = BMK_clockSpan(roundStart);
241
242 cSize = 0;
243 for (blockNb=0; blockNb<nbBlocks; blockNb++)
244 cSize += blockTable[blockNb].cSize;
245 if ((double)roundClock < fastestC * CLOCKS_PER_SEC * nbLoops) fastestC = ((double)roundClock / CLOCKS_PER_SEC) / nbLoops;
246 ratio = (double)srcSize / (double)cSize;
247 DISPLAY("\r");
248 DISPLAY("%1u-%s : %9u ->", loopNb, name, (U32)srcSize);
249 DISPLAY(" %9u (%4.3f),%7.1f MB/s", (U32)cSize, ratio, (double)srcSize / fastestC / 1000000.);
250 resultPtr->cSize = cSize;
251 resultPtr->cSpeed = (double)srcSize / fastestC;
252
253 #if 1
254 /* Decompression */
255 memset(resultBuffer, 0xD6, srcSize);
256
257 nbLoops = 0;
258 roundStart = clock();
259 while (clock() == roundStart);
260 roundStart = clock();
261 for ( ; BMK_clockSpan(roundStart) < TIMELOOP; nbLoops++) {
262 for (blockNb=0; blockNb<nbBlocks; blockNb++)
263 blockTable[blockNb].resSize = ZSTD_decompress(blockTable[blockNb].resPtr, blockTable[blockNb].srcSize,
264 blockTable[blockNb].cPtr, blockTable[blockNb].cSize);
265 }
266 roundClock = BMK_clockSpan(roundStart);
267
268 if ((double)roundClock < fastestD * CLOCKS_PER_SEC * nbLoops) fastestD = ((double)roundClock / CLOCKS_PER_SEC) / nbLoops;
269 DISPLAY("\r");
270 DISPLAY("%1u-%s : %9u -> ", loopNb, name, (U32)srcSize);
271 DISPLAY("%9u (%4.3f),%7.1f MB/s, ", (U32)cSize, ratio, (double)srcSize / fastestC / 1000000.);
272 DISPLAY("%7.1f MB/s", (double)srcSize / fastestD / 1000000.);
273 resultPtr->dSpeed = (double)srcSize / fastestD;
274
275 /* CRC Checking */
276 crcCheck = XXH64(resultBuffer, srcSize, 0);
277 if (crcOrig!=crcCheck) {
278 unsigned u;
279 unsigned eBlockSize = (unsigned)(MIN(65536*2, blockSize));
280 DISPLAY("\n!!! WARNING !!! Invalid Checksum : %x != %x\n", (unsigned)crcOrig, (unsigned)crcCheck);
281 for (u=0; u<srcSize; u++) {
282 if (((const BYTE*)srcBuffer)[u] != ((BYTE*)resultBuffer)[u]) {
283 printf("Decoding error at pos %u (block %u, pos %u) \n", u, u / eBlockSize, u % eBlockSize);
284 break;
285 } }
286 break;
287 }
288 #endif
289 } }
290
291 /* End cleaning */
292 DISPLAY("\r");
293 free(compressedBuffer);
294 free(resultBuffer);
295 return 0;
296 }
297
298
299 const char* g_stratName[] = { "ZSTD_fast ",
300 "ZSTD_dfast ",
301 "ZSTD_greedy ",
302 "ZSTD_lazy ",
303 "ZSTD_lazy2 ",
304 "ZSTD_btlazy2",
305 "ZSTD_btopt ",
306 "ZSTD_btopt2 "};
307
308 static void BMK_printWinner(FILE* f, U32 cLevel, BMK_result_t result, ZSTD_compressionParameters params, size_t srcSize)
309 {
310 DISPLAY("\r%79s\r", "");
311 fprintf(f," {%3u,%3u,%3u,%3u,%3u,%3u, %s }, ",
312 params.windowLog, params.chainLog, params.hashLog, params.searchLog, params.searchLength,
313 params.targetLength, g_stratName[(U32)(params.strategy)]);
314 fprintf(f,
315 "/* level %2u */ /* R:%5.3f at %5.1f MB/s - %5.1f MB/s */\n",
316 cLevel, (double)srcSize / result.cSize, result.cSpeed / 1000000., result.dSpeed / 1000000.);
317 }
318
319
320 static double g_cSpeedTarget[NB_LEVELS_TRACKED] = { 0. }; /* NB_LEVELS_TRACKED : checked at main() */
321
322 typedef struct {
323 BMK_result_t result;
324 ZSTD_compressionParameters params;
325 } winnerInfo_t;
326
327 static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, size_t srcSize)
328 {
329 int cLevel;
330
331 fprintf(f, "\n /* Proposed configurations : */ \n");
332 fprintf(f, " /* W, C, H, S, L, T, strat */ \n");
333
334 for (cLevel=0; cLevel <= ZSTD_maxCLevel(); cLevel++)
335 BMK_printWinner(f, cLevel, winners[cLevel].result, winners[cLevel].params, srcSize);
336 }
337
338
339 static void BMK_printWinners(FILE* f, const winnerInfo_t* winners, size_t srcSize)
340 {
341 fseek(f, 0, SEEK_SET);
342 BMK_printWinners2(f, winners, srcSize);
343 fflush(f);
344 BMK_printWinners2(stdout, winners, srcSize);
345 }
346
347 static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters params,
348 const void* srcBuffer, size_t srcSize,
349 ZSTD_CCtx* ctx)
350 {
351 BMK_result_t testResult;
352 int better = 0;
353 int cLevel;
354
355 BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, params);
356
357 for (cLevel = 1; cLevel <= ZSTD_maxCLevel(); cLevel++) {
358 if (testResult.cSpeed < g_cSpeedTarget[cLevel])
359 continue; /* not fast enough for this level */
360 if (winners[cLevel].result.cSize==0) {
361 /* first solution for this cLevel */
362 winners[cLevel].result = testResult;
363 winners[cLevel].params = params;
364 BMK_printWinner(stdout, cLevel, testResult, params, srcSize);
365 better = 1;
366 continue;
367 }
368
369 if ((double)testResult.cSize <= ((double)winners[cLevel].result.cSize * (1. + (0.02 / cLevel))) ) {
370 /* Validate solution is "good enough" */
371 double W_ratio = (double)srcSize / testResult.cSize;
372 double O_ratio = (double)srcSize / winners[cLevel].result.cSize;
373 double W_ratioNote = log (W_ratio);
374 double O_ratioNote = log (O_ratio);
375 size_t W_DMemUsed = (1 << params.windowLog) + (16 KB);
376 size_t O_DMemUsed = (1 << winners[cLevel].params.windowLog) + (16 KB);
377 double W_DMemUsed_note = W_ratioNote * ( 40 + 9*cLevel) - log((double)W_DMemUsed);
378 double O_DMemUsed_note = O_ratioNote * ( 40 + 9*cLevel) - log((double)O_DMemUsed);
379
380 size_t W_CMemUsed = (1 << params.windowLog) + ZSTD_estimateCCtxSize(params);
381 size_t O_CMemUsed = (1 << winners[cLevel].params.windowLog) + ZSTD_estimateCCtxSize(winners[cLevel].params);
382 double W_CMemUsed_note = W_ratioNote * ( 50 + 13*cLevel) - log((double)W_CMemUsed);
383 double O_CMemUsed_note = O_ratioNote * ( 50 + 13*cLevel) - log((double)O_CMemUsed);
384
385 double W_CSpeed_note = W_ratioNote * ( 30 + 10*cLevel) + log(testResult.cSpeed);
386 double O_CSpeed_note = O_ratioNote * ( 30 + 10*cLevel) + log(winners[cLevel].result.cSpeed);
387
388 double W_DSpeed_note = W_ratioNote * ( 20 + 2*cLevel) + log(testResult.dSpeed);
389 double O_DSpeed_note = O_ratioNote * ( 20 + 2*cLevel) + log(winners[cLevel].result.dSpeed);
390
391 if (W_DMemUsed_note < O_DMemUsed_note) {
392 /* uses too much Decompression memory for too little benefit */
393 if (W_ratio > O_ratio)
394 DISPLAY ("Decompression Memory : %5.3f @ %4.1f MB vs %5.3f @ %4.1f MB : not enough for level %i\n",
395 W_ratio, (double)(W_DMemUsed) / 1024 / 1024,
396 O_ratio, (double)(O_DMemUsed) / 1024 / 1024, cLevel);
397 continue;
398 }
399 if (W_CMemUsed_note < O_CMemUsed_note) {
400 /* uses too much memory for compression for too little benefit */
401 if (W_ratio > O_ratio)
402 DISPLAY ("Compression Memory : %5.3f @ %4.1f MB vs %5.3f @ %4.1f MB : not enough for level %i\n",
403 W_ratio, (double)(W_CMemUsed) / 1024 / 1024,
404 O_ratio, (double)(O_CMemUsed) / 1024 / 1024, cLevel);
405 continue;
406 }
407 if (W_CSpeed_note < O_CSpeed_note ) {
408 /* too large compression speed difference for the compression benefit */
409 if (W_ratio > O_ratio)
410 DISPLAY ("Compression Speed : %5.3f @ %4.1f MB/s vs %5.3f @ %4.1f MB/s : not enough for level %i\n",
411 W_ratio, testResult.cSpeed / 1000000,
412 O_ratio, winners[cLevel].result.cSpeed / 1000000., cLevel);
413 continue;
414 }
415 if (W_DSpeed_note < O_DSpeed_note ) {
416 /* too large decompression speed difference for the compression benefit */
417 if (W_ratio > O_ratio)
418 DISPLAY ("Decompression Speed : %5.3f @ %4.1f MB/s vs %5.3f @ %4.1f MB/s : not enough for level %i\n",
419 W_ratio, testResult.dSpeed / 1000000.,
420 O_ratio, winners[cLevel].result.dSpeed / 1000000., cLevel);
421 continue;
422 }
423
424 if (W_ratio < O_ratio)
425 DISPLAY("Solution %4.3f selected over %4.3f at level %i, due to better secondary statistics \n", W_ratio, O_ratio, cLevel);
426
427 winners[cLevel].result = testResult;
428 winners[cLevel].params = params;
429 BMK_printWinner(stdout, cLevel, testResult, params, srcSize);
430
431 better = 1;
432 } }
433
434 return better;
435 }
436
437
438 /* nullified useless params, to ensure count stats */
439 static ZSTD_compressionParameters* sanitizeParams(ZSTD_compressionParameters params)
440 {
441 g_params = params;
442 if (params.strategy == ZSTD_fast)
443 g_params.chainLog = 0, g_params.searchLog = 0;
444 if (params.strategy == ZSTD_dfast)
445 g_params.searchLog = 0;
446 if (params.strategy != ZSTD_btopt && params.strategy != ZSTD_btopt2)
447 g_params.targetLength = 0;
448 return &g_params;
449 }
450
451
452 static void paramVariation(ZSTD_compressionParameters* ptr)
453 {
454 ZSTD_compressionParameters p;
455 U32 validated = 0;
456 while (!validated) {
457 U32 nbChanges = (FUZ_rand(&g_rand) & 3) + 1;
458 p = *ptr;
459 for ( ; nbChanges ; nbChanges--) {
460 const U32 changeID = FUZ_rand(&g_rand) % 14;
461 switch(changeID)
462 {
463 case 0:
464 p.chainLog++; break;
465 case 1:
466 p.chainLog--; break;
467 case 2:
468 p.hashLog++; break;
469 case 3:
470 p.hashLog--; break;
471 case 4:
472 p.searchLog++; break;
473 case 5:
474 p.searchLog--; break;
475 case 6:
476 p.windowLog++; break;
477 case 7:
478 p.windowLog--; break;
479 case 8:
480 p.searchLength++; break;
481 case 9:
482 p.searchLength--; break;
483 case 10:
484 p.strategy = (ZSTD_strategy)(((U32)p.strategy)+1); break;
485 case 11:
486 p.strategy = (ZSTD_strategy)(((U32)p.strategy)-1); break;
487 case 12:
488 p.targetLength *= 1 + ((double)(FUZ_rand(&g_rand)&255)) / 256.; break;
489 case 13:
490 p.targetLength /= 1 + ((double)(FUZ_rand(&g_rand)&255)) / 256.; break;
491 }
492 }
493 validated = !ZSTD_isError(ZSTD_checkCParams(p));
494 }
495 *ptr = p;
496 }
497
498
499 #define PARAMTABLELOG 25
500 #define PARAMTABLESIZE (1<<PARAMTABLELOG)
501 #define PARAMTABLEMASK (PARAMTABLESIZE-1)
502 static BYTE g_alreadyTested[PARAMTABLESIZE] = {0}; /* init to zero */
503
504 #define NB_TESTS_PLAYED(p) \
505 g_alreadyTested[(XXH64(sanitizeParams(p), sizeof(p), 0) >> 3) & PARAMTABLEMASK]
506
507
508 #define MAX(a,b) ( (a) > (b) ? (a) : (b) )
509
510 static void playAround(FILE* f, winnerInfo_t* winners,
511 ZSTD_compressionParameters params,
512 const void* srcBuffer, size_t srcSize,
513 ZSTD_CCtx* ctx)
514 {
515 int nbVariations = 0;
516 clock_t const clockStart = clock();
517
518 while (BMK_clockSpan(clockStart) < g_maxVariationTime) {
519 ZSTD_compressionParameters p = params;
520
521 if (nbVariations++ > g_maxNbVariations) break;
522 paramVariation(&p);
523
524 /* exclude faster if already played params */
525 if (FUZ_rand(&g_rand) & ((1 << NB_TESTS_PLAYED(p))-1))
526 continue;
527
528 /* test */
529 NB_TESTS_PLAYED(p)++;
530 if (!BMK_seed(winners, p, srcBuffer, srcSize, ctx)) continue;
531
532 /* improvement found => search more */
533 BMK_printWinners(f, winners, srcSize);
534 playAround(f, winners, p, srcBuffer, srcSize, ctx);
535 }
536
537 }
538
539
540 static ZSTD_compressionParameters randomParams(void)
541 {
542 ZSTD_compressionParameters p;
543 U32 validated = 0;
544 while (!validated) {
545 /* totally random entry */
546 p.chainLog = FUZ_rand(&g_rand) % (ZSTD_CHAINLOG_MAX+1 - ZSTD_CHAINLOG_MIN) + ZSTD_CHAINLOG_MIN;
547 p.hashLog = FUZ_rand(&g_rand) % (ZSTD_HASHLOG_MAX+1 - ZSTD_HASHLOG_MIN) + ZSTD_HASHLOG_MIN;
548 p.searchLog = FUZ_rand(&g_rand) % (ZSTD_SEARCHLOG_MAX+1 - ZSTD_SEARCHLOG_MIN) + ZSTD_SEARCHLOG_MIN;
549 p.windowLog = FUZ_rand(&g_rand) % (ZSTD_WINDOWLOG_MAX+1 - ZSTD_WINDOWLOG_MIN) + ZSTD_WINDOWLOG_MIN;
550 p.searchLength=FUZ_rand(&g_rand) % (ZSTD_SEARCHLENGTH_MAX+1 - ZSTD_SEARCHLENGTH_MIN) + ZSTD_SEARCHLENGTH_MIN;
551 p.targetLength=FUZ_rand(&g_rand) % (ZSTD_TARGETLENGTH_MAX+1 - ZSTD_TARGETLENGTH_MIN) + ZSTD_TARGETLENGTH_MIN;
552 p.strategy = (ZSTD_strategy) (FUZ_rand(&g_rand) % (ZSTD_btopt2 +1));
553 validated = !ZSTD_isError(ZSTD_checkCParams(p));
554 }
555 return p;
556 }
557
558 static void BMK_selectRandomStart(
559 FILE* f, winnerInfo_t* winners,
560 const void* srcBuffer, size_t srcSize,
561 ZSTD_CCtx* ctx)
562 {
563 U32 const id = (FUZ_rand(&g_rand) % (ZSTD_maxCLevel()+1));
564 if ((id==0) || (winners[id].params.windowLog==0)) {
565 /* totally random entry */
566 ZSTD_compressionParameters const p = ZSTD_adjustCParams(randomParams(), srcSize, 0);
567 playAround(f, winners, p, srcBuffer, srcSize, ctx);
568 }
569 else
570 playAround(f, winners, winners[id].params, srcBuffer, srcSize, ctx);
571 }
572
573
574 static void BMK_benchMem(void* srcBuffer, size_t srcSize)
575 {
576 ZSTD_CCtx* const ctx = ZSTD_createCCtx();
577 ZSTD_compressionParameters params;
578 winnerInfo_t winners[NB_LEVELS_TRACKED];
579 const char* const rfName = "grillResults.txt";
580 FILE* const f = fopen(rfName, "w");
581 const size_t blockSize = g_blockSize ? g_blockSize : srcSize;
582
583 /* init */
584 if (ctx==NULL) { DISPLAY("ZSTD_createCCtx() failed \n"); exit(1); }
585 memset(winners, 0, sizeof(winners));
586 if (f==NULL) { DISPLAY("error opening %s \n", rfName); exit(1); }
587
588 if (g_singleRun) {
589 BMK_result_t testResult;
590 g_params = ZSTD_adjustCParams(g_params, srcSize, 0);
591 BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, g_params);
592 DISPLAY("\n");
593 return;
594 }
595
596 if (g_target)
597 g_cSpeedTarget[1] = g_target * 1000000;
598 else {
599 /* baseline config for level 1 */
600 BMK_result_t testResult;
601 params = ZSTD_getCParams(1, blockSize, 0);
602 BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, params);
603 g_cSpeedTarget[1] = (testResult.cSpeed * 31) / 32;
604 }
605
606 /* establish speed objectives (relative to level 1) */
607 { int i;
608 for (i=2; i<=ZSTD_maxCLevel(); i++)
609 g_cSpeedTarget[i] = (g_cSpeedTarget[i-1] * 25) / 32;
610 }
611
612 /* populate initial solution */
613 { const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel();
614 int i;
615 for (i=0; i<=maxSeeds; i++) {
616 params = ZSTD_getCParams(i, blockSize, 0);
617 BMK_seed(winners, params, srcBuffer, srcSize, ctx);
618 } }
619 BMK_printWinners(f, winners, srcSize);
620
621 /* start tests */
622 { const time_t grillStart = time(NULL);
623 do {
624 BMK_selectRandomStart(f, winners, srcBuffer, srcSize, ctx);
625 } while (BMK_timeSpan(grillStart) < g_grillDuration_s);
626 }
627
628 /* end summary */
629 BMK_printWinners(f, winners, srcSize);
630 DISPLAY("grillParams operations completed \n");
631
632 /* clean up*/
633 fclose(f);
634 ZSTD_freeCCtx(ctx);
635 }
636
637
638 static int benchSample(void)
639 {
640 void* origBuff;
641 size_t const benchedSize = sampleSize;
642 const char* const name = "Sample 10MiB";
643
644 /* Allocation */
645 origBuff = malloc(benchedSize);
646 if (!origBuff) { DISPLAY("\nError: not enough memory!\n"); return 12; }
647
648 /* Fill buffer */
649 RDG_genBuffer(origBuff, benchedSize, g_compressibility, 0.0, 0);
650
651 /* bench */
652 DISPLAY("\r%79s\r", "");
653 DISPLAY("using %s %i%%: \n", name, (int)(g_compressibility*100));
654 BMK_benchMem(origBuff, benchedSize);
655
656 free(origBuff);
657 return 0;
658 }
659
660
661 int benchFiles(const char** fileNamesTable, int nbFiles)
662 {
663 int fileIdx=0;
664
665 /* Loop for each file */
666 while (fileIdx<nbFiles) {
667 const char* const inFileName = fileNamesTable[fileIdx++];
668 FILE* const inFile = fopen( inFileName, "rb" );
669 U64 const inFileSize = UTIL_getFileSize(inFileName);
670 size_t benchedSize;
671 void* origBuff;
672
673 /* Check file existence */
674 if (inFile==NULL) {
675 DISPLAY( "Pb opening %s\n", inFileName);
676 return 11;
677 }
678
679 /* Memory allocation */
680 benchedSize = BMK_findMaxMem(inFileSize*3) / 3;
681 if ((U64)benchedSize > inFileSize) benchedSize = (size_t)inFileSize;
682 if (benchedSize < inFileSize)
683 DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", inFileName, (int)(benchedSize>>20));
684 origBuff = malloc(benchedSize);
685 if (origBuff==NULL) {
686 DISPLAY("\nError: not enough memory!\n");
687 fclose(inFile);
688 return 12;
689 }
690
691 /* Fill input buffer */
692 DISPLAY("Loading %s... \r", inFileName);
693 { size_t const readSize = fread(origBuff, 1, benchedSize, inFile);
694 fclose(inFile);
695 if(readSize != benchedSize) {
696 DISPLAY("\nError: problem reading file '%s' !! \n", inFileName);
697 free(origBuff);
698 return 13;
699 } }
700
701 /* bench */
702 DISPLAY("\r%79s\r", "");
703 DISPLAY("using %s : \n", inFileName);
704 BMK_benchMem(origBuff, benchedSize);
705
706 /* clean */
707 free(origBuff);
708 }
709
710 return 0;
711 }
712
713
714 int optimizeForSize(const char* inFileName, U32 targetSpeed)
715 {
716 FILE* const inFile = fopen( inFileName, "rb" );
717 U64 const inFileSize = UTIL_getFileSize(inFileName);
718 size_t benchedSize = BMK_findMaxMem(inFileSize*3) / 3;
719 void* origBuff;
720
721 /* Init */
722 if (inFile==NULL) { DISPLAY( "Pb opening %s\n", inFileName); return 11; }
723
724 /* Memory allocation & restrictions */
725 if ((U64)benchedSize > inFileSize) benchedSize = (size_t)inFileSize;
726 if (benchedSize < inFileSize)
727 DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", inFileName, (int)(benchedSize>>20));
728
729 /* Alloc */
730 origBuff = malloc(benchedSize);
731 if(!origBuff) {
732 DISPLAY("\nError: not enough memory!\n");
733 fclose(inFile);
734 return 12;
735 }
736
737 /* Fill input buffer */
738 DISPLAY("Loading %s... \r", inFileName);
739 { size_t const readSize = fread(origBuff, 1, benchedSize, inFile);
740 fclose(inFile);
741 if(readSize != benchedSize) {
742 DISPLAY("\nError: problem reading file '%s' !! \n", inFileName);
743 free(origBuff);
744 return 13;
745 } }
746
747 /* bench */
748 DISPLAY("\r%79s\r", "");
749 DISPLAY("optimizing for %s - limit speed %u MB/s \n", inFileName, targetSpeed);
750 targetSpeed *= 1000;
751
752 { ZSTD_CCtx* const ctx = ZSTD_createCCtx();
753 ZSTD_compressionParameters params;
754 winnerInfo_t winner;
755 BMK_result_t candidate;
756 const size_t blockSize = g_blockSize ? g_blockSize : benchedSize;
757
758 /* init */
759 if (ctx==NULL) { DISPLAY("\n ZSTD_createCCtx error \n"); free(origBuff); return 14;}
760 memset(&winner, 0, sizeof(winner));
761 winner.result.cSize = (size_t)(-1);
762
763 /* find best solution from default params */
764 { const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel();
765 int i;
766 for (i=1; i<=maxSeeds; i++) {
767 params = ZSTD_getCParams(i, blockSize, 0);
768 BMK_benchParam(&candidate, origBuff, benchedSize, ctx, params);
769 if (candidate.cSpeed < targetSpeed)
770 break;
771 if ( (candidate.cSize < winner.result.cSize)
772 | ((candidate.cSize == winner.result.cSize) & (candidate.cSpeed > winner.result.cSpeed)) )
773 {
774 winner.params = params;
775 winner.result = candidate;
776 BMK_printWinner(stdout, i, winner.result, winner.params, benchedSize);
777 } }
778 }
779 BMK_printWinner(stdout, 99, winner.result, winner.params, benchedSize);
780
781 /* start tests */
782 { time_t const grillStart = time(NULL);
783 do {
784 params = winner.params;
785 paramVariation(&params);
786 if ((FUZ_rand(&g_rand) & 15) == 3) params = randomParams();
787
788 /* exclude faster if already played set of params */
789 if (FUZ_rand(&g_rand) & ((1 << NB_TESTS_PLAYED(params))-1)) continue;
790
791 /* test */
792 NB_TESTS_PLAYED(params)++;
793 BMK_benchParam(&candidate, origBuff, benchedSize, ctx, params);
794
795 /* improvement found => new winner */
796 if ( (candidate.cSpeed > targetSpeed)
797 & ( (candidate.cSize < winner.result.cSize)
798 | ((candidate.cSize == winner.result.cSize) & (candidate.cSpeed > winner.result.cSpeed)) ) )
799 {
800 winner.params = params;
801 winner.result = candidate;
802 BMK_printWinner(stdout, 99, winner.result, winner.params, benchedSize);
803 }
804 } while (BMK_timeSpan(grillStart) < g_grillDuration_s);
805 }
806
807 /* end summary */
808 BMK_printWinner(stdout, 99, winner.result, winner.params, benchedSize);
809 DISPLAY("grillParams size - optimizer completed \n");
810
811 /* clean up*/
812 ZSTD_freeCCtx(ctx);
813 }
814
815 free(origBuff);
816 return 0;
817 }
818
819
820 static int usage(const char* exename)
821 {
822 DISPLAY( "Usage :\n");
823 DISPLAY( " %s [arg] file\n", exename);
824 DISPLAY( "Arguments :\n");
825 DISPLAY( " file : path to the file used as reference (if none, generates a compressible sample)\n");
826 DISPLAY( " -H/-h : Help (this text + advanced options)\n");
827 return 0;
828 }
829
830 static int usage_advanced(void)
831 {
832 DISPLAY( "\nAdvanced options :\n");
833 DISPLAY( " -T# : set level 1 speed objective \n");
834 DISPLAY( " -B# : cut input into blocks of size # (default : single block) \n");
835 DISPLAY( " -i# : iteration loops [1-9](default : %i) \n", NBLOOPS);
836 DISPLAY( " -O# : find Optimized parameters for # target speed (default : 0) \n");
837 DISPLAY( " -S : Single run \n");
838 DISPLAY( " -P# : generated sample compressibility (default : %.1f%%) \n", COMPRESSIBILITY_DEFAULT * 100);
839 return 0;
840 }
841
842 static int badusage(const char* exename)
843 {
844 DISPLAY("Wrong parameters\n");
845 usage(exename);
846 return 1;
847 }
848
849 int main(int argc, const char** argv)
850 {
851 int i,
852 filenamesStart=0,
853 result;
854 const char* exename=argv[0];
855 const char* input_filename=0;
856 U32 optimizer = 0;
857 U32 main_pause = 0;
858 U32 targetSpeed = 0;
859
860 /* checks */
861 if (NB_LEVELS_TRACKED <= ZSTD_maxCLevel()) {
862 DISPLAY("Error : NB_LEVELS_TRACKED <= ZSTD_maxCLevel() \n");
863 exit(1);
864 }
865
866 /* Welcome message */
867 DISPLAY(WELCOME_MESSAGE);
868
869 if (argc<1) { badusage(exename); return 1; }
870
871 for(i=1; i<argc; i++) {
872 const char* argument = argv[i];
873
874 if(!argument) continue; /* Protection if argument empty */
875
876 if(!strcmp(argument,"--no-seed")) { g_noSeed = 1; continue; }
877
878 /* Decode command (note : aggregated commands are allowed) */
879 if (argument[0]=='-') {
880 argument++;
881
882 while (argument[0]!=0) {
883
884 switch(argument[0])
885 {
886 /* Display help on usage */
887 case 'h' :
888 case 'H': usage(exename); usage_advanced(); return 0;
889
890 /* Pause at the end (hidden option) */
891 case 'p': main_pause = 1; argument++; break;
892
893 /* Modify Nb Iterations */
894 case 'i':
895 argument++;
896 if ((argument[0] >='0') & (argument[0] <='9'))
897 g_nbIterations = *argument++ - '0';
898 break;
899
900 /* Sample compressibility (when no file provided) */
901 case 'P':
902 argument++;
903 { U32 proba32 = 0;
904 while ((argument[0]>= '0') & (argument[0]<= '9'))
905 proba32 = (proba32*10) + (*argument++ - '0');
906 g_compressibility = (double)proba32 / 100.;
907 }
908 break;
909
910 case 'O':
911 argument++;
912 optimizer=1;
913 targetSpeed = 0;
914 while ((*argument >= '0') & (*argument <= '9'))
915 targetSpeed = (targetSpeed*10) + (*argument++ - '0');
916 break;
917
918 /* Run Single conf */
919 case 'S':
920 g_singleRun = 1;
921 argument++;
922 g_params = ZSTD_getCParams(2, g_blockSize, 0);
923 for ( ; ; ) {
924 switch(*argument)
925 {
926 case 'w':
927 g_params.windowLog = 0;
928 argument++;
929 while ((*argument>= '0') && (*argument<='9'))
930 g_params.windowLog *= 10, g_params.windowLog += *argument++ - '0';
931 continue;
932 case 'c':
933 g_params.chainLog = 0;
934 argument++;
935 while ((*argument>= '0') && (*argument<='9'))
936 g_params.chainLog *= 10, g_params.chainLog += *argument++ - '0';
937 continue;
938 case 'h':
939 g_params.hashLog = 0;
940 argument++;
941 while ((*argument>= '0') && (*argument<='9'))
942 g_params.hashLog *= 10, g_params.hashLog += *argument++ - '0';
943 continue;
944 case 's':
945 g_params.searchLog = 0;
946 argument++;
947 while ((*argument>= '0') && (*argument<='9'))
948 g_params.searchLog *= 10, g_params.searchLog += *argument++ - '0';
949 continue;
950 case 'l': /* search length */
951 g_params.searchLength = 0;
952 argument++;
953 while ((*argument>= '0') && (*argument<='9'))
954 g_params.searchLength *= 10, g_params.searchLength += *argument++ - '0';
955 continue;
956 case 't': /* target length */
957 g_params.targetLength = 0;
958 argument++;
959 while ((*argument>= '0') && (*argument<='9'))
960 g_params.targetLength *= 10, g_params.targetLength += *argument++ - '0';
961 continue;
962 case 'S': /* strategy */
963 argument++;
964 while ((*argument>= '0') && (*argument<='9'))
965 g_params.strategy = (ZSTD_strategy)(*argument++ - '0');
966 continue;
967 case 'L':
968 { int cLevel = 0;
969 argument++;
970 while ((*argument>= '0') && (*argument<='9'))
971 cLevel *= 10, cLevel += *argument++ - '0';
972 g_params = ZSTD_getCParams(cLevel, g_blockSize, 0);
973 continue;
974 }
975 default : ;
976 }
977 break;
978 }
979 break;
980
981 /* target level1 speed objective, in MB/s */
982 case 'T':
983 argument++;
984 g_target = 0;
985 while ((*argument >= '0') && (*argument <= '9'))
986 g_target = (g_target*10) + (*argument++ - '0');
987 break;
988
989 /* cut input into blocks */
990 case 'B':
991 g_blockSize = 0;
992 argument++;
993 while ((*argument >='0') & (*argument <='9'))
994 g_blockSize = (g_blockSize*10) + (*argument++ - '0');
995 if (*argument=='K') g_blockSize<<=10, argument++; /* allows using KB notation */
996 if (*argument=='M') g_blockSize<<=20, argument++;
997 if (*argument=='B') argument++;
998 DISPLAY("using %u KB block size \n", g_blockSize>>10);
999 break;
1000
1001 /* Unknown command */
1002 default : return badusage(exename);
1003 }
1004 }
1005 continue;
1006 } /* if (argument[0]=='-') */
1007
1008 /* first provided filename is input */
1009 if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }
1010 }
1011
1012 if (filenamesStart==0)
1013 result = benchSample();
1014 else {
1015 if (optimizer)
1016 result = optimizeForSize(input_filename, targetSpeed);
1017 else
1018 result = benchFiles(argv+filenamesStart, argc-filenamesStart);
1019 }
1020
1021 if (main_pause) { int unused; printf("press enter...\n"); unused = getchar(); (void)unused; }
1022
1023 return result;
1024 }