2 * Copyright (c) 2015-present, Yann Collet, Facebook, Inc.
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.
12 /*_************************************
14 **************************************/
15 #include "util.h" /* Compiler options, UTIL_GetFileSize */
16 #include <stdlib.h> /* malloc */
17 #include <stdio.h> /* fprintf, fopen, ftello64 */
19 #include "mem.h" /* U32 */
20 #ifndef ZSTD_DLL_IMPORT
21 #include "zstd_internal.h" /* ZSTD_blockHeaderSize, blockType_e, KB, MB */
26 typedef enum { bt_raw
, bt_rle
, bt_compressed
, bt_reserved
} blockType_e
;
28 #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressBegin, ZSTD_compressContinue, etc. */
29 #include "zstd.h" /* ZSTD_versionString */
30 #include "util.h" /* time functions */
34 /*_************************************
36 **************************************/
37 #define PROGRAM_DESCRIPTION "Zstandard speed analyzer"
38 #define AUTHOR "Yann Collet"
39 #define WELCOME_MESSAGE "*** %s %s %i-bits, by %s (%s) ***\n", PROGRAM_DESCRIPTION, ZSTD_versionString(), (int)(sizeof(void*)*8), AUTHOR, __DATE__
44 #define KNUTH 2654435761U
45 #define MAX_MEM (1984 MB)
47 #define COMPRESSIBILITY_DEFAULT 0.50
48 static const size_t g_sampleSize
= 10000000;
51 /*_************************************
53 **************************************/
54 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
57 /*_************************************
58 * Benchmark Parameters
59 **************************************/
60 static U32 g_nbIterations
= NBLOOPS
;
61 static double g_compressibility
= COMPRESSIBILITY_DEFAULT
;
63 static void BMK_SetNbIterations(U32 nbLoops
)
65 g_nbIterations
= nbLoops
;
66 DISPLAY("- %i iterations -\n", g_nbIterations
);
70 /*_*******************************************************
72 *********************************************************/
73 static size_t BMK_findMaxMem(U64 requiredMem
)
75 size_t const step
= 64 MB
;
78 requiredMem
= (((requiredMem
>> 26) + 1) << 26);
79 if (requiredMem
> MAX_MEM
) requiredMem
= MAX_MEM
;
83 testmem
= malloc ((size_t)requiredMem
);
88 return (size_t) requiredMem
;
92 /*_*******************************************************
94 *********************************************************/
95 size_t local_ZSTD_compress(void* dst
, size_t dstSize
, void* buff2
, const void* src
, size_t srcSize
)
98 return ZSTD_compress(dst
, dstSize
, src
, srcSize
, 1);
101 static size_t g_cSize
= 0;
102 size_t local_ZSTD_decompress(void* dst
, size_t dstSize
, void* buff2
, const void* src
, size_t srcSize
)
104 (void)src
; (void)srcSize
;
105 return ZSTD_decompress(dst
, dstSize
, buff2
, g_cSize
);
108 static ZSTD_DCtx
* g_zdc
= NULL
;
110 #ifndef ZSTD_DLL_IMPORT
111 extern size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx
* ctx
, const void* src
, size_t srcSize
);
112 size_t local_ZSTD_decodeLiteralsBlock(void* dst
, size_t dstSize
, void* buff2
, const void* src
, size_t srcSize
)
114 (void)src
; (void)srcSize
; (void)dst
; (void)dstSize
;
115 return ZSTD_decodeLiteralsBlock((ZSTD_DCtx
*)g_zdc
, buff2
, g_cSize
);
118 extern size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx
* dctx
, int* nbSeq
, const void* src
, size_t srcSize
);
119 size_t local_ZSTD_decodeSeqHeaders(void* dst
, size_t dstSize
, void* buff2
, const void* src
, size_t srcSize
)
122 (void)src
; (void)srcSize
; (void)dst
; (void)dstSize
;
123 return ZSTD_decodeSeqHeaders(g_zdc
, &nbSeq
, buff2
, g_cSize
);
127 static ZSTD_CStream
* g_cstream
= NULL
;
128 size_t local_ZSTD_compressStream(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
130 ZSTD_outBuffer buffOut
;
131 ZSTD_inBuffer buffIn
;
133 ZSTD_initCStream(g_cstream
, 1);
135 buffOut
.size
= dstCapacity
;
138 buffIn
.size
= srcSize
;
140 ZSTD_compressStream(g_cstream
, &buffOut
, &buffIn
);
141 ZSTD_endStream(g_cstream
, &buffOut
);
145 static size_t local_ZSTD_compress_generic_end(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
147 ZSTD_outBuffer buffOut
;
148 ZSTD_inBuffer buffIn
;
150 ZSTD_CCtx_setParameter(g_cstream
, ZSTD_p_compressionLevel
, 1);
152 buffOut
.size
= dstCapacity
;
155 buffIn
.size
= srcSize
;
157 ZSTD_compress_generic(g_cstream
, &buffOut
, &buffIn
, ZSTD_e_end
);
161 static size_t local_ZSTD_compress_generic_continue(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
163 ZSTD_outBuffer buffOut
;
164 ZSTD_inBuffer buffIn
;
166 ZSTD_CCtx_setParameter(g_cstream
, ZSTD_p_compressionLevel
, 1);
168 buffOut
.size
= dstCapacity
;
171 buffIn
.size
= srcSize
;
173 ZSTD_compress_generic(g_cstream
, &buffOut
, &buffIn
, ZSTD_e_continue
);
174 ZSTD_compress_generic(g_cstream
, &buffOut
, &buffIn
, ZSTD_e_end
);
178 static size_t local_ZSTD_compress_generic_T2_end(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
180 ZSTD_outBuffer buffOut
;
181 ZSTD_inBuffer buffIn
;
183 ZSTD_CCtx_setParameter(g_cstream
, ZSTD_p_compressionLevel
, 1);
184 ZSTD_CCtx_setParameter(g_cstream
, ZSTD_p_nbThreads
, 2);
186 buffOut
.size
= dstCapacity
;
189 buffIn
.size
= srcSize
;
191 while (ZSTD_compress_generic(g_cstream
, &buffOut
, &buffIn
, ZSTD_e_end
)) {}
195 static size_t local_ZSTD_compress_generic_T2_continue(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
197 ZSTD_outBuffer buffOut
;
198 ZSTD_inBuffer buffIn
;
200 ZSTD_CCtx_setParameter(g_cstream
, ZSTD_p_compressionLevel
, 1);
201 ZSTD_CCtx_setParameter(g_cstream
, ZSTD_p_nbThreads
, 2);
203 buffOut
.size
= dstCapacity
;
206 buffIn
.size
= srcSize
;
208 ZSTD_compress_generic(g_cstream
, &buffOut
, &buffIn
, ZSTD_e_continue
);
209 while(ZSTD_compress_generic(g_cstream
, &buffOut
, &buffIn
, ZSTD_e_end
)) {}
213 static ZSTD_DStream
* g_dstream
= NULL
;
214 static size_t local_ZSTD_decompressStream(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
216 ZSTD_outBuffer buffOut
;
217 ZSTD_inBuffer buffIn
;
218 (void)src
; (void)srcSize
;
219 ZSTD_initDStream(g_dstream
);
221 buffOut
.size
= dstCapacity
;
224 buffIn
.size
= g_cSize
;
226 ZSTD_decompressStream(g_dstream
, &buffOut
, &buffIn
);
230 static ZSTD_CCtx
* g_zcc
= NULL
;
232 #ifndef ZSTD_DLL_IMPORT
233 size_t local_ZSTD_compressContinue(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
236 ZSTD_compressBegin(g_zcc
, 1);
237 return ZSTD_compressEnd(g_zcc
, dst
, dstCapacity
, src
, srcSize
);
240 #define FIRST_BLOCK_SIZE 8
241 size_t local_ZSTD_compressContinue_extDict(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
243 BYTE firstBlockBuf
[FIRST_BLOCK_SIZE
];
246 memcpy(firstBlockBuf
, src
, FIRST_BLOCK_SIZE
);
247 ZSTD_compressBegin(g_zcc
, 1);
249 { size_t const compressResult
= ZSTD_compressContinue(g_zcc
, dst
, dstCapacity
, firstBlockBuf
, FIRST_BLOCK_SIZE
);
250 if (ZSTD_isError(compressResult
)) { DISPLAY("local_ZSTD_compressContinue_extDict error : %s\n", ZSTD_getErrorName(compressResult
)); return compressResult
; }
251 dst
= (BYTE
*)dst
+ compressResult
;
252 dstCapacity
-= compressResult
;
254 return ZSTD_compressEnd(g_zcc
, dst
, dstCapacity
, (const BYTE
*)src
+ FIRST_BLOCK_SIZE
, srcSize
- FIRST_BLOCK_SIZE
);
257 size_t local_ZSTD_decompressContinue(void* dst
, size_t dstCapacity
, void* buff2
, const void* src
, size_t srcSize
)
259 size_t regeneratedSize
= 0;
260 const BYTE
* ip
= (const BYTE
*)buff2
;
261 const BYTE
* const iend
= ip
+ g_cSize
;
262 BYTE
* op
= (BYTE
*)dst
;
263 size_t remainingCapacity
= dstCapacity
;
265 (void)src
; (void)srcSize
;
266 ZSTD_decompressBegin(g_zdc
);
268 size_t const iSize
= ZSTD_nextSrcSizeToDecompress(g_zdc
);
269 size_t const decodedSize
= ZSTD_decompressContinue(g_zdc
, op
, remainingCapacity
, ip
, iSize
);
271 regeneratedSize
+= decodedSize
;
273 remainingCapacity
-= decodedSize
;
276 return regeneratedSize
;
281 /*_*******************************************************
283 *********************************************************/
284 static size_t benchMem(const void* src
, size_t srcSize
, U32 benchNb
)
287 size_t const dstBuffSize
= ZSTD_compressBound(srcSize
);
289 const char* benchName
;
290 size_t (*benchFunction
)(void* dst
, size_t dstSize
, void* verifBuff
, const void* src
, size_t srcSize
);
291 double bestTime
= 100000000.;
297 benchFunction
= local_ZSTD_compress
; benchName
= "compress(1)";
300 benchFunction
= local_ZSTD_decompress
; benchName
= "decompress";
302 #ifndef ZSTD_DLL_IMPORT
304 benchFunction
= local_ZSTD_compressContinue
; benchName
= "compressContinue(1)";
307 benchFunction
= local_ZSTD_compressContinue_extDict
; benchName
= "compressContinue_extDict";
310 benchFunction
= local_ZSTD_decompressContinue
; benchName
= "decompressContinue";
313 benchFunction
= local_ZSTD_decodeLiteralsBlock
; benchName
= "decodeLiteralsBlock";
316 benchFunction
= local_ZSTD_decodeSeqHeaders
; benchName
= "decodeSeqHeaders";
320 benchFunction
= local_ZSTD_compressStream
; benchName
= "compressStream(1)";
323 benchFunction
= local_ZSTD_decompressStream
; benchName
= "decompressStream";
326 benchFunction
= local_ZSTD_compress_generic_continue
; benchName
= "compress_generic, continue";
329 benchFunction
= local_ZSTD_compress_generic_end
; benchName
= "compress_generic, end";
332 benchFunction
= local_ZSTD_compress_generic_T2_continue
; benchName
= "compress_generic, -T2, continue";
335 benchFunction
= local_ZSTD_compress_generic_T2_end
; benchName
= "compress_generic, -T2, end";
342 dstBuff
= (BYTE
*)malloc(dstBuffSize
);
343 buff2
= malloc(dstBuffSize
);
344 if ((!dstBuff
) || (!buff2
)) {
345 DISPLAY("\nError: not enough memory!\n");
346 free(dstBuff
); free(buff2
);
349 if (g_zcc
==NULL
) g_zcc
= ZSTD_createCCtx();
350 if (g_zdc
==NULL
) g_zdc
= ZSTD_createDCtx();
351 if (g_cstream
==NULL
) g_cstream
= ZSTD_createCStream();
352 if (g_dstream
==NULL
) g_dstream
= ZSTD_createDStream();
358 g_cSize
= ZSTD_compress(buff2
, dstBuffSize
, src
, srcSize
, 1);
360 #ifndef ZSTD_DLL_IMPORT
362 g_cSize
= ZSTD_compress(buff2
, dstBuffSize
, src
, srcSize
, 1);
364 case 31: /* ZSTD_decodeLiteralsBlock */
365 { blockProperties_t bp
;
366 ZSTD_frameHeader zfp
;
367 size_t frameHeaderSize
, skippedSize
;
368 g_cSize
= ZSTD_compress(dstBuff
, dstBuffSize
, src
, srcSize
, 1);
369 frameHeaderSize
= ZSTD_getFrameHeader(&zfp
, dstBuff
, ZSTD_frameHeaderSize_min
);
370 if (frameHeaderSize
==0) frameHeaderSize
= ZSTD_frameHeaderSize_min
;
371 ZSTD_getcBlockSize(dstBuff
+frameHeaderSize
, dstBuffSize
, &bp
); /* Get 1st block type */
372 if (bp
.blockType
!= bt_compressed
) {
373 DISPLAY("ZSTD_decodeLiteralsBlock : impossible to test on this sample (not compressible)\n");
376 skippedSize
= frameHeaderSize
+ ZSTD_blockHeaderSize
;
377 memcpy(buff2
, dstBuff
+skippedSize
, g_cSize
-skippedSize
);
378 srcSize
= srcSize
> 128 KB
? 128 KB
: srcSize
; /* speed relative to block */
379 ZSTD_decompressBegin(g_zdc
);
382 case 32: /* ZSTD_decodeSeqHeaders */
383 { blockProperties_t bp
;
384 ZSTD_frameHeader zfp
;
385 const BYTE
* ip
= dstBuff
;
387 size_t frameHeaderSize
, cBlockSize
;
388 ZSTD_compress(dstBuff
, dstBuffSize
, src
, srcSize
, 1); /* it would be better to use direct block compression here */
389 g_cSize
= ZSTD_compress(dstBuff
, dstBuffSize
, src
, srcSize
, 1);
390 frameHeaderSize
= ZSTD_getFrameHeader(&zfp
, dstBuff
, ZSTD_frameHeaderSize_min
);
391 if (frameHeaderSize
==0) frameHeaderSize
= ZSTD_frameHeaderSize_min
;
392 ip
+= frameHeaderSize
; /* Skip frame Header */
393 cBlockSize
= ZSTD_getcBlockSize(ip
, dstBuffSize
, &bp
); /* Get 1st block type */
394 if (bp
.blockType
!= bt_compressed
) {
395 DISPLAY("ZSTD_decodeSeqHeaders : impossible to test on this sample (not compressible)\n");
398 iend
= ip
+ ZSTD_blockHeaderSize
+ cBlockSize
; /* End of first block */
399 ip
+= ZSTD_blockHeaderSize
; /* skip block header */
400 ZSTD_decompressBegin(g_zdc
);
401 ip
+= ZSTD_decodeLiteralsBlock(g_zdc
, ip
, iend
-ip
); /* skip literal segment */
403 memcpy(buff2
, ip
, g_cSize
); /* copy rest of block (it starts by SeqHeader) */
404 srcSize
= srcSize
> 128 KB
? 128 KB
: srcSize
; /* speed relative to block */
412 g_cSize
= ZSTD_compress(buff2
, dstBuffSize
, src
, srcSize
, 1);
416 /* by convention, test functions can be added > 100 */
421 { size_t i
; for (i
=0; i
<dstBuffSize
; i
++) dstBuff
[i
]=(BYTE
)i
; } /* warming up memory */
424 # define TIME_SEC_MICROSEC (1*1000000ULL) /* 1 second */
425 U64
const clockLoop
= TIMELOOP_S
* TIME_SEC_MICROSEC
;
426 DISPLAY("%2i- %-30.30s : \r", benchNb
, benchName
);
427 for (loopNb
= 1; loopNb
<= g_nbIterations
; loopNb
++) {
428 UTIL_time_t clockStart
;
429 size_t benchResult
=0;
432 UTIL_sleepMilli(1); /* give processor time to other processes */
433 UTIL_waitForNextTick();
434 clockStart
= UTIL_getTime();
435 for (nbRounds
=0; UTIL_clockSpanMicro(clockStart
) < clockLoop
; nbRounds
++) {
436 benchResult
= benchFunction(dstBuff
, dstBuffSize
, buff2
, src
, srcSize
);
437 if (ZSTD_isError(benchResult
)) { DISPLAY("ERROR ! %s() => %s !! \n", benchName
, ZSTD_getErrorName(benchResult
)); exit(1); }
439 { U64
const clockSpanMicro
= UTIL_clockSpanMicro(clockStart
);
440 double const averageTime
= (double)clockSpanMicro
/ TIME_SEC_MICROSEC
/ nbRounds
;
441 if (averageTime
< bestTime
) bestTime
= averageTime
;
442 DISPLAY("%2i- %-30.30s : %7.1f MB/s (%9u)\r", loopNb
, benchName
, (double)srcSize
/ (1 MB
) / bestTime
, (U32
)benchResult
);
444 DISPLAY("%2u\n", benchNb
);
449 ZSTD_freeCCtx(g_zcc
); g_zcc
=NULL
;
450 ZSTD_freeDCtx(g_zdc
); g_zdc
=NULL
;
451 ZSTD_freeCStream(g_cstream
); g_cstream
=NULL
;
452 ZSTD_freeDStream(g_dstream
); g_dstream
=NULL
;
457 static int benchSample(U32 benchNb
)
459 size_t const benchedSize
= g_sampleSize
;
460 const char* name
= "Sample 10MiB";
463 void* origBuff
= malloc(benchedSize
);
464 if (!origBuff
) { DISPLAY("\nError: not enough memory!\n"); return 12; }
467 RDG_genBuffer(origBuff
, benchedSize
, g_compressibility
, 0.0, 0);
470 DISPLAY("\r%79s\r", "");
471 DISPLAY(" %s : \n", name
);
473 benchMem(origBuff
, benchedSize
, benchNb
);
475 for (benchNb
=0; benchNb
<100; benchNb
++) benchMem(origBuff
, benchedSize
, benchNb
);
482 static int benchFiles(const char** fileNamesTable
, const int nbFiles
, U32 benchNb
)
484 /* Loop for each file */
486 for (fileIdx
=0; fileIdx
<nbFiles
; fileIdx
++) {
487 const char* inFileName
= fileNamesTable
[fileIdx
];
488 FILE* inFile
= fopen( inFileName
, "rb" );
493 /* Check file existence */
494 if (inFile
==NULL
) { DISPLAY( "Pb opening %s\n", inFileName
); return 11; }
496 /* Memory allocation & restrictions */
497 inFileSize
= UTIL_getFileSize(inFileName
);
498 benchedSize
= BMK_findMaxMem(inFileSize
*3) / 3;
499 if ((U64
)benchedSize
> inFileSize
) benchedSize
= (size_t)inFileSize
;
500 if (benchedSize
< inFileSize
)
501 DISPLAY("Not enough memory for '%s' full size; testing %u MB only...\n", inFileName
, (U32
)(benchedSize
>>20));
504 origBuff
= malloc(benchedSize
);
505 if (!origBuff
) { DISPLAY("\nError: not enough memory!\n"); fclose(inFile
); return 12; }
507 /* Fill input buffer */
508 DISPLAY("Loading %s... \r", inFileName
);
510 size_t readSize
= fread(origBuff
, 1, benchedSize
, inFile
);
512 if (readSize
!= benchedSize
) {
513 DISPLAY("\nError: problem reading file '%s' !! \n", inFileName
);
519 DISPLAY("\r%79s\r", "");
520 DISPLAY(" %s : \n", inFileName
);
522 benchMem(origBuff
, benchedSize
, benchNb
);
524 for (benchNb
=0; benchNb
<100; benchNb
++) benchMem(origBuff
, benchedSize
, benchNb
);
533 static int usage(const char* exename
)
535 DISPLAY( "Usage :\n");
536 DISPLAY( " %s [arg] file1 file2 ... fileX\n", exename
);
537 DISPLAY( "Arguments :\n");
538 DISPLAY( " -H/-h : Help (this text + advanced options)\n");
542 static int usage_advanced(const char* exename
)
545 DISPLAY( "\nAdvanced options :\n");
546 DISPLAY( " -b# : test only function # \n");
547 DISPLAY( " -i# : iteration loops [1-9](default : %i)\n", NBLOOPS
);
548 DISPLAY( " -P# : sample compressibility (default : %.1f%%)\n", COMPRESSIBILITY_DEFAULT
* 100);
552 static int badusage(const char* exename
)
554 DISPLAY("Wrong parameters\n");
559 int main(int argc
, const char** argv
)
561 int i
, filenamesStart
=0, result
;
562 const char* exename
= argv
[0];
563 const char* input_filename
= NULL
;
564 U32 benchNb
= 0, main_pause
= 0;
566 DISPLAY(WELCOME_MESSAGE
);
567 if (argc
<1) return badusage(exename
);
569 for(i
=1; i
<argc
; i
++) {
570 const char* argument
= argv
[i
];
571 if(!argument
) continue; /* Protection if argument empty */
573 /* Commands (note : aggregated commands are allowed) */
574 if (argument
[0]=='-') {
576 while (argument
[1]!=0) {
581 /* Display help on usage */
583 case 'H': return usage_advanced(exename
);
585 /* Pause at the end (hidden option) */
586 case 'p': main_pause
= 1; break;
588 /* Select specific algorithm to bench */
591 while ((argument
[1]>= '0') && (argument
[1]<= '9')) {
593 benchNb
+= argument
[1] - '0';
598 /* Modify Nb Iterations */
600 if ((argument
[1] >='0') && (argument
[1] <='9')) {
601 int iters
= argument
[1] - '0';
602 BMK_SetNbIterations(iters
);
607 /* Select compressibility of synthetic sample */
610 while ((argument
[1]>= '0') && (argument
[1]<= '9')) {
612 proba32
+= argument
[1] - '0';
615 g_compressibility
= (double)proba32
/ 100.;
619 /* Unknown command */
620 default : return badusage(exename
);
626 /* first provided filename is input */
627 if (!input_filename
) { input_filename
=argument
; filenamesStart
=i
; continue; }
630 if (filenamesStart
==0) /* no input file */
631 result
= benchSample(benchNb
);
633 result
= benchFiles(argv
+filenamesStart
, argc
-filenamesStart
, benchNb
);
635 if (main_pause
) { int unused
; printf("press enter...\n"); unused
= getchar(); (void)unused
; }