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