]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/fullbench.c
import zstd 1.4.2
[FreeBSD/FreeBSD.git] / tests / fullbench.c
1 /*
2  * Copyright (c) 2015-present, Yann Collet, Facebook, Inc.
3  * All rights reserved.
4  *
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.
9  */
10
11
12 /*_************************************
13 *  Includes
14 **************************************/
15 #include "util.h"        /* Compiler options, UTIL_GetFileSize */
16 #include <stdlib.h>      /* malloc */
17 #include <stdio.h>       /* fprintf, fopen, ftello64 */
18 #include <assert.h>
19
20 #include "timefn.h"      /* UTIL_clockSpanNano, UTIL_getTime */
21 #include "mem.h"         /* U32 */
22 #ifndef ZSTD_DLL_IMPORT
23     #include "zstd_internal.h"   /* ZSTD_decodeSeqHeaders, ZSTD_blockHeaderSize, blockType_e, KB, MB */
24 #else
25     #define KB *(1 <<10)
26     #define MB *(1 <<20)
27     #define GB *(1U<<30)
28     typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e;
29 #endif
30 #define ZSTD_STATIC_LINKING_ONLY  /* ZSTD_compressBegin, ZSTD_compressContinue, etc. */
31 #include "zstd.h"        /* ZSTD_versionString */
32 #include "util.h"        /* time functions */
33 #include "datagen.h"
34 #include "benchfn.h"     /* CustomBench */
35 #include "benchzstd.h"   /* MB_UNIT */
36
37
38 /*_************************************
39 *  Constants
40 **************************************/
41 #define PROGRAM_DESCRIPTION "Zstandard speed analyzer"
42 #define AUTHOR "Yann Collet"
43 #define WELCOME_MESSAGE "*** %s %s %i-bits, by %s (%s) ***\n", PROGRAM_DESCRIPTION, ZSTD_versionString(), (int)(sizeof(void*)*8), AUTHOR, __DATE__
44
45 #define NBLOOPS    6
46 #define TIMELOOP_S 2
47
48 #define KNUTH      2654435761U
49 #define MAX_MEM    (1984 MB)
50
51 #define DEFAULT_CLEVEL 1
52
53 #define COMPRESSIBILITY_DEFAULT 0.50
54 static const size_t kSampleSizeDefault = 10000000;
55
56 #define TIMELOOP_NANOSEC      (1*1000000000ULL) /* 1 second */
57
58
59 /*_************************************
60 *  Macros
61 **************************************/
62 #define DISPLAY(...)  fprintf(stderr, __VA_ARGS__)
63
64 #define CONTROL(c)  { if (!(c)) { abort(); } }   /* like assert(), but cannot be disabled */
65
66 /*_************************************
67 *  Benchmark Parameters
68 **************************************/
69 static unsigned g_nbIterations = NBLOOPS;
70
71
72 /*_*******************************************************
73 *  Private functions
74 *********************************************************/
75 static size_t BMK_findMaxMem(U64 requiredMem)
76 {
77     size_t const step = 64 MB;
78     void* testmem = NULL;
79
80     requiredMem = (((requiredMem >> 26) + 1) << 26);
81     if (requiredMem > MAX_MEM) requiredMem = MAX_MEM;
82
83     requiredMem += step;
84     do {
85         testmem = malloc ((size_t)requiredMem);
86         requiredMem -= step;
87     } while (!testmem);
88
89     free (testmem);
90     return (size_t) requiredMem;
91 }
92
93
94 /*_*******************************************************
95 *  Benchmark wrappers
96 *********************************************************/
97
98 static ZSTD_CCtx* g_zcc = NULL;
99
100 static size_t
101 local_ZSTD_compress(const void* src, size_t srcSize,
102                     void* dst, size_t dstSize,
103                     void* payload)
104 {
105     ZSTD_parameters p;
106     ZSTD_frameParameters f = { 1 /* contentSizeHeader*/, 0, 0 };
107     p.fParams = f;
108     p.cParams = *(ZSTD_compressionParameters*)payload;
109     return ZSTD_compress_advanced (g_zcc, dst, dstSize, src, srcSize, NULL ,0, p);
110     //return ZSTD_compress(dst, dstSize, src, srcSize, cLevel);
111 }
112
113 static size_t g_cSize = 0;
114 static size_t local_ZSTD_decompress(const void* src, size_t srcSize,
115                                     void* dst, size_t dstSize,
116                                     void* buff2)
117 {
118     (void)src; (void)srcSize;
119     return ZSTD_decompress(dst, dstSize, buff2, g_cSize);
120 }
121
122 static ZSTD_DCtx* g_zdc = NULL;
123
124 #ifndef ZSTD_DLL_IMPORT
125 extern size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* ctx, const void* src, size_t srcSize);
126 static size_t local_ZSTD_decodeLiteralsBlock(const void* src, size_t srcSize, void* dst, size_t dstSize, void* buff2)
127 {
128     (void)src; (void)srcSize; (void)dst; (void)dstSize;
129     return ZSTD_decodeLiteralsBlock(g_zdc, buff2, g_cSize);
130 }
131
132 static size_t local_ZSTD_decodeSeqHeaders(const void* src, size_t srcSize, void* dst, size_t dstSize, void* buff2)
133 {
134     int nbSeq;
135     (void)src; (void)srcSize; (void)dst; (void)dstSize;
136     return ZSTD_decodeSeqHeaders(g_zdc, &nbSeq, buff2, g_cSize);
137 }
138 #endif
139
140 static ZSTD_CStream* g_cstream= NULL;
141 static size_t
142 local_ZSTD_compressStream(const void* src, size_t srcSize,
143                           void* dst, size_t dstCapacity,
144                           void* payload)
145 {
146     ZSTD_outBuffer buffOut;
147     ZSTD_inBuffer buffIn;
148     ZSTD_parameters p;
149     ZSTD_frameParameters f = {1 /* contentSizeHeader*/, 0, 0};
150     p.fParams = f;
151     p.cParams = *(ZSTD_compressionParameters*)payload;
152     ZSTD_initCStream_advanced(g_cstream, NULL, 0, p, ZSTD_CONTENTSIZE_UNKNOWN);
153     buffOut.dst = dst;
154     buffOut.size = dstCapacity;
155     buffOut.pos = 0;
156     buffIn.src = src;
157     buffIn.size = srcSize;
158     buffIn.pos = 0;
159     ZSTD_compressStream(g_cstream, &buffOut, &buffIn);
160     ZSTD_endStream(g_cstream, &buffOut);
161     return buffOut.pos;
162 }
163
164 static size_t
165 local_ZSTD_compressStream_freshCCtx(const void* src, size_t srcSize,
166                           void* dst, size_t dstCapacity,
167                           void* payload)
168 {
169     ZSTD_CCtx* const cctx = ZSTD_createCCtx();
170     size_t r;
171     assert(cctx != NULL);
172
173     r = local_ZSTD_compressStream(src, srcSize, dst, dstCapacity, payload);
174
175     ZSTD_freeCCtx(cctx);
176
177     return r;
178 }
179
180 static size_t
181 local_ZSTD_compress_generic_end(const void* src, size_t srcSize,
182                                 void* dst, size_t dstCapacity,
183                                 void* payload)
184 {
185     (void)payload;
186     return ZSTD_compress2(g_cstream, dst, dstCapacity, src, srcSize);
187 }
188
189 static size_t
190 local_ZSTD_compress_generic_continue(const void* src, size_t srcSize,
191                                      void* dst, size_t dstCapacity,
192                                      void* payload)
193 {
194     ZSTD_outBuffer buffOut;
195     ZSTD_inBuffer buffIn;
196     (void)payload;
197     buffOut.dst = dst;
198     buffOut.size = dstCapacity;
199     buffOut.pos = 0;
200     buffIn.src = src;
201     buffIn.size = srcSize;
202     buffIn.pos = 0;
203     ZSTD_compressStream2(g_cstream, &buffOut, &buffIn, ZSTD_e_continue);
204     ZSTD_compressStream2(g_cstream, &buffOut, &buffIn, ZSTD_e_end);
205     return buffOut.pos;
206 }
207
208 static size_t
209 local_ZSTD_compress_generic_T2_end(const void* src, size_t srcSize,
210                                    void* dst, size_t dstCapacity,
211                                    void* payload)
212 {
213     (void)payload;
214     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_nbWorkers, 2);
215     return ZSTD_compress2(g_cstream, dst, dstCapacity, src, srcSize);
216 }
217
218 static size_t
219 local_ZSTD_compress_generic_T2_continue(const void* src, size_t srcSize,
220                                         void* dst, size_t dstCapacity,
221                                         void* payload)
222 {
223     ZSTD_outBuffer buffOut;
224     ZSTD_inBuffer buffIn;
225     (void)payload;
226     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_nbWorkers, 2);
227     buffOut.dst = dst;
228     buffOut.size = dstCapacity;
229     buffOut.pos = 0;
230     buffIn.src = src;
231     buffIn.size = srcSize;
232     buffIn.pos = 0;
233     ZSTD_compressStream2(g_cstream, &buffOut, &buffIn, ZSTD_e_continue);
234     while(ZSTD_compressStream2(g_cstream, &buffOut, &buffIn, ZSTD_e_end)) {}
235     return buffOut.pos;
236 }
237
238 static ZSTD_DStream* g_dstream= NULL;
239 static size_t
240 local_ZSTD_decompressStream(const void* src, size_t srcSize,
241                             void* dst, size_t dstCapacity,
242                             void* buff2)
243 {
244     ZSTD_outBuffer buffOut;
245     ZSTD_inBuffer buffIn;
246     (void)src; (void)srcSize;
247     ZSTD_initDStream(g_dstream);
248     buffOut.dst = dst;
249     buffOut.size = dstCapacity;
250     buffOut.pos = 0;
251     buffIn.src = buff2;
252     buffIn.size = g_cSize;
253     buffIn.pos = 0;
254     ZSTD_decompressStream(g_dstream, &buffOut, &buffIn);
255     return buffOut.pos;
256 }
257
258 #ifndef ZSTD_DLL_IMPORT
259 static size_t local_ZSTD_compressContinue(const void* src, size_t srcSize,
260                                           void* dst, size_t dstCapacity,
261                                           void* payload)
262 {
263     ZSTD_parameters p;
264     ZSTD_frameParameters f = { 1 /* contentSizeHeader*/, 0, 0 };
265     p.fParams = f;
266     p.cParams = *(ZSTD_compressionParameters*)payload;
267     ZSTD_compressBegin_advanced(g_zcc, NULL, 0, p, srcSize);
268     return ZSTD_compressEnd(g_zcc, dst, dstCapacity, src, srcSize);
269 }
270
271 #define FIRST_BLOCK_SIZE 8
272 static size_t
273 local_ZSTD_compressContinue_extDict(const void* src, size_t srcSize,
274                                     void* dst, size_t dstCapacity,
275                                     void* payload)
276 {
277     BYTE firstBlockBuf[FIRST_BLOCK_SIZE];
278
279     ZSTD_parameters p;
280     ZSTD_frameParameters const f = { 1, 0, 0 };
281     p.fParams = f;
282     p.cParams = *(ZSTD_compressionParameters*)payload;
283     ZSTD_compressBegin_advanced(g_zcc, NULL, 0, p, srcSize);
284     memcpy(firstBlockBuf, src, FIRST_BLOCK_SIZE);
285
286     {   size_t const compressResult = ZSTD_compressContinue(g_zcc,
287                                             dst, dstCapacity,
288                                             firstBlockBuf, FIRST_BLOCK_SIZE);
289         if (ZSTD_isError(compressResult)) {
290             DISPLAY("local_ZSTD_compressContinue_extDict error : %s\n",
291                     ZSTD_getErrorName(compressResult));
292             return compressResult;
293         }
294         dst = (BYTE*)dst + compressResult;
295         dstCapacity -= compressResult;
296     }
297     return ZSTD_compressEnd(g_zcc, dst, dstCapacity,
298                             (const BYTE*)src + FIRST_BLOCK_SIZE,
299                             srcSize - FIRST_BLOCK_SIZE);
300 }
301
302 static size_t local_ZSTD_decompressContinue(const void* src, size_t srcSize,
303                                             void* dst, size_t dstCapacity,
304                                             void* buff2)
305 {
306     size_t regeneratedSize = 0;
307     const BYTE* ip = (const BYTE*)buff2;
308     const BYTE* const iend = ip + g_cSize;
309     BYTE* op = (BYTE*)dst;
310     size_t remainingCapacity = dstCapacity;
311
312     (void)src; (void)srcSize;  /* unused */
313     ZSTD_decompressBegin(g_zdc);
314     while (ip < iend) {
315         size_t const iSize = ZSTD_nextSrcSizeToDecompress(g_zdc);
316         size_t const decodedSize = ZSTD_decompressContinue(g_zdc, op, remainingCapacity, ip, iSize);
317         ip += iSize;
318         regeneratedSize += decodedSize;
319         op += decodedSize;
320         remainingCapacity -= decodedSize;
321     }
322
323     return regeneratedSize;
324 }
325 #endif
326
327
328 /*_*******************************************************
329 *  Bench functions
330 *********************************************************/
331 static int benchMem(unsigned benchNb,
332                     const void* src, size_t srcSize,
333                     int cLevel, ZSTD_compressionParameters cparams)
334 {
335     size_t dstBuffSize = ZSTD_compressBound(srcSize);
336     BYTE*  dstBuff;
337     void*  dstBuff2;
338     void*  payload;
339     const char* benchName;
340     BMK_benchFn_t benchFunction;
341     int errorcode = 0;
342
343     /* Selection */
344     switch(benchNb)
345     {
346     case 1:
347         benchFunction = local_ZSTD_compress; benchName = "compress";
348         break;
349     case 2:
350         benchFunction = local_ZSTD_decompress; benchName = "decompress";
351         break;
352 #ifndef ZSTD_DLL_IMPORT
353     case 11:
354         benchFunction = local_ZSTD_compressContinue; benchName = "compressContinue";
355         break;
356     case 12:
357         benchFunction = local_ZSTD_compressContinue_extDict; benchName = "compressContinue_extDict";
358         break;
359     case 13:
360         benchFunction = local_ZSTD_decompressContinue; benchName = "decompressContinue";
361         break;
362     case 31:
363         benchFunction = local_ZSTD_decodeLiteralsBlock; benchName = "decodeLiteralsBlock";
364         break;
365     case 32:
366         benchFunction = local_ZSTD_decodeSeqHeaders; benchName = "decodeSeqHeaders";
367         break;
368 #endif
369     case 41:
370         benchFunction = local_ZSTD_compressStream; benchName = "compressStream";
371         break;
372     case 42:
373         benchFunction = local_ZSTD_decompressStream; benchName = "decompressStream";
374         break;
375     case 43:
376         benchFunction = local_ZSTD_compressStream_freshCCtx; benchName = "compressStream_freshCCtx";
377         break;
378     case 51:
379         benchFunction = local_ZSTD_compress_generic_continue; benchName = "compress_generic, continue";
380         break;
381     case 52:
382         benchFunction = local_ZSTD_compress_generic_end; benchName = "compress_generic, end";
383         break;
384     case 61:
385         benchFunction = local_ZSTD_compress_generic_T2_continue; benchName = "compress_generic, -T2, continue";
386         break;
387     case 62:
388         benchFunction = local_ZSTD_compress_generic_T2_end; benchName = "compress_generic, -T2, end";
389         break;
390     default :
391         return 0;
392     }
393
394     /* Allocation */
395     dstBuff = (BYTE*)malloc(dstBuffSize);
396     dstBuff2 = malloc(dstBuffSize);
397     if ((!dstBuff) || (!dstBuff2)) {
398         DISPLAY("\nError: not enough memory!\n");
399         free(dstBuff); free(dstBuff2);
400         return 12;
401     }
402     payload = dstBuff2;
403     if (g_zcc==NULL) g_zcc = ZSTD_createCCtx();
404     if (g_zdc==NULL) g_zdc = ZSTD_createDCtx();
405     if (g_cstream==NULL) g_cstream = ZSTD_createCStream();
406     if (g_dstream==NULL) g_dstream = ZSTD_createDStream();
407
408     /* DISPLAY("params: cLevel %d, wlog %d hlog %d clog %d slog %d mml %d tlen %d strat %d \n",
409           cLevel, cparams->windowLog, cparams->hashLog, cparams->chainLog, cparams->searchLog,
410           cparams->minMatch, cparams->targetLength, cparams->strategy); */
411
412     ZSTD_CCtx_setParameter(g_zcc, ZSTD_c_compressionLevel, cLevel);
413     ZSTD_CCtx_setParameter(g_zcc, ZSTD_c_windowLog, (int)cparams.windowLog);
414     ZSTD_CCtx_setParameter(g_zcc, ZSTD_c_hashLog, (int)cparams.hashLog);
415     ZSTD_CCtx_setParameter(g_zcc, ZSTD_c_chainLog, (int)cparams.chainLog);
416     ZSTD_CCtx_setParameter(g_zcc, ZSTD_c_searchLog, (int)cparams.searchLog);
417     ZSTD_CCtx_setParameter(g_zcc, ZSTD_c_minMatch, (int)cparams.minMatch);
418     ZSTD_CCtx_setParameter(g_zcc, ZSTD_c_targetLength, (int)cparams.targetLength);
419     ZSTD_CCtx_setParameter(g_zcc, ZSTD_c_strategy, cparams.strategy);
420
421
422     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_compressionLevel, cLevel);
423     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_windowLog, (int)cparams.windowLog);
424     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_hashLog, (int)cparams.hashLog);
425     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_chainLog, (int)cparams.chainLog);
426     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_searchLog, (int)cparams.searchLog);
427     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_minMatch, (int)cparams.minMatch);
428     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_targetLength, (int)cparams.targetLength);
429     ZSTD_CCtx_setParameter(g_cstream, ZSTD_c_strategy, cparams.strategy);
430
431     /* Preparation */
432     switch(benchNb)
433     {
434     case 1:
435         payload = &cparams;
436         break;
437     case 2:
438         g_cSize = ZSTD_compress(dstBuff2, dstBuffSize, src, srcSize, cLevel);
439         break;
440 #ifndef ZSTD_DLL_IMPORT
441     case 11:
442         payload = &cparams;
443         break;
444     case 12:
445         payload = &cparams;
446         break;
447     case 13 :
448         g_cSize = ZSTD_compress(dstBuff2, dstBuffSize, src, srcSize, cLevel);
449         break;
450     case 31:  /* ZSTD_decodeLiteralsBlock : starts literals block in dstBuff2 */
451         {   size_t frameHeaderSize;
452             g_cSize = ZSTD_compress(dstBuff, dstBuffSize, src, srcSize, cLevel);
453             frameHeaderSize = ZSTD_frameHeaderSize(dstBuff, ZSTD_FRAMEHEADERSIZE_PREFIX);
454             CONTROL(!ZSTD_isError(frameHeaderSize));
455             /* check block is compressible, hence contains a literals section */
456             {   blockProperties_t bp;
457                 ZSTD_getcBlockSize(dstBuff+frameHeaderSize, dstBuffSize, &bp);  /* Get 1st block type */
458                 if (bp.blockType != bt_compressed) {
459                     DISPLAY("ZSTD_decodeLiteralsBlock : impossible to test on this sample (not compressible)\n");
460                     goto _cleanOut;
461             }   }
462             {   size_t const skippedSize = frameHeaderSize + ZSTD_blockHeaderSize;
463                 memcpy(dstBuff2, dstBuff+skippedSize, g_cSize-skippedSize);
464             }
465             srcSize = srcSize > 128 KB ? 128 KB : srcSize;    /* speed relative to block */
466             ZSTD_decompressBegin(g_zdc);
467             break;
468         }
469     case 32:   /* ZSTD_decodeSeqHeaders */
470         {   blockProperties_t bp;
471             const BYTE* ip = dstBuff;
472             const BYTE* iend;
473             {   size_t const cSize = ZSTD_compress(dstBuff, dstBuffSize, src, srcSize, cLevel);
474                 CONTROL(cSize > ZSTD_FRAMEHEADERSIZE_PREFIX);
475             }
476             /* Skip frame Header */
477             {   size_t const frameHeaderSize = ZSTD_frameHeaderSize(dstBuff, ZSTD_FRAMEHEADERSIZE_PREFIX);
478                 CONTROL(!ZSTD_isError(frameHeaderSize));
479                 ip += frameHeaderSize;
480             }
481             /* Find end of block */
482             {   size_t const cBlockSize = ZSTD_getcBlockSize(ip, dstBuffSize, &bp);   /* Get 1st block type */
483                 if (bp.blockType != bt_compressed) {
484                     DISPLAY("ZSTD_decodeSeqHeaders : impossible to test on this sample (not compressible)\n");
485                     goto _cleanOut;
486                 }
487                 iend = ip + ZSTD_blockHeaderSize + cBlockSize;   /* End of first block */
488             }
489             ip += ZSTD_blockHeaderSize;    /* skip block header */
490             ZSTD_decompressBegin(g_zdc);
491             CONTROL(iend > ip);
492             ip += ZSTD_decodeLiteralsBlock(g_zdc, ip, (size_t)(iend-ip));   /* skip literal segment */
493             g_cSize = (size_t)(iend-ip);
494             memcpy(dstBuff2, ip, g_cSize);   /* copy rest of block (it starts by SeqHeader) */
495             srcSize = srcSize > 128 KB ? 128 KB : srcSize;   /* speed relative to block */
496             break;
497         }
498 #else
499     case 31:
500         goto _cleanOut;
501 #endif
502     case 41 :
503         payload = &cparams;
504         break;
505     case 42 :
506         g_cSize = ZSTD_compress(payload, dstBuffSize, src, srcSize, cLevel);
507         break;
508     case 43 :
509         payload = &cparams;
510         break;
511
512     /* test functions */
513     /* convention: test functions have ID > 100 */
514
515     default : ;
516     }
517
518      /* warming up dstBuff */
519     { size_t i; for (i=0; i<dstBuffSize; i++) dstBuff[i]=(BYTE)i; }
520
521     /* benchmark loop */
522     {   BMK_timedFnState_t* const tfs = BMK_createTimedFnState(g_nbIterations * 1000, 1000);
523         void* const avoidStrictAliasingPtr = &dstBuff;
524         BMK_benchParams_t bp;
525         BMK_runTime_t bestResult;
526         bestResult.sumOfReturn = 0;
527         bestResult.nanoSecPerRun = (double)TIMELOOP_NANOSEC * 2000000000;  /* hopefully large enough : must be larger than any potential measurement */
528         CONTROL(tfs != NULL);
529
530         bp.benchFn = benchFunction;
531         bp.benchPayload = payload;
532         bp.initFn = NULL;
533         bp.initPayload = NULL;
534         bp.errorFn = ZSTD_isError;
535         bp.blockCount = 1;
536         bp.srcBuffers = &src;
537         bp.srcSizes = &srcSize;
538         bp.dstBuffers = (void* const*) avoidStrictAliasingPtr;  /* circumvent strict aliasing warning on gcc-8,
539                                                                  * because gcc considers that `void* const *`  and `void**` are 2 different types */
540         bp.dstCapacities = &dstBuffSize;
541         bp.blockResults = NULL;
542
543         for (;;) {
544             BMK_runOutcome_t const bOutcome = BMK_benchTimedFn(tfs, bp);
545
546             if (!BMK_isSuccessful_runOutcome(bOutcome)) {
547                 DISPLAY("ERROR benchmarking function ! ! \n");
548                 errorcode = 1;
549                 goto _cleanOut;
550             }
551
552             {   BMK_runTime_t const newResult = BMK_extract_runTime(bOutcome);
553                 if (newResult.nanoSecPerRun < bestResult.nanoSecPerRun )
554                     bestResult.nanoSecPerRun = newResult.nanoSecPerRun;
555                 DISPLAY("\r%2u#%-29.29s:%8.1f MB/s  (%8u) ",
556                         benchNb, benchName,
557                         (double)srcSize * TIMELOOP_NANOSEC / bestResult.nanoSecPerRun / MB_UNIT,
558                         (unsigned)newResult.sumOfReturn );
559             }
560
561             if ( BMK_isCompleted_TimedFn(tfs) ) break;
562         }
563         BMK_freeTimedFnState(tfs);
564     }
565     DISPLAY("\n");
566
567 _cleanOut:
568     free(dstBuff);
569     free(dstBuff2);
570     ZSTD_freeCCtx(g_zcc); g_zcc=NULL;
571     ZSTD_freeDCtx(g_zdc); g_zdc=NULL;
572     ZSTD_freeCStream(g_cstream); g_cstream=NULL;
573     ZSTD_freeDStream(g_dstream); g_dstream=NULL;
574     return errorcode;
575 }
576
577
578 static int benchSample(U32 benchNb,
579                        size_t benchedSize, double compressibility,
580                        int cLevel, ZSTD_compressionParameters cparams)
581 {
582     /* Allocation */
583     void* const origBuff = malloc(benchedSize);
584     if (!origBuff) { DISPLAY("\nError: not enough memory!\n"); return 12; }
585
586     /* Fill buffer */
587     RDG_genBuffer(origBuff, benchedSize, compressibility, 0.0, 0);
588
589     /* bench */
590     DISPLAY("\r%70s\r", "");
591     DISPLAY(" Sample %u bytes : \n", (unsigned)benchedSize);
592     if (benchNb) {
593         benchMem(benchNb, origBuff, benchedSize, cLevel, cparams);
594     } else {  /* 0 == run all tests */
595         for (benchNb=0; benchNb<100; benchNb++) {
596             benchMem(benchNb, origBuff, benchedSize, cLevel, cparams);
597     }   }
598
599     free(origBuff);
600     return 0;
601 }
602
603
604 static int benchFiles(U32 benchNb,
605                       const char** fileNamesTable, const int nbFiles,
606                       int cLevel, ZSTD_compressionParameters cparams)
607 {
608     /* Loop for each file */
609     int fileIdx;
610     for (fileIdx=0; fileIdx<nbFiles; fileIdx++) {
611         const char* const inFileName = fileNamesTable[fileIdx];
612         FILE* const inFile = fopen( inFileName, "rb" );
613         size_t benchedSize;
614
615         /* Check file existence */
616         if (inFile==NULL) { DISPLAY( "Pb opening %s\n", inFileName); return 11; }
617
618         /* Memory allocation & restrictions */
619         {   U64 const inFileSize = UTIL_getFileSize(inFileName);
620             if (inFileSize == UTIL_FILESIZE_UNKNOWN) {
621                 DISPLAY( "Cannot measure size of %s\n", inFileName);
622                 fclose(inFile);
623                 return 11;
624             }
625             benchedSize = BMK_findMaxMem(inFileSize*3) / 3;
626             if ((U64)benchedSize > inFileSize)
627                 benchedSize = (size_t)inFileSize;
628             if ((U64)benchedSize < inFileSize) {
629                 DISPLAY("Not enough memory for '%s' full size; testing %u MB only... \n",
630                         inFileName, (unsigned)(benchedSize>>20));
631         }   }
632
633         /* Alloc */
634         {   void* const origBuff = malloc(benchedSize);
635             if (!origBuff) { DISPLAY("\nError: not enough memory!\n"); fclose(inFile); return 12; }
636
637             /* Fill input buffer */
638             DISPLAY("Loading %s...       \r", inFileName);
639             {   size_t const readSize = fread(origBuff, 1, benchedSize, inFile);
640                 fclose(inFile);
641                 if (readSize != benchedSize) {
642                     DISPLAY("\nError: problem reading file '%s' !!    \n", inFileName);
643                     free(origBuff);
644                     return 13;
645             }   }
646
647             /* bench */
648             DISPLAY("\r%70s\r", "");   /* blank line */
649             DISPLAY(" %s : \n", inFileName);
650             if (benchNb) {
651                 benchMem(benchNb, origBuff, benchedSize, cLevel, cparams);
652             } else {
653                 for (benchNb=0; benchNb<100; benchNb++) {
654                     benchMem(benchNb, origBuff, benchedSize, cLevel, cparams);
655             }   }
656
657             free(origBuff);
658     }   }
659
660     return 0;
661 }
662
663
664
665 /*_*******************************************************
666 *  Argument Parsing
667 *********************************************************/
668
669 #define ERROR_OUT(msg) { DISPLAY("%s \n", msg); exit(1); }
670
671 static unsigned readU32FromChar(const char** stringPtr)
672 {
673     const char errorMsg[] = "error: numeric value too large";
674     unsigned result = 0;
675     while ((**stringPtr >='0') && (**stringPtr <='9')) {
676         unsigned const max = (((unsigned)(-1)) / 10) - 1;
677         if (result > max) ERROR_OUT(errorMsg);
678         result *= 10;
679         result += (unsigned)(**stringPtr - '0');
680         (*stringPtr)++ ;
681     }
682     if ((**stringPtr=='K') || (**stringPtr=='M')) {
683         unsigned const maxK = ((unsigned)(-1)) >> 10;
684         if (result > maxK) ERROR_OUT(errorMsg);
685         result <<= 10;
686         if (**stringPtr=='M') {
687             if (result > maxK) ERROR_OUT(errorMsg);
688             result <<= 10;
689         }
690         (*stringPtr)++;  /* skip `K` or `M` */
691         if (**stringPtr=='i') (*stringPtr)++;
692         if (**stringPtr=='B') (*stringPtr)++;
693     }
694     return result;
695 }
696
697 static int longCommandWArg(const char** stringPtr, const char* longCommand)
698 {
699     size_t const comSize = strlen(longCommand);
700     int const result = !strncmp(*stringPtr, longCommand, comSize);
701     if (result) *stringPtr += comSize;
702     return result;
703 }
704
705
706 /*_*******************************************************
707 *  Command line
708 *********************************************************/
709
710 static int usage(const char* exename)
711 {
712     DISPLAY( "Usage :\n");
713     DISPLAY( "      %s [arg] file1 file2 ... fileX\n", exename);
714     DISPLAY( "Arguments :\n");
715     DISPLAY( " -H/-h  : Help (this text + advanced options)\n");
716     return 0;
717 }
718
719 static int usage_advanced(const char* exename)
720 {
721     usage(exename);
722     DISPLAY( "\nAdvanced options :\n");
723     DISPLAY( " -b#    : test only function # \n");
724     DISPLAY( " -l#    : benchmark functions at that compression level (default : %i)\n", DEFAULT_CLEVEL);
725     DISPLAY( " --zstd : custom parameter selection. Format same as zstdcli \n");
726     DISPLAY( " -P#    : sample compressibility (default : %.1f%%)\n", COMPRESSIBILITY_DEFAULT * 100);
727     DISPLAY( " -B#    : sample size (default : %u)\n", (unsigned)kSampleSizeDefault);
728     DISPLAY( " -i#    : iteration loops [1-9](default : %i)\n", NBLOOPS);
729     return 0;
730 }
731
732 static int badusage(const char* exename)
733 {
734     DISPLAY("Wrong parameters\n");
735     usage(exename);
736     return 1;
737 }
738
739 int main(int argc, const char** argv)
740 {
741     int argNb, filenamesStart=0, result;
742     const char* const exename = argv[0];
743     const char* input_filename = NULL;
744     U32 benchNb = 0, main_pause = 0;
745     int cLevel = DEFAULT_CLEVEL;
746     ZSTD_compressionParameters cparams = ZSTD_getCParams(cLevel, 0, 0);
747     size_t sampleSize = kSampleSizeDefault;
748     double compressibility = COMPRESSIBILITY_DEFAULT;
749
750     DISPLAY(WELCOME_MESSAGE);
751     if (argc<1) return badusage(exename);
752
753     for (argNb=1; argNb<argc; argNb++) {
754         const char* argument = argv[argNb];
755         CONTROL(argument != NULL);
756
757         if (longCommandWArg(&argument, "--zstd=")) {
758             for ( ; ;) {
759                 if (longCommandWArg(&argument, "windowLog=") || longCommandWArg(&argument, "wlog=")) { cparams.windowLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
760                 if (longCommandWArg(&argument, "chainLog=") || longCommandWArg(&argument, "clog=")) { cparams.chainLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
761                 if (longCommandWArg(&argument, "hashLog=") || longCommandWArg(&argument, "hlog=")) { cparams.hashLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
762                 if (longCommandWArg(&argument, "searchLog=") || longCommandWArg(&argument, "slog=")) { cparams.searchLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
763                 if (longCommandWArg(&argument, "minMatch=") || longCommandWArg(&argument, "mml=")) { cparams.minMatch = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
764                 if (longCommandWArg(&argument, "targetLength=") || longCommandWArg(&argument, "tlen=")) { cparams.targetLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
765                 if (longCommandWArg(&argument, "strategy=") || longCommandWArg(&argument, "strat=")) { cparams.strategy = (ZSTD_strategy)(readU32FromChar(&argument)); if (argument[0]==',') { argument++; continue; } else break; }
766                 if (longCommandWArg(&argument, "level=") || longCommandWArg(&argument, "lvl=")) { cLevel = (int)readU32FromChar(&argument); cparams = ZSTD_getCParams(cLevel, 0, 0); if (argument[0]==',') { argument++; continue; } else break; }
767                 DISPLAY("invalid compression parameter \n");
768                 return 1;
769             }
770
771             /* check end of string */
772             if (argument[0] != 0) {
773                 DISPLAY("invalid --zstd= format \n");
774                 return 1;
775             } else {
776                 continue;
777             }
778
779         } else if (argument[0]=='-') { /* Commands (note : aggregated commands are allowed) */
780             argument++;
781             while (argument[0]!=0) {
782
783                 switch(argument[0])
784                 {
785                     /* Display help on usage */
786                 case 'h':
787                 case 'H': return usage_advanced(exename);
788
789                     /* Pause at the end (hidden option) */
790                 case 'p': main_pause = 1; break;
791
792                     /* Select specific algorithm to bench */
793                 case 'b':
794                     argument++;
795                     benchNb = readU32FromChar(&argument);
796                     break;
797
798                     /* Select compression level to use */
799                 case 'l':
800                     argument++;
801                     cLevel = (int)readU32FromChar(&argument);
802                     cparams = ZSTD_getCParams(cLevel, 0, 0);
803                     break;
804
805                     /* Select compressibility of synthetic sample */
806                 case 'P':
807                     argument++;
808                     compressibility = (double)readU32FromChar(&argument) / 100.;
809                     break;
810
811                     /* Select size of synthetic sample */
812                 case 'B':
813                     argument++;
814                     sampleSize = (size_t)readU32FromChar(&argument);
815                     break;
816
817                     /* Modify Nb Iterations */
818                 case 'i':
819                     argument++;
820                     g_nbIterations = readU32FromChar(&argument);
821                     break;
822
823                     /* Unknown command */
824                 default : return badusage(exename);
825                 }
826             }
827             continue;
828         }
829
830         /* first provided filename is input */
831         if (!input_filename) { input_filename=argument; filenamesStart=argNb; continue; }
832     }
833
834
835
836     if (filenamesStart==0)   /* no input file */
837         result = benchSample(benchNb, sampleSize, compressibility, cLevel, cparams);
838     else
839         result = benchFiles(benchNb, argv+filenamesStart, argc-filenamesStart, cLevel, cparams);
840
841     if (main_pause) { int unused; printf("press enter...\n"); unused = getchar(); (void)unused; }
842
843     return result;
844 }