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