2 * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
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.
11 /*-************************************
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 */
22 #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_estimateCCtxSize */
28 /*-************************************
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__
38 #define GB *(1ULL<<30)
41 #define TIMELOOP (2 * CLOCKS_PER_SEC)
43 #define NB_LEVELS_TRACKED 30
45 static const size_t maxMemory
= (sizeof(size_t)==4) ? (2 GB
- 64 MB
) : (size_t)(1ULL << ((sizeof(size_t)*8)-31));
47 #define COMPRESSIBILITY_DEFAULT 0.50
48 static const size_t sampleSize
= 10000000;
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;
56 /*-************************************
58 **************************************/
59 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
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
};
74 void BMK_SetNbIterations(int nbLoops
)
76 g_nbIterations
= nbLoops
;
77 DISPLAY("- %u iterations -\n", g_nbIterations
);
81 /*-*******************************************************
83 *********************************************************/
85 static clock_t BMK_clockSpan(clock_t cStart
) { return clock() - cStart
; } /* works even if overflow ; max span ~ 30 mn */
87 static U32
BMK_timeSpan(time_t tStart
) { return (U32
)difftime(time(NULL
), tStart
); } /* accuracy in seconds only, span can be multiple years */
90 static size_t BMK_findMaxMem(U64 requiredMem
)
92 size_t const step
= 64 MB
;
95 requiredMem
= (((requiredMem
>> 26) + 1) << 26);
96 if (requiredMem
> maxMemory
) requiredMem
= maxMemory
;
98 requiredMem
+= 2*step
;
101 testmem
= malloc ((size_t)requiredMem
);
105 return (size_t) (requiredMem
- step
);
109 # define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r)))
110 U32
FUZ_rand(U32
* src
)
112 const U32 prime1
= 2654435761U;
113 const U32 prime2
= 2246822519U;
117 rand32
= FUZ_rotl32(rand32
, 13);
123 /*-*******************************************************
125 *********************************************************/
144 #define MIN(a,b) ( (a) < (b) ? (a) : (b) )
146 static size_t BMK_benchParam(BMK_result_t
* resultPtr
,
147 const void* srcBuffer
, size_t srcSize
,
149 const ZSTD_compressionParameters cParams
)
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 };
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
);
178 /* Calculating input Checksum */
179 crcOrig
= XXH64(srcBuffer
, srcSize
, 0);
181 /* Init blockTable data */
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
;
201 /* warmimg up memory */
202 RDG_genBuffer(compressedBuffer
, maxCompressedSize
, 0.10, 0.10, 1);
207 double fastestC
= 100000000., fastestD
= 100000000.;
210 clock_t const benchStart
= clock();
212 DISPLAY("\r%79s\r", "");
213 memset(¶ms
, 0, sizeof(params
));
214 params
.cParams
= cParams
;
215 for (loopNb
= 1; loopNb
<= g_nbIterations
; loopNb
++) {
218 clock_t roundStart
, roundClock
;
220 { clock_t const benchTime
= BMK_clockSpan(benchStart
);
221 if (benchTime
> g_maxParamTime
) break; }
224 DISPLAY("\r%1u-%s : %9u ->", loopNb
, name
, (U32
)srcSize
);
225 memset(compressedBuffer
, 0xE5, maxCompressedSize
);
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
,
240 roundClock
= BMK_clockSpan(roundStart
);
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
;
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
;
255 memset(resultBuffer
, 0xD6, srcSize
);
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
);
266 roundClock
= BMK_clockSpan(roundStart
);
268 if ((double)roundClock
< fastestD
* CLOCKS_PER_SEC
* nbLoops
) fastestD
= ((double)roundClock
/ CLOCKS_PER_SEC
) / nbLoops
;
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
;
276 crcCheck
= XXH64(resultBuffer
, srcSize
, 0);
277 if (crcOrig
!=crcCheck
) {
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
);
293 free(compressedBuffer
);
299 const char* g_stratName
[] = { "ZSTD_fast ",
308 static void BMK_printWinner(FILE* f
, U32 cLevel
, BMK_result_t result
, ZSTD_compressionParameters params
, size_t srcSize
)
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
)]);
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.);
320 static double g_cSpeedTarget
[NB_LEVELS_TRACKED
] = { 0. }; /* NB_LEVELS_TRACKED : checked at main() */
324 ZSTD_compressionParameters params
;
327 static void BMK_printWinners2(FILE* f
, const winnerInfo_t
* winners
, size_t srcSize
)
331 fprintf(f
, "\n /* Proposed configurations : */ \n");
332 fprintf(f
, " /* W, C, H, S, L, T, strat */ \n");
334 for (cLevel
=0; cLevel
<= ZSTD_maxCLevel(); cLevel
++)
335 BMK_printWinner(f
, cLevel
, winners
[cLevel
].result
, winners
[cLevel
].params
, srcSize
);
339 static void BMK_printWinners(FILE* f
, const winnerInfo_t
* winners
, size_t srcSize
)
341 fseek(f
, 0, SEEK_SET
);
342 BMK_printWinners2(f
, winners
, srcSize
);
344 BMK_printWinners2(stdout
, winners
, srcSize
);
347 static int BMK_seed(winnerInfo_t
* winners
, const ZSTD_compressionParameters params
,
348 const void* srcBuffer
, size_t srcSize
,
351 BMK_result_t testResult
;
355 BMK_benchParam(&testResult
, srcBuffer
, srcSize
, ctx
, params
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
427 winners
[cLevel
].result
= testResult
;
428 winners
[cLevel
].params
= params
;
429 BMK_printWinner(stdout
, cLevel
, testResult
, params
, srcSize
);
438 /* nullified useless params, to ensure count stats */
439 static ZSTD_compressionParameters
* sanitizeParams(ZSTD_compressionParameters 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;
452 static void paramVariation(ZSTD_compressionParameters
* ptr
)
454 ZSTD_compressionParameters p
;
457 U32 nbChanges
= (FUZ_rand(&g_rand
) & 3) + 1;
459 for ( ; nbChanges
; nbChanges
--) {
460 const U32 changeID
= FUZ_rand(&g_rand
) % 14;
472 p
.searchLog
++; break;
474 p
.searchLog
--; break;
476 p
.windowLog
++; break;
478 p
.windowLog
--; break;
480 p
.searchLength
++; break;
482 p
.searchLength
--; break;
484 p
.strategy
= (ZSTD_strategy
)(((U32
)p
.strategy
)+1); break;
486 p
.strategy
= (ZSTD_strategy
)(((U32
)p
.strategy
)-1); break;
488 p
.targetLength
*= 1 + ((double)(FUZ_rand(&g_rand
)&255)) / 256.; break;
490 p
.targetLength
/= 1 + ((double)(FUZ_rand(&g_rand
)&255)) / 256.; break;
493 validated
= !ZSTD_isError(ZSTD_checkCParams(p
));
499 #define PARAMTABLELOG 25
500 #define PARAMTABLESIZE (1<<PARAMTABLELOG)
501 #define PARAMTABLEMASK (PARAMTABLESIZE-1)
502 static BYTE g_alreadyTested
[PARAMTABLESIZE
] = {0}; /* init to zero */
504 #define NB_TESTS_PLAYED(p) \
505 g_alreadyTested[(XXH64(sanitizeParams(p), sizeof(p), 0) >> 3) & PARAMTABLEMASK]
508 #define MAX(a,b) ( (a) > (b) ? (a) : (b) )
510 static void playAround(FILE* f
, winnerInfo_t
* winners
,
511 ZSTD_compressionParameters params
,
512 const void* srcBuffer
, size_t srcSize
,
515 int nbVariations
= 0;
516 clock_t const clockStart
= clock();
518 while (BMK_clockSpan(clockStart
) < g_maxVariationTime
) {
519 ZSTD_compressionParameters p
= params
;
521 if (nbVariations
++ > g_maxNbVariations
) break;
524 /* exclude faster if already played params */
525 if (FUZ_rand(&g_rand
) & ((1 << NB_TESTS_PLAYED(p
))-1))
529 NB_TESTS_PLAYED(p
)++;
530 if (!BMK_seed(winners
, p
, srcBuffer
, srcSize
, ctx
)) continue;
532 /* improvement found => search more */
533 BMK_printWinners(f
, winners
, srcSize
);
534 playAround(f
, winners
, p
, srcBuffer
, srcSize
, ctx
);
540 static ZSTD_compressionParameters
randomParams(void)
542 ZSTD_compressionParameters p
;
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
));
558 static void BMK_selectRandomStart(
559 FILE* f
, winnerInfo_t
* winners
,
560 const void* srcBuffer
, size_t srcSize
,
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
);
570 playAround(f
, winners
, winners
[id
].params
, srcBuffer
, srcSize
, ctx
);
574 static void BMK_benchMem(void* srcBuffer
, size_t srcSize
)
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
;
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); }
589 BMK_result_t testResult
;
590 g_params
= ZSTD_adjustCParams(g_params
, srcSize
, 0);
591 BMK_benchParam(&testResult
, srcBuffer
, srcSize
, ctx
, g_params
);
597 g_cSpeedTarget
[1] = g_target
* 1000000;
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;
606 /* establish speed objectives (relative to level 1) */
608 for (i
=2; i
<=ZSTD_maxCLevel(); i
++)
609 g_cSpeedTarget
[i
] = (g_cSpeedTarget
[i
-1] * 25) / 32;
612 /* populate initial solution */
613 { const int maxSeeds
= g_noSeed
? 1 : ZSTD_maxCLevel();
615 for (i
=0; i
<=maxSeeds
; i
++) {
616 params
= ZSTD_getCParams(i
, blockSize
, 0);
617 BMK_seed(winners
, params
, srcBuffer
, srcSize
, ctx
);
619 BMK_printWinners(f
, winners
, srcSize
);
622 { const time_t grillStart
= time(NULL
);
624 BMK_selectRandomStart(f
, winners
, srcBuffer
, srcSize
, ctx
);
625 } while (BMK_timeSpan(grillStart
) < g_grillDuration_s
);
629 BMK_printWinners(f
, winners
, srcSize
);
630 DISPLAY("grillParams operations completed \n");
638 static int benchSample(void)
641 size_t const benchedSize
= sampleSize
;
642 const char* const name
= "Sample 10MiB";
645 origBuff
= malloc(benchedSize
);
646 if (!origBuff
) { DISPLAY("\nError: not enough memory!\n"); return 12; }
649 RDG_genBuffer(origBuff
, benchedSize
, g_compressibility
, 0.0, 0);
652 DISPLAY("\r%79s\r", "");
653 DISPLAY("using %s %i%%: \n", name
, (int)(g_compressibility
*100));
654 BMK_benchMem(origBuff
, benchedSize
);
661 int benchFiles(const char** fileNamesTable
, int nbFiles
)
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
);
673 /* Check file existence */
675 DISPLAY( "Pb opening %s\n", inFileName
);
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");
691 /* Fill input buffer */
692 DISPLAY("Loading %s... \r", inFileName
);
693 { size_t const readSize
= fread(origBuff
, 1, benchedSize
, inFile
);
695 if(readSize
!= benchedSize
) {
696 DISPLAY("\nError: problem reading file '%s' !! \n", inFileName
);
702 DISPLAY("\r%79s\r", "");
703 DISPLAY("using %s : \n", inFileName
);
704 BMK_benchMem(origBuff
, benchedSize
);
714 int optimizeForSize(const char* inFileName
, U32 targetSpeed
)
716 FILE* const inFile
= fopen( inFileName
, "rb" );
717 U64
const inFileSize
= UTIL_getFileSize(inFileName
);
718 size_t benchedSize
= BMK_findMaxMem(inFileSize
*3) / 3;
722 if (inFile
==NULL
) { DISPLAY( "Pb opening %s\n", inFileName
); return 11; }
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));
730 origBuff
= malloc(benchedSize
);
732 DISPLAY("\nError: not enough memory!\n");
737 /* Fill input buffer */
738 DISPLAY("Loading %s... \r", inFileName
);
739 { size_t const readSize
= fread(origBuff
, 1, benchedSize
, inFile
);
741 if(readSize
!= benchedSize
) {
742 DISPLAY("\nError: problem reading file '%s' !! \n", inFileName
);
748 DISPLAY("\r%79s\r", "");
749 DISPLAY("optimizing for %s - limit speed %u MB/s \n", inFileName
, targetSpeed
);
752 { ZSTD_CCtx
* const ctx
= ZSTD_createCCtx();
753 ZSTD_compressionParameters params
;
755 BMK_result_t candidate
;
756 const size_t blockSize
= g_blockSize
? g_blockSize
: benchedSize
;
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);
763 /* find best solution from default params */
764 { const int maxSeeds
= g_noSeed
? 1 : ZSTD_maxCLevel();
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
)
771 if ( (candidate
.cSize
< winner
.result
.cSize
)
772 | ((candidate
.cSize
== winner
.result
.cSize
) & (candidate
.cSpeed
> winner
.result
.cSpeed
)) )
774 winner
.params
= params
;
775 winner
.result
= candidate
;
776 BMK_printWinner(stdout
, i
, winner
.result
, winner
.params
, benchedSize
);
779 BMK_printWinner(stdout
, 99, winner
.result
, winner
.params
, benchedSize
);
782 { time_t const grillStart
= time(NULL
);
784 params
= winner
.params
;
785 paramVariation(¶ms
);
786 if ((FUZ_rand(&g_rand
) & 15) == 3) params
= randomParams();
788 /* exclude faster if already played set of params */
789 if (FUZ_rand(&g_rand
) & ((1 << NB_TESTS_PLAYED(params
))-1)) continue;
792 NB_TESTS_PLAYED(params
)++;
793 BMK_benchParam(&candidate
, origBuff
, benchedSize
, ctx
, params
);
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
)) ) )
800 winner
.params
= params
;
801 winner
.result
= candidate
;
802 BMK_printWinner(stdout
, 99, winner
.result
, winner
.params
, benchedSize
);
804 } while (BMK_timeSpan(grillStart
) < g_grillDuration_s
);
808 BMK_printWinner(stdout
, 99, winner
.result
, winner
.params
, benchedSize
);
809 DISPLAY("grillParams size - optimizer completed \n");
820 static int usage(const char* exename
)
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");
830 static int usage_advanced(void)
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);
842 static int badusage(const char* exename
)
844 DISPLAY("Wrong parameters\n");
849 int main(int argc
, const char** argv
)
854 const char* exename
=argv
[0];
855 const char* input_filename
=0;
861 if (NB_LEVELS_TRACKED
<= ZSTD_maxCLevel()) {
862 DISPLAY("Error : NB_LEVELS_TRACKED <= ZSTD_maxCLevel() \n");
866 /* Welcome message */
867 DISPLAY(WELCOME_MESSAGE
);
869 if (argc
<1) { badusage(exename
); return 1; }
871 for(i
=1; i
<argc
; i
++) {
872 const char* argument
= argv
[i
];
874 if(!argument
) continue; /* Protection if argument empty */
876 if(!strcmp(argument
,"--no-seed")) { g_noSeed
= 1; continue; }
878 /* Decode command (note : aggregated commands are allowed) */
879 if (argument
[0]=='-') {
882 while (argument
[0]!=0) {
886 /* Display help on usage */
888 case 'H': usage(exename
); usage_advanced(); return 0;
890 /* Pause at the end (hidden option) */
891 case 'p': main_pause
= 1; argument
++; break;
893 /* Modify Nb Iterations */
896 if ((argument
[0] >='0') & (argument
[0] <='9'))
897 g_nbIterations
= *argument
++ - '0';
900 /* Sample compressibility (when no file provided) */
904 while ((argument
[0]>= '0') & (argument
[0]<= '9'))
905 proba32
= (proba32
*10) + (*argument
++ - '0');
906 g_compressibility
= (double)proba32
/ 100.;
914 while ((*argument
>= '0') & (*argument
<= '9'))
915 targetSpeed
= (targetSpeed
*10) + (*argument
++ - '0');
918 /* Run Single conf */
922 g_params
= ZSTD_getCParams(2, g_blockSize
, 0);
927 g_params
.windowLog
= 0;
929 while ((*argument
>= '0') && (*argument
<='9'))
930 g_params
.windowLog
*= 10, g_params
.windowLog
+= *argument
++ - '0';
933 g_params
.chainLog
= 0;
935 while ((*argument
>= '0') && (*argument
<='9'))
936 g_params
.chainLog
*= 10, g_params
.chainLog
+= *argument
++ - '0';
939 g_params
.hashLog
= 0;
941 while ((*argument
>= '0') && (*argument
<='9'))
942 g_params
.hashLog
*= 10, g_params
.hashLog
+= *argument
++ - '0';
945 g_params
.searchLog
= 0;
947 while ((*argument
>= '0') && (*argument
<='9'))
948 g_params
.searchLog
*= 10, g_params
.searchLog
+= *argument
++ - '0';
950 case 'l': /* search length */
951 g_params
.searchLength
= 0;
953 while ((*argument
>= '0') && (*argument
<='9'))
954 g_params
.searchLength
*= 10, g_params
.searchLength
+= *argument
++ - '0';
956 case 't': /* target length */
957 g_params
.targetLength
= 0;
959 while ((*argument
>= '0') && (*argument
<='9'))
960 g_params
.targetLength
*= 10, g_params
.targetLength
+= *argument
++ - '0';
962 case 'S': /* strategy */
964 while ((*argument
>= '0') && (*argument
<='9'))
965 g_params
.strategy
= (ZSTD_strategy
)(*argument
++ - '0');
970 while ((*argument
>= '0') && (*argument
<='9'))
971 cLevel
*= 10, cLevel
+= *argument
++ - '0';
972 g_params
= ZSTD_getCParams(cLevel
, g_blockSize
, 0);
981 /* target level1 speed objective, in MB/s */
985 while ((*argument
>= '0') && (*argument
<= '9'))
986 g_target
= (g_target
*10) + (*argument
++ - '0');
989 /* cut input into blocks */
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);
1001 /* Unknown command */
1002 default : return badusage(exename
);
1006 } /* if (argument[0]=='-') */
1008 /* first provided filename is input */
1009 if (!input_filename
) { input_filename
=argument
; filenamesStart
=i
; continue; }
1012 if (filenamesStart
==0)
1013 result
= benchSample();
1016 result
= optimizeForSize(input_filename
, targetSpeed
);
1018 result
= benchFiles(argv
+filenamesStart
, argc
-filenamesStart
);
1021 if (main_pause
) { int unused
; printf("press enter...\n"); unused
= getchar(); (void)unused
; }