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