1*3117ece4Schristos /* 2*3117ece4Schristos * Copyright (c) Meta Platforms, Inc. and affiliates. 3*3117ece4Schristos * All rights reserved. 4*3117ece4Schristos * 5*3117ece4Schristos * This source code is licensed under both the BSD-style license (found in the 6*3117ece4Schristos * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7*3117ece4Schristos * in the COPYING file in the root directory of this source tree). 8*3117ece4Schristos */ 9*3117ece4Schristos 10*3117ece4Schristos 11*3117ece4Schristos /* ************************************* 12*3117ece4Schristos * Includes 13*3117ece4Schristos ***************************************/ 14*3117ece4Schristos #include "util.h" /* Compiler options, UTIL_GetFileSize, UTIL_sleep */ 15*3117ece4Schristos #include <stdlib.h> /* malloc, free */ 16*3117ece4Schristos #include <string.h> /* memset */ 17*3117ece4Schristos #include <stdio.h> /* fprintf, fopen, ftello64 */ 18*3117ece4Schristos #include <time.h> /* clock_t, clock, CLOCKS_PER_SEC */ 19*3117ece4Schristos #include <ctype.h> /* toupper */ 20*3117ece4Schristos #include <errno.h> /* errno */ 21*3117ece4Schristos 22*3117ece4Schristos #include "timefn.h" /* UTIL_time_t, UTIL_getTime, UTIL_clockSpanMicro, UTIL_waitForNextTick */ 23*3117ece4Schristos #include "mem.h" 24*3117ece4Schristos #define ZSTD_STATIC_LINKING_ONLY 25*3117ece4Schristos #include "zstd.h" 26*3117ece4Schristos #include "datagen.h" /* RDG_genBuffer */ 27*3117ece4Schristos #include "xxhash.h" 28*3117ece4Schristos 29*3117ece4Schristos #include "../zstd_zlibwrapper.h" 30*3117ece4Schristos 31*3117ece4Schristos 32*3117ece4Schristos 33*3117ece4Schristos /*-************************************ 34*3117ece4Schristos * Tuning parameters 35*3117ece4Schristos **************************************/ 36*3117ece4Schristos #ifndef ZSTDCLI_CLEVEL_DEFAULT 37*3117ece4Schristos # define ZSTDCLI_CLEVEL_DEFAULT 3 38*3117ece4Schristos #endif 39*3117ece4Schristos 40*3117ece4Schristos 41*3117ece4Schristos /*-************************************ 42*3117ece4Schristos * Constants 43*3117ece4Schristos **************************************/ 44*3117ece4Schristos #define COMPRESSOR_NAME "Zstandard wrapper for zlib command line interface" 45*3117ece4Schristos #ifndef ZSTD_VERSION 46*3117ece4Schristos # define ZSTD_VERSION "v" ZSTD_VERSION_STRING 47*3117ece4Schristos #endif 48*3117ece4Schristos #define AUTHOR "Yann Collet" 49*3117ece4Schristos #define WELCOME_MESSAGE "*** %s %i-bits %s, by %s ***\n", COMPRESSOR_NAME, (int)(sizeof(size_t)*8), ZSTD_VERSION, AUTHOR 50*3117ece4Schristos 51*3117ece4Schristos #ifndef ZSTD_GIT_COMMIT 52*3117ece4Schristos # define ZSTD_GIT_COMMIT_STRING "" 53*3117ece4Schristos #else 54*3117ece4Schristos # define ZSTD_GIT_COMMIT_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_GIT_COMMIT) 55*3117ece4Schristos #endif 56*3117ece4Schristos 57*3117ece4Schristos #define NBLOOPS 3 58*3117ece4Schristos #define TIMELOOP_MICROSEC 1*1000000ULL /* 1 second */ 59*3117ece4Schristos #define ACTIVEPERIOD_MICROSEC 70*1000000ULL /* 70 seconds */ 60*3117ece4Schristos #define COOLPERIOD_SEC 10 61*3117ece4Schristos 62*3117ece4Schristos #define KB *(1 <<10) 63*3117ece4Schristos #define MB *(1 <<20) 64*3117ece4Schristos #define GB *(1U<<30) 65*3117ece4Schristos 66*3117ece4Schristos static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31)); 67*3117ece4Schristos 68*3117ece4Schristos static U32 g_compressibilityDefault = 50; 69*3117ece4Schristos 70*3117ece4Schristos 71*3117ece4Schristos /* ************************************* 72*3117ece4Schristos * console display 73*3117ece4Schristos ***************************************/ 74*3117ece4Schristos #define DEFAULT_DISPLAY_LEVEL 2 75*3117ece4Schristos #define DISPLAY(...) fprintf(displayOut, __VA_ARGS__) 76*3117ece4Schristos #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } 77*3117ece4Schristos static unsigned g_displayLevel = DEFAULT_DISPLAY_LEVEL; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */ 78*3117ece4Schristos static FILE* displayOut; 79*3117ece4Schristos 80*3117ece4Schristos #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ 81*3117ece4Schristos if ((clock() - g_time > refreshRate) || (g_displayLevel>=4)) \ 82*3117ece4Schristos { g_time = clock(); DISPLAY(__VA_ARGS__); \ 83*3117ece4Schristos if (g_displayLevel>=4) fflush(displayOut); } } 84*3117ece4Schristos static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; 85*3117ece4Schristos static clock_t g_time = 0; 86*3117ece4Schristos 87*3117ece4Schristos 88*3117ece4Schristos /* ************************************* 89*3117ece4Schristos * Exceptions 90*3117ece4Schristos ***************************************/ 91*3117ece4Schristos #ifndef DEBUG 92*3117ece4Schristos # define DEBUG 0 93*3117ece4Schristos #endif 94*3117ece4Schristos #define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); } 95*3117ece4Schristos #define EXM_THROW(error, ...) \ 96*3117ece4Schristos { \ 97*3117ece4Schristos DEBUGOUTPUT("Error defined at %s, line %i : \n", __FILE__, __LINE__); \ 98*3117ece4Schristos DISPLAYLEVEL(1, "Error %i : ", error); \ 99*3117ece4Schristos DISPLAYLEVEL(1, __VA_ARGS__); \ 100*3117ece4Schristos DISPLAYLEVEL(1, "\n"); \ 101*3117ece4Schristos exit(error); \ 102*3117ece4Schristos } 103*3117ece4Schristos 104*3117ece4Schristos 105*3117ece4Schristos /* ************************************* 106*3117ece4Schristos * Benchmark Parameters 107*3117ece4Schristos ***************************************/ 108*3117ece4Schristos static unsigned g_nbIterations = NBLOOPS; 109*3117ece4Schristos static size_t g_blockSize = 0; 110*3117ece4Schristos int g_additionalParam = 0; 111*3117ece4Schristos 112*3117ece4Schristos static void BMK_setNotificationLevel(unsigned level) { g_displayLevel=level; } 113*3117ece4Schristos 114*3117ece4Schristos static void BMK_setAdditionalParam(int additionalParam) { g_additionalParam=additionalParam; } 115*3117ece4Schristos 116*3117ece4Schristos static void BMK_SetNbIterations(unsigned nbLoops) 117*3117ece4Schristos { 118*3117ece4Schristos g_nbIterations = nbLoops; 119*3117ece4Schristos DISPLAYLEVEL(3, "- test >= %u seconds per compression / decompression -\n", g_nbIterations); 120*3117ece4Schristos } 121*3117ece4Schristos 122*3117ece4Schristos static void BMK_SetBlockSize(size_t blockSize) 123*3117ece4Schristos { 124*3117ece4Schristos g_blockSize = blockSize; 125*3117ece4Schristos DISPLAYLEVEL(2, "using blocks of size %u KB \n", (unsigned)(blockSize>>10)); 126*3117ece4Schristos } 127*3117ece4Schristos 128*3117ece4Schristos 129*3117ece4Schristos /* ******************************************************** 130*3117ece4Schristos * Bench functions 131*3117ece4Schristos **********************************************************/ 132*3117ece4Schristos #undef MIN 133*3117ece4Schristos #undef MAX 134*3117ece4Schristos #define MIN(a,b) ((a)<(b) ? (a) : (b)) 135*3117ece4Schristos #define MAX(a,b) ((a)>(b) ? (a) : (b)) 136*3117ece4Schristos 137*3117ece4Schristos typedef struct 138*3117ece4Schristos { 139*3117ece4Schristos z_const char* srcPtr; 140*3117ece4Schristos size_t srcSize; 141*3117ece4Schristos char* cPtr; 142*3117ece4Schristos size_t cRoom; 143*3117ece4Schristos size_t cSize; 144*3117ece4Schristos char* resPtr; 145*3117ece4Schristos size_t resSize; 146*3117ece4Schristos } blockParam_t; 147*3117ece4Schristos 148*3117ece4Schristos typedef enum { BMK_ZSTD, BMK_ZSTD_STREAM, BMK_ZLIB, BMK_ZWRAP_ZLIB, BMK_ZWRAP_ZSTD, BMK_ZLIB_REUSE, BMK_ZWRAP_ZLIB_REUSE, BMK_ZWRAP_ZSTD_REUSE } BMK_compressor; 149*3117ece4Schristos 150*3117ece4Schristos 151*3117ece4Schristos static int BMK_benchMem(z_const void* srcBuffer, size_t srcSize, 152*3117ece4Schristos const char* displayName, int cLevel, 153*3117ece4Schristos const size_t* fileSizes, U32 nbFiles, 154*3117ece4Schristos const void* dictBuffer, size_t dictBufferSize, BMK_compressor compressor) 155*3117ece4Schristos { 156*3117ece4Schristos size_t const blockSize = (g_blockSize>=32 ? g_blockSize : srcSize) + (!srcSize) /* avoid div by 0 */ ; 157*3117ece4Schristos size_t const avgSize = MIN(g_blockSize, (srcSize / nbFiles)); 158*3117ece4Schristos U32 const maxNbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize) + nbFiles; 159*3117ece4Schristos blockParam_t* const blockTable = (blockParam_t*) malloc(maxNbBlocks * sizeof(blockParam_t)); 160*3117ece4Schristos size_t const maxCompressedSize = ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); /* add some room for safety */ 161*3117ece4Schristos void* const compressedBuffer = malloc(maxCompressedSize); 162*3117ece4Schristos void* const resultBuffer = malloc(srcSize); 163*3117ece4Schristos ZSTD_CCtx* const ctx = ZSTD_createCCtx(); 164*3117ece4Schristos ZSTD_DCtx* const dctx = ZSTD_createDCtx(); 165*3117ece4Schristos U32 nbBlocks; 166*3117ece4Schristos 167*3117ece4Schristos /* checks */ 168*3117ece4Schristos if (!compressedBuffer || !resultBuffer || !blockTable || !ctx || !dctx) 169*3117ece4Schristos EXM_THROW(31, "allocation error : not enough memory"); 170*3117ece4Schristos 171*3117ece4Schristos /* init */ 172*3117ece4Schristos if (strlen(displayName)>17) displayName += strlen(displayName)-17; /* can only display 17 characters */ 173*3117ece4Schristos 174*3117ece4Schristos /* Init blockTable data */ 175*3117ece4Schristos { z_const char* srcPtr = (z_const char*)srcBuffer; 176*3117ece4Schristos char* cPtr = (char*)compressedBuffer; 177*3117ece4Schristos char* resPtr = (char*)resultBuffer; 178*3117ece4Schristos U32 fileNb; 179*3117ece4Schristos for (nbBlocks=0, fileNb=0; fileNb<nbFiles; fileNb++) { 180*3117ece4Schristos size_t remaining = fileSizes[fileNb]; 181*3117ece4Schristos U32 const nbBlocksforThisFile = (U32)((remaining + (blockSize-1)) / blockSize); 182*3117ece4Schristos U32 const blockEnd = nbBlocks + nbBlocksforThisFile; 183*3117ece4Schristos for ( ; nbBlocks<blockEnd; nbBlocks++) { 184*3117ece4Schristos size_t const thisBlockSize = MIN(remaining, blockSize); 185*3117ece4Schristos blockTable[nbBlocks].srcPtr = srcPtr; 186*3117ece4Schristos blockTable[nbBlocks].cPtr = cPtr; 187*3117ece4Schristos blockTable[nbBlocks].resPtr = resPtr; 188*3117ece4Schristos blockTable[nbBlocks].srcSize = thisBlockSize; 189*3117ece4Schristos blockTable[nbBlocks].cRoom = ZSTD_compressBound(thisBlockSize); 190*3117ece4Schristos srcPtr += thisBlockSize; 191*3117ece4Schristos cPtr += blockTable[nbBlocks].cRoom; 192*3117ece4Schristos resPtr += thisBlockSize; 193*3117ece4Schristos remaining -= thisBlockSize; 194*3117ece4Schristos } } } 195*3117ece4Schristos 196*3117ece4Schristos /* warming up memory */ 197*3117ece4Schristos RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1); 198*3117ece4Schristos 199*3117ece4Schristos /* Bench */ 200*3117ece4Schristos { U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL); 201*3117ece4Schristos U64 const crcOrig = XXH64(srcBuffer, srcSize, 0); 202*3117ece4Schristos UTIL_time_t coolTime; 203*3117ece4Schristos U64 const maxTime = (g_nbIterations * TIMELOOP_MICROSEC) + 100; 204*3117ece4Schristos U64 totalCTime=0, totalDTime=0; 205*3117ece4Schristos U32 cCompleted=0, dCompleted=0; 206*3117ece4Schristos # define NB_MARKS 4 207*3117ece4Schristos const char* const marks[NB_MARKS] = { " |", " /", " =", "\\" }; 208*3117ece4Schristos U32 markNb = 0; 209*3117ece4Schristos size_t cSize = 0; 210*3117ece4Schristos double ratio = 0.; 211*3117ece4Schristos 212*3117ece4Schristos coolTime = UTIL_getTime(); 213*3117ece4Schristos DISPLAYLEVEL(2, "\r%79s\r", ""); 214*3117ece4Schristos while (!cCompleted | !dCompleted) { 215*3117ece4Schristos UTIL_time_t clockStart; 216*3117ece4Schristos U64 clockLoop = g_nbIterations ? TIMELOOP_MICROSEC : 1; 217*3117ece4Schristos 218*3117ece4Schristos /* overheat protection */ 219*3117ece4Schristos if (UTIL_clockSpanMicro(coolTime) > ACTIVEPERIOD_MICROSEC) { 220*3117ece4Schristos DISPLAYLEVEL(2, "\rcooling down ... \r"); 221*3117ece4Schristos UTIL_sleep(COOLPERIOD_SEC); 222*3117ece4Schristos coolTime = UTIL_getTime(); 223*3117ece4Schristos } 224*3117ece4Schristos 225*3117ece4Schristos /* Compression */ 226*3117ece4Schristos DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (unsigned)srcSize); 227*3117ece4Schristos if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ 228*3117ece4Schristos 229*3117ece4Schristos UTIL_sleepMilli(1); /* give processor time to other processes */ 230*3117ece4Schristos UTIL_waitForNextTick(); 231*3117ece4Schristos clockStart = UTIL_getTime(); 232*3117ece4Schristos 233*3117ece4Schristos if (!cCompleted) { /* still some time to do compression tests */ 234*3117ece4Schristos U32 nbLoops = 0; 235*3117ece4Schristos if (compressor == BMK_ZSTD) { 236*3117ece4Schristos ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); 237*3117ece4Schristos ZSTD_customMem const cmem = { NULL, NULL, NULL }; 238*3117ece4Schristos ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dct_auto, zparams.cParams, cmem); 239*3117ece4Schristos if (cdict==NULL) EXM_THROW(1, "ZSTD_createCDict_advanced() allocation failure"); 240*3117ece4Schristos 241*3117ece4Schristos do { 242*3117ece4Schristos U32 blockNb; 243*3117ece4Schristos size_t rSize; 244*3117ece4Schristos for (blockNb=0; blockNb<nbBlocks; blockNb++) { 245*3117ece4Schristos if (dictBufferSize) { 246*3117ece4Schristos rSize = ZSTD_compress_usingCDict(ctx, 247*3117ece4Schristos blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, 248*3117ece4Schristos blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, 249*3117ece4Schristos cdict); 250*3117ece4Schristos } else { 251*3117ece4Schristos rSize = ZSTD_compressCCtx (ctx, 252*3117ece4Schristos blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, 253*3117ece4Schristos blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, cLevel); 254*3117ece4Schristos } 255*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_compress_usingCDict() failed : %s", ZSTD_getErrorName(rSize)); 256*3117ece4Schristos blockTable[blockNb].cSize = rSize; 257*3117ece4Schristos } 258*3117ece4Schristos nbLoops++; 259*3117ece4Schristos } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 260*3117ece4Schristos ZSTD_freeCDict(cdict); 261*3117ece4Schristos } else if (compressor == BMK_ZSTD_STREAM) { 262*3117ece4Schristos ZSTD_parameters const zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); 263*3117ece4Schristos ZSTD_inBuffer inBuffer; 264*3117ece4Schristos ZSTD_outBuffer outBuffer; 265*3117ece4Schristos ZSTD_CStream* zbc = ZSTD_createCStream(); 266*3117ece4Schristos size_t rSize; 267*3117ece4Schristos ZSTD_CCtx_params* cctxParams = ZSTD_createCCtxParams(); 268*3117ece4Schristos 269*3117ece4Schristos if (!cctxParams) EXM_THROW(1, "ZSTD_createCCtxParams() allocation failure"); 270*3117ece4Schristos if (zbc == NULL) EXM_THROW(1, "ZSTD_createCStream() allocation failure"); 271*3117ece4Schristos 272*3117ece4Schristos { int initErr = 0; 273*3117ece4Schristos initErr |= ZSTD_isError(ZSTD_CCtx_reset(zbc, ZSTD_reset_session_only)); 274*3117ece4Schristos initErr |= ZSTD_isError(ZSTD_CCtxParams_init_advanced(cctxParams, zparams)); 275*3117ece4Schristos initErr |= ZSTD_isError(ZSTD_CCtx_setParametersUsingCCtxParams(zbc, cctxParams)); 276*3117ece4Schristos initErr |= ZSTD_isError(ZSTD_CCtx_setPledgedSrcSize(zbc, avgSize)); 277*3117ece4Schristos initErr |= ZSTD_isError(ZSTD_CCtx_loadDictionary(zbc, dictBuffer, dictBufferSize)); 278*3117ece4Schristos 279*3117ece4Schristos ZSTD_freeCCtxParams(cctxParams); 280*3117ece4Schristos if (initErr) EXM_THROW(1, "CCtx init failed!"); 281*3117ece4Schristos } 282*3117ece4Schristos 283*3117ece4Schristos do { 284*3117ece4Schristos U32 blockNb; 285*3117ece4Schristos for (blockNb=0; blockNb<nbBlocks; blockNb++) { 286*3117ece4Schristos rSize = ZSTD_CCtx_reset(zbc, ZSTD_reset_session_only); 287*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_CCtx_reset() failed : %s", ZSTD_getErrorName(rSize)); 288*3117ece4Schristos rSize = ZSTD_CCtx_setPledgedSrcSize(zbc, blockTable[blockNb].srcSize); 289*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_CCtx_setPledgedSrcSize() failed : %s", ZSTD_getErrorName(rSize)); 290*3117ece4Schristos inBuffer.src = blockTable[blockNb].srcPtr; 291*3117ece4Schristos inBuffer.size = blockTable[blockNb].srcSize; 292*3117ece4Schristos inBuffer.pos = 0; 293*3117ece4Schristos outBuffer.dst = blockTable[blockNb].cPtr; 294*3117ece4Schristos outBuffer.size = blockTable[blockNb].cRoom; 295*3117ece4Schristos outBuffer.pos = 0; 296*3117ece4Schristos rSize = ZSTD_compressStream(zbc, &outBuffer, &inBuffer); 297*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_compressStream() failed : %s", ZSTD_getErrorName(rSize)); 298*3117ece4Schristos rSize = ZSTD_endStream(zbc, &outBuffer); 299*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_endStream() failed : %s", ZSTD_getErrorName(rSize)); 300*3117ece4Schristos blockTable[blockNb].cSize = outBuffer.pos; 301*3117ece4Schristos } 302*3117ece4Schristos nbLoops++; 303*3117ece4Schristos } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 304*3117ece4Schristos ZSTD_freeCStream(zbc); 305*3117ece4Schristos } else if (compressor == BMK_ZWRAP_ZLIB_REUSE || compressor == BMK_ZWRAP_ZSTD_REUSE || compressor == BMK_ZLIB_REUSE) { 306*3117ece4Schristos z_stream def; 307*3117ece4Schristos int ret; 308*3117ece4Schristos int useSetDict = (dictBuffer != NULL); 309*3117ece4Schristos if (compressor == BMK_ZLIB_REUSE || compressor == BMK_ZWRAP_ZLIB_REUSE) ZWRAP_useZSTDcompression(0); 310*3117ece4Schristos else ZWRAP_useZSTDcompression(1); 311*3117ece4Schristos def.zalloc = Z_NULL; 312*3117ece4Schristos def.zfree = Z_NULL; 313*3117ece4Schristos def.opaque = Z_NULL; 314*3117ece4Schristos ret = deflateInit(&def, cLevel); 315*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "deflateInit failure"); 316*3117ece4Schristos /* if (ZWRAP_isUsingZSTDcompression()) { 317*3117ece4Schristos ret = ZWRAP_setPledgedSrcSize(&def, avgSize); 318*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "ZWRAP_setPledgedSrcSize failure"); 319*3117ece4Schristos } */ 320*3117ece4Schristos do { 321*3117ece4Schristos U32 blockNb; 322*3117ece4Schristos for (blockNb=0; blockNb<nbBlocks; blockNb++) { 323*3117ece4Schristos if (ZWRAP_isUsingZSTDcompression()) 324*3117ece4Schristos ret = ZWRAP_deflateReset_keepDict(&def); /* reuse dictionary to make compression faster */ 325*3117ece4Schristos else 326*3117ece4Schristos ret = deflateReset(&def); 327*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "deflateReset failure"); 328*3117ece4Schristos if (useSetDict) { 329*3117ece4Schristos ret = deflateSetDictionary(&def, (const z_Bytef*)dictBuffer, dictBufferSize); 330*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "deflateSetDictionary failure"); 331*3117ece4Schristos if (ZWRAP_isUsingZSTDcompression()) useSetDict = 0; /* zstd doesn't require deflateSetDictionary after ZWRAP_deflateReset_keepDict */ 332*3117ece4Schristos } 333*3117ece4Schristos def.next_in = (z_const z_Bytef*) blockTable[blockNb].srcPtr; 334*3117ece4Schristos def.avail_in = (uInt)blockTable[blockNb].srcSize; 335*3117ece4Schristos def.total_in = 0; 336*3117ece4Schristos def.next_out = (z_Bytef*) blockTable[blockNb].cPtr; 337*3117ece4Schristos def.avail_out = (uInt)blockTable[blockNb].cRoom; 338*3117ece4Schristos def.total_out = 0; 339*3117ece4Schristos ret = deflate(&def, Z_FINISH); 340*3117ece4Schristos if (ret != Z_STREAM_END) EXM_THROW(1, "deflate failure ret=%d srcSize=%d" , ret, (int)blockTable[blockNb].srcSize); 341*3117ece4Schristos blockTable[blockNb].cSize = def.total_out; 342*3117ece4Schristos } 343*3117ece4Schristos nbLoops++; 344*3117ece4Schristos } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 345*3117ece4Schristos ret = deflateEnd(&def); 346*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "deflateEnd failure"); 347*3117ece4Schristos } else { 348*3117ece4Schristos z_stream def; 349*3117ece4Schristos if (compressor == BMK_ZLIB || compressor == BMK_ZWRAP_ZLIB) ZWRAP_useZSTDcompression(0); 350*3117ece4Schristos else ZWRAP_useZSTDcompression(1); 351*3117ece4Schristos do { 352*3117ece4Schristos U32 blockNb; 353*3117ece4Schristos for (blockNb=0; blockNb<nbBlocks; blockNb++) { 354*3117ece4Schristos int ret; 355*3117ece4Schristos def.zalloc = Z_NULL; 356*3117ece4Schristos def.zfree = Z_NULL; 357*3117ece4Schristos def.opaque = Z_NULL; 358*3117ece4Schristos ret = deflateInit(&def, cLevel); 359*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "deflateInit failure"); 360*3117ece4Schristos if (dictBuffer) { 361*3117ece4Schristos ret = deflateSetDictionary(&def, (const z_Bytef*)dictBuffer, dictBufferSize); 362*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "deflateSetDictionary failure"); 363*3117ece4Schristos } 364*3117ece4Schristos def.next_in = (z_const z_Bytef*) blockTable[blockNb].srcPtr; 365*3117ece4Schristos def.avail_in = (uInt)blockTable[blockNb].srcSize; 366*3117ece4Schristos def.total_in = 0; 367*3117ece4Schristos def.next_out = (z_Bytef*) blockTable[blockNb].cPtr; 368*3117ece4Schristos def.avail_out = (uInt)blockTable[blockNb].cRoom; 369*3117ece4Schristos def.total_out = 0; 370*3117ece4Schristos ret = deflate(&def, Z_FINISH); 371*3117ece4Schristos if (ret != Z_STREAM_END) EXM_THROW(1, "deflate failure"); 372*3117ece4Schristos ret = deflateEnd(&def); 373*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "deflateEnd failure"); 374*3117ece4Schristos blockTable[blockNb].cSize = def.total_out; 375*3117ece4Schristos } 376*3117ece4Schristos nbLoops++; 377*3117ece4Schristos } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 378*3117ece4Schristos } 379*3117ece4Schristos { U64 const clockSpan = UTIL_clockSpanMicro(clockStart); 380*3117ece4Schristos if (clockSpan < fastestC*nbLoops) fastestC = clockSpan / nbLoops; 381*3117ece4Schristos totalCTime += clockSpan; 382*3117ece4Schristos cCompleted = totalCTime>maxTime; 383*3117ece4Schristos } } 384*3117ece4Schristos 385*3117ece4Schristos cSize = 0; 386*3117ece4Schristos { U32 blockNb; for (blockNb=0; blockNb<nbBlocks; blockNb++) cSize += blockTable[blockNb].cSize; } 387*3117ece4Schristos ratio = (double)srcSize / (double)cSize; 388*3117ece4Schristos markNb = (markNb+1) % NB_MARKS; 389*3117ece4Schristos DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s\r", 390*3117ece4Schristos marks[markNb], displayName, (unsigned)srcSize, (unsigned)cSize, ratio, 391*3117ece4Schristos (double)srcSize / (double)fastestC ); 392*3117ece4Schristos 393*3117ece4Schristos (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ 394*3117ece4Schristos #if 1 395*3117ece4Schristos /* Decompression */ 396*3117ece4Schristos if (!dCompleted) memset(resultBuffer, 0xD6, srcSize); /* warm result buffer */ 397*3117ece4Schristos 398*3117ece4Schristos UTIL_sleepMilli(1); /* give processor time to other processes */ 399*3117ece4Schristos UTIL_waitForNextTick(); 400*3117ece4Schristos clockStart = UTIL_getTime(); 401*3117ece4Schristos 402*3117ece4Schristos if (!dCompleted) { 403*3117ece4Schristos U32 nbLoops = 0; 404*3117ece4Schristos if (compressor == BMK_ZSTD) { 405*3117ece4Schristos ZSTD_DDict* ddict = ZSTD_createDDict(dictBuffer, dictBufferSize); 406*3117ece4Schristos if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure"); 407*3117ece4Schristos do { 408*3117ece4Schristos unsigned blockNb; 409*3117ece4Schristos for (blockNb=0; blockNb<nbBlocks; blockNb++) { 410*3117ece4Schristos size_t const regenSize = ZSTD_decompress_usingDDict(dctx, 411*3117ece4Schristos blockTable[blockNb].resPtr, blockTable[blockNb].srcSize, 412*3117ece4Schristos blockTable[blockNb].cPtr, blockTable[blockNb].cSize, 413*3117ece4Schristos ddict); 414*3117ece4Schristos if (ZSTD_isError(regenSize)) { 415*3117ece4Schristos DISPLAY("ZSTD_decompress_usingDDict() failed on block %u : %s \n", 416*3117ece4Schristos blockNb, ZSTD_getErrorName(regenSize)); 417*3117ece4Schristos clockLoop = 0; /* force immediate test end */ 418*3117ece4Schristos break; 419*3117ece4Schristos } 420*3117ece4Schristos blockTable[blockNb].resSize = regenSize; 421*3117ece4Schristos } 422*3117ece4Schristos nbLoops++; 423*3117ece4Schristos } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 424*3117ece4Schristos ZSTD_freeDDict(ddict); 425*3117ece4Schristos } else if (compressor == BMK_ZSTD_STREAM) { 426*3117ece4Schristos ZSTD_inBuffer inBuffer; 427*3117ece4Schristos ZSTD_outBuffer outBuffer; 428*3117ece4Schristos ZSTD_DStream* zbd = ZSTD_createDStream(); 429*3117ece4Schristos size_t rSize; 430*3117ece4Schristos if (zbd == NULL) EXM_THROW(1, "ZSTD_createDStream() allocation failure"); 431*3117ece4Schristos rSize = ZSTD_DCtx_reset(zbd, ZSTD_reset_session_only); 432*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_DCtx_reset() failed : %s", ZSTD_getErrorName(rSize)); 433*3117ece4Schristos rSize = ZSTD_DCtx_loadDictionary(zbd, dictBuffer, dictBufferSize); 434*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_DCtx_loadDictionary() failed : %s", ZSTD_getErrorName(rSize)); 435*3117ece4Schristos do { 436*3117ece4Schristos U32 blockNb; 437*3117ece4Schristos for (blockNb=0; blockNb<nbBlocks; blockNb++) { 438*3117ece4Schristos rSize = ZSTD_DCtx_reset(zbd, ZSTD_reset_session_only); 439*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_DCtx_reset() failed : %s", ZSTD_getErrorName(rSize)); 440*3117ece4Schristos inBuffer.src = blockTable[blockNb].cPtr; 441*3117ece4Schristos inBuffer.size = blockTable[blockNb].cSize; 442*3117ece4Schristos inBuffer.pos = 0; 443*3117ece4Schristos outBuffer.dst = blockTable[blockNb].resPtr; 444*3117ece4Schristos outBuffer.size = blockTable[blockNb].srcSize; 445*3117ece4Schristos outBuffer.pos = 0; 446*3117ece4Schristos rSize = ZSTD_decompressStream(zbd, &outBuffer, &inBuffer); 447*3117ece4Schristos if (ZSTD_isError(rSize)) EXM_THROW(1, "ZSTD_decompressStream() failed : %s", ZSTD_getErrorName(rSize)); 448*3117ece4Schristos blockTable[blockNb].resSize = outBuffer.pos; 449*3117ece4Schristos } 450*3117ece4Schristos nbLoops++; 451*3117ece4Schristos } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 452*3117ece4Schristos ZSTD_freeDStream(zbd); 453*3117ece4Schristos } else if (compressor == BMK_ZWRAP_ZLIB_REUSE || compressor == BMK_ZWRAP_ZSTD_REUSE || compressor == BMK_ZLIB_REUSE) { 454*3117ece4Schristos z_stream inf; 455*3117ece4Schristos int ret; 456*3117ece4Schristos if (compressor == BMK_ZLIB_REUSE) ZWRAP_setDecompressionType(ZWRAP_FORCE_ZLIB); 457*3117ece4Schristos else ZWRAP_setDecompressionType(ZWRAP_AUTO); 458*3117ece4Schristos inf.zalloc = Z_NULL; 459*3117ece4Schristos inf.zfree = Z_NULL; 460*3117ece4Schristos inf.opaque = Z_NULL; 461*3117ece4Schristos ret = inflateInit(&inf); 462*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "inflateInit failure"); 463*3117ece4Schristos do { 464*3117ece4Schristos U32 blockNb; 465*3117ece4Schristos for (blockNb=0; blockNb<nbBlocks; blockNb++) { 466*3117ece4Schristos if (ZWRAP_isUsingZSTDdecompression(&inf)) 467*3117ece4Schristos ret = ZWRAP_inflateReset_keepDict(&inf); /* reuse dictionary to make decompression faster; inflate will return Z_NEED_DICT only for the first time */ 468*3117ece4Schristos else 469*3117ece4Schristos ret = inflateReset(&inf); 470*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "inflateReset failure"); 471*3117ece4Schristos inf.next_in = (z_const z_Bytef*) blockTable[blockNb].cPtr; 472*3117ece4Schristos inf.avail_in = (uInt)blockTable[blockNb].cSize; 473*3117ece4Schristos inf.total_in = 0; 474*3117ece4Schristos inf.next_out = (z_Bytef*) blockTable[blockNb].resPtr; 475*3117ece4Schristos inf.avail_out = (uInt)blockTable[blockNb].srcSize; 476*3117ece4Schristos inf.total_out = 0; 477*3117ece4Schristos ret = inflate(&inf, Z_FINISH); 478*3117ece4Schristos if (ret == Z_NEED_DICT) { 479*3117ece4Schristos ret = inflateSetDictionary(&inf, (const z_Bytef*)dictBuffer, dictBufferSize); 480*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "inflateSetDictionary failure"); 481*3117ece4Schristos ret = inflate(&inf, Z_FINISH); 482*3117ece4Schristos } 483*3117ece4Schristos if (ret != Z_STREAM_END) EXM_THROW(1, "inflate failure"); 484*3117ece4Schristos blockTable[blockNb].resSize = inf.total_out; 485*3117ece4Schristos } 486*3117ece4Schristos nbLoops++; 487*3117ece4Schristos } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 488*3117ece4Schristos ret = inflateEnd(&inf); 489*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "inflateEnd failure"); 490*3117ece4Schristos } else { 491*3117ece4Schristos z_stream inf; 492*3117ece4Schristos if (compressor == BMK_ZLIB) ZWRAP_setDecompressionType(ZWRAP_FORCE_ZLIB); 493*3117ece4Schristos else ZWRAP_setDecompressionType(ZWRAP_AUTO); 494*3117ece4Schristos do { 495*3117ece4Schristos U32 blockNb; 496*3117ece4Schristos for (blockNb=0; blockNb<nbBlocks; blockNb++) { 497*3117ece4Schristos int ret; 498*3117ece4Schristos inf.zalloc = Z_NULL; 499*3117ece4Schristos inf.zfree = Z_NULL; 500*3117ece4Schristos inf.opaque = Z_NULL; 501*3117ece4Schristos ret = inflateInit(&inf); 502*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "inflateInit failure"); 503*3117ece4Schristos inf.next_in = (z_const z_Bytef*) blockTable[blockNb].cPtr; 504*3117ece4Schristos inf.avail_in = (uInt)blockTable[blockNb].cSize; 505*3117ece4Schristos inf.total_in = 0; 506*3117ece4Schristos inf.next_out = (z_Bytef*) blockTable[blockNb].resPtr; 507*3117ece4Schristos inf.avail_out = (uInt)blockTable[blockNb].srcSize; 508*3117ece4Schristos inf.total_out = 0; 509*3117ece4Schristos ret = inflate(&inf, Z_FINISH); 510*3117ece4Schristos if (ret == Z_NEED_DICT) { 511*3117ece4Schristos ret = inflateSetDictionary(&inf, (const z_Bytef*) dictBuffer, dictBufferSize); 512*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "inflateSetDictionary failure"); 513*3117ece4Schristos ret = inflate(&inf, Z_FINISH); 514*3117ece4Schristos } 515*3117ece4Schristos if (ret != Z_STREAM_END) EXM_THROW(1, "inflate failure"); 516*3117ece4Schristos ret = inflateEnd(&inf); 517*3117ece4Schristos if (ret != Z_OK) EXM_THROW(1, "inflateEnd failure"); 518*3117ece4Schristos blockTable[blockNb].resSize = inf.total_out; 519*3117ece4Schristos } 520*3117ece4Schristos nbLoops++; 521*3117ece4Schristos } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 522*3117ece4Schristos } 523*3117ece4Schristos { U64 const clockSpan = UTIL_clockSpanMicro(clockStart); 524*3117ece4Schristos if (clockSpan < fastestD*nbLoops) fastestD = clockSpan / nbLoops; 525*3117ece4Schristos totalDTime += clockSpan; 526*3117ece4Schristos dCompleted = totalDTime>maxTime; 527*3117ece4Schristos } } 528*3117ece4Schristos 529*3117ece4Schristos markNb = (markNb+1) % NB_MARKS; 530*3117ece4Schristos DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s ,%6.1f MB/s\r", 531*3117ece4Schristos marks[markNb], displayName, (unsigned)srcSize, (unsigned)cSize, ratio, 532*3117ece4Schristos (double)srcSize / (double)fastestC, 533*3117ece4Schristos (double)srcSize / (double)fastestD ); 534*3117ece4Schristos 535*3117ece4Schristos /* CRC Checking */ 536*3117ece4Schristos { U64 const crcCheck = XXH64(resultBuffer, srcSize, 0); 537*3117ece4Schristos if (crcOrig!=crcCheck) { 538*3117ece4Schristos size_t u; 539*3117ece4Schristos DISPLAY("!!! WARNING !!! %14s : Invalid Checksum : %x != %x \n", displayName, (unsigned)crcOrig, (unsigned)crcCheck); 540*3117ece4Schristos for (u=0; u<srcSize; u++) { 541*3117ece4Schristos if (((const BYTE*)srcBuffer)[u] != ((const BYTE*)resultBuffer)[u]) { 542*3117ece4Schristos unsigned segNb, bNb, pos; 543*3117ece4Schristos size_t bacc = 0; 544*3117ece4Schristos DISPLAY("Decoding error at pos %u ", (unsigned)u); 545*3117ece4Schristos for (segNb = 0; segNb < nbBlocks; segNb++) { 546*3117ece4Schristos if (bacc + blockTable[segNb].srcSize > u) break; 547*3117ece4Schristos bacc += blockTable[segNb].srcSize; 548*3117ece4Schristos } 549*3117ece4Schristos pos = (U32)(u - bacc); 550*3117ece4Schristos bNb = pos / (128 KB); 551*3117ece4Schristos DISPLAY("(block %u, sub %u, pos %u) \n", segNb, bNb, pos); 552*3117ece4Schristos break; 553*3117ece4Schristos } 554*3117ece4Schristos if (u==srcSize-1) { /* should never happen */ 555*3117ece4Schristos DISPLAY("no difference detected\n"); 556*3117ece4Schristos } } 557*3117ece4Schristos break; 558*3117ece4Schristos } } /* CRC Checking */ 559*3117ece4Schristos #endif 560*3117ece4Schristos } /* for (testNb = 1; testNb <= (g_nbIterations + !g_nbIterations); testNb++) */ 561*3117ece4Schristos 562*3117ece4Schristos if (g_displayLevel == 1) { 563*3117ece4Schristos double cSpeed = (double)srcSize / (double)fastestC; 564*3117ece4Schristos double dSpeed = (double)srcSize / (double)fastestD; 565*3117ece4Schristos if (g_additionalParam) 566*3117ece4Schristos DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName, g_additionalParam); 567*3117ece4Schristos else 568*3117ece4Schristos DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName); 569*3117ece4Schristos } 570*3117ece4Schristos DISPLAYLEVEL(2, "%2i#\n", cLevel); 571*3117ece4Schristos } /* Bench */ 572*3117ece4Schristos 573*3117ece4Schristos /* clean up */ 574*3117ece4Schristos free(blockTable); 575*3117ece4Schristos free(compressedBuffer); 576*3117ece4Schristos free(resultBuffer); 577*3117ece4Schristos ZSTD_freeCCtx(ctx); 578*3117ece4Schristos ZSTD_freeDCtx(dctx); 579*3117ece4Schristos return 0; 580*3117ece4Schristos } 581*3117ece4Schristos 582*3117ece4Schristos 583*3117ece4Schristos static size_t BMK_findMaxMem(U64 requiredMem) 584*3117ece4Schristos { 585*3117ece4Schristos size_t const step = 64 MB; 586*3117ece4Schristos BYTE* testmem = NULL; 587*3117ece4Schristos 588*3117ece4Schristos requiredMem = (((requiredMem >> 26) + 1) << 26); 589*3117ece4Schristos requiredMem += step; 590*3117ece4Schristos if (requiredMem > maxMemory) requiredMem = maxMemory; 591*3117ece4Schristos 592*3117ece4Schristos do { 593*3117ece4Schristos testmem = (BYTE*)malloc((size_t)requiredMem); 594*3117ece4Schristos requiredMem -= step; 595*3117ece4Schristos } while (!testmem && requiredMem); /* do not allocate zero bytes */ 596*3117ece4Schristos 597*3117ece4Schristos free(testmem); 598*3117ece4Schristos return (size_t)(requiredMem+1); /* avoid zero */ 599*3117ece4Schristos } 600*3117ece4Schristos 601*3117ece4Schristos static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, 602*3117ece4Schristos const char* displayName, int cLevel, int cLevelLast, 603*3117ece4Schristos const size_t* fileSizes, unsigned nbFiles, 604*3117ece4Schristos const void* dictBuffer, size_t dictBufferSize) 605*3117ece4Schristos { 606*3117ece4Schristos int l; 607*3117ece4Schristos 608*3117ece4Schristos const char* pch = strrchr(displayName, '\\'); /* Windows */ 609*3117ece4Schristos if (!pch) pch = strrchr(displayName, '/'); /* Linux */ 610*3117ece4Schristos if (pch) displayName = pch+1; 611*3117ece4Schristos 612*3117ece4Schristos SET_REALTIME_PRIORITY; 613*3117ece4Schristos 614*3117ece4Schristos if (g_displayLevel == 1 && !g_additionalParam) 615*3117ece4Schristos DISPLAY("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n", 616*3117ece4Schristos ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING, 617*3117ece4Schristos (unsigned)benchedSize, g_nbIterations, (unsigned)(g_blockSize>>10)); 618*3117ece4Schristos 619*3117ece4Schristos if (cLevelLast < cLevel) cLevelLast = cLevel; 620*3117ece4Schristos 621*3117ece4Schristos DISPLAY("benchmarking zstd %s (using ZSTD_CStream)\n", ZSTD_VERSION_STRING); 622*3117ece4Schristos for (l=cLevel; l <= cLevelLast; l++) { 623*3117ece4Schristos BMK_benchMem(srcBuffer, benchedSize, 624*3117ece4Schristos displayName, l, 625*3117ece4Schristos fileSizes, nbFiles, 626*3117ece4Schristos dictBuffer, dictBufferSize, BMK_ZSTD_STREAM); 627*3117ece4Schristos } 628*3117ece4Schristos 629*3117ece4Schristos DISPLAY("benchmarking zstd %s (using ZSTD_CCtx)\n", ZSTD_VERSION_STRING); 630*3117ece4Schristos for (l=cLevel; l <= cLevelLast; l++) { 631*3117ece4Schristos BMK_benchMem(srcBuffer, benchedSize, 632*3117ece4Schristos displayName, l, 633*3117ece4Schristos fileSizes, nbFiles, 634*3117ece4Schristos dictBuffer, dictBufferSize, BMK_ZSTD); 635*3117ece4Schristos } 636*3117ece4Schristos 637*3117ece4Schristos DISPLAY("benchmarking zstd %s (using zlibWrapper)\n", ZSTD_VERSION_STRING); 638*3117ece4Schristos for (l=cLevel; l <= cLevelLast; l++) { 639*3117ece4Schristos BMK_benchMem(srcBuffer, benchedSize, 640*3117ece4Schristos displayName, l, 641*3117ece4Schristos fileSizes, nbFiles, 642*3117ece4Schristos dictBuffer, dictBufferSize, BMK_ZWRAP_ZSTD_REUSE); 643*3117ece4Schristos } 644*3117ece4Schristos 645*3117ece4Schristos DISPLAY("benchmarking zstd %s (zlibWrapper not reusing a context)\n", ZSTD_VERSION_STRING); 646*3117ece4Schristos for (l=cLevel; l <= cLevelLast; l++) { 647*3117ece4Schristos BMK_benchMem(srcBuffer, benchedSize, 648*3117ece4Schristos displayName, l, 649*3117ece4Schristos fileSizes, nbFiles, 650*3117ece4Schristos dictBuffer, dictBufferSize, BMK_ZWRAP_ZSTD); 651*3117ece4Schristos } 652*3117ece4Schristos 653*3117ece4Schristos 654*3117ece4Schristos if (cLevelLast > Z_BEST_COMPRESSION) cLevelLast = Z_BEST_COMPRESSION; 655*3117ece4Schristos 656*3117ece4Schristos DISPLAY("\n"); 657*3117ece4Schristos DISPLAY("benchmarking zlib %s\n", ZLIB_VERSION); 658*3117ece4Schristos for (l=cLevel; l <= cLevelLast; l++) { 659*3117ece4Schristos BMK_benchMem(srcBuffer, benchedSize, 660*3117ece4Schristos displayName, l, 661*3117ece4Schristos fileSizes, nbFiles, 662*3117ece4Schristos dictBuffer, dictBufferSize, BMK_ZLIB_REUSE); 663*3117ece4Schristos } 664*3117ece4Schristos 665*3117ece4Schristos DISPLAY("benchmarking zlib %s (zlib not reusing a context)\n", ZLIB_VERSION); 666*3117ece4Schristos for (l=cLevel; l <= cLevelLast; l++) { 667*3117ece4Schristos BMK_benchMem(srcBuffer, benchedSize, 668*3117ece4Schristos displayName, l, 669*3117ece4Schristos fileSizes, nbFiles, 670*3117ece4Schristos dictBuffer, dictBufferSize, BMK_ZLIB); 671*3117ece4Schristos } 672*3117ece4Schristos 673*3117ece4Schristos DISPLAY("benchmarking zlib %s (using zlibWrapper)\n", ZLIB_VERSION); 674*3117ece4Schristos for (l=cLevel; l <= cLevelLast; l++) { 675*3117ece4Schristos BMK_benchMem(srcBuffer, benchedSize, 676*3117ece4Schristos displayName, l, 677*3117ece4Schristos fileSizes, nbFiles, 678*3117ece4Schristos dictBuffer, dictBufferSize, BMK_ZWRAP_ZLIB_REUSE); 679*3117ece4Schristos } 680*3117ece4Schristos 681*3117ece4Schristos DISPLAY("benchmarking zlib %s (zlibWrapper not reusing a context)\n", ZLIB_VERSION); 682*3117ece4Schristos for (l=cLevel; l <= cLevelLast; l++) { 683*3117ece4Schristos BMK_benchMem(srcBuffer, benchedSize, 684*3117ece4Schristos displayName, l, 685*3117ece4Schristos fileSizes, nbFiles, 686*3117ece4Schristos dictBuffer, dictBufferSize, BMK_ZWRAP_ZLIB); 687*3117ece4Schristos } 688*3117ece4Schristos } 689*3117ece4Schristos 690*3117ece4Schristos 691*3117ece4Schristos /*! BMK_loadFiles() : 692*3117ece4Schristos Loads `buffer` with content of files listed within `fileNamesTable`. 693*3117ece4Schristos At most, fills `buffer` entirely */ 694*3117ece4Schristos static void BMK_loadFiles(void* buffer, size_t bufferSize, 695*3117ece4Schristos size_t* fileSizes, 696*3117ece4Schristos const char** fileNamesTable, unsigned nbFiles) 697*3117ece4Schristos { 698*3117ece4Schristos size_t pos = 0, totalSize = 0; 699*3117ece4Schristos unsigned n; 700*3117ece4Schristos for (n=0; n<nbFiles; n++) { 701*3117ece4Schristos FILE* f; 702*3117ece4Schristos U64 fileSize = UTIL_getFileSize(fileNamesTable[n]); 703*3117ece4Schristos if (UTIL_isDirectory(fileNamesTable[n])) { 704*3117ece4Schristos DISPLAYLEVEL(2, "Ignoring %s directory... \n", fileNamesTable[n]); 705*3117ece4Schristos fileSizes[n] = 0; 706*3117ece4Schristos continue; 707*3117ece4Schristos } 708*3117ece4Schristos if (fileSize == UTIL_FILESIZE_UNKNOWN) { 709*3117ece4Schristos DISPLAYLEVEL(2, "Cannot determine size of %s ... \n", fileNamesTable[n]); 710*3117ece4Schristos fileSizes[n] = 0; 711*3117ece4Schristos continue; 712*3117ece4Schristos } 713*3117ece4Schristos f = fopen(fileNamesTable[n], "rb"); 714*3117ece4Schristos if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]); 715*3117ece4Schristos DISPLAYUPDATE(2, "Loading %s... \r", fileNamesTable[n]); 716*3117ece4Schristos if (fileSize > bufferSize-pos) fileSize = bufferSize-pos, nbFiles=n; /* buffer too small - stop after this file */ 717*3117ece4Schristos { size_t const readSize = fread(((char*)buffer)+pos, 1, (size_t)fileSize, f); 718*3117ece4Schristos if (readSize != (size_t)fileSize) EXM_THROW(11, "could not read %s", fileNamesTable[n]); 719*3117ece4Schristos pos += readSize; } 720*3117ece4Schristos fileSizes[n] = (size_t)fileSize; 721*3117ece4Schristos totalSize += (size_t)fileSize; 722*3117ece4Schristos fclose(f); 723*3117ece4Schristos } 724*3117ece4Schristos 725*3117ece4Schristos if (totalSize == 0) EXM_THROW(12, "no data to bench"); 726*3117ece4Schristos } 727*3117ece4Schristos 728*3117ece4Schristos static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, 729*3117ece4Schristos const char* dictFileName, int cLevel, int cLevelLast) 730*3117ece4Schristos { 731*3117ece4Schristos void* srcBuffer; 732*3117ece4Schristos size_t benchedSize; 733*3117ece4Schristos void* dictBuffer = NULL; 734*3117ece4Schristos size_t dictBufferSize = 0; 735*3117ece4Schristos size_t* fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t)); 736*3117ece4Schristos U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles); 737*3117ece4Schristos char mfName[20] = {0}; 738*3117ece4Schristos 739*3117ece4Schristos if (!fileSizes) EXM_THROW(12, "not enough memory for fileSizes"); 740*3117ece4Schristos 741*3117ece4Schristos /* Load dictionary */ 742*3117ece4Schristos if (dictFileName != NULL) { 743*3117ece4Schristos U64 const dictFileSize = UTIL_getFileSize(dictFileName); 744*3117ece4Schristos if (dictFileSize > 64 MB) 745*3117ece4Schristos EXM_THROW(10, "dictionary file %s too large", dictFileName); 746*3117ece4Schristos dictBufferSize = (size_t)dictFileSize; 747*3117ece4Schristos dictBuffer = malloc(dictBufferSize); 748*3117ece4Schristos if (dictBuffer==NULL) 749*3117ece4Schristos EXM_THROW(11, "not enough memory for dictionary (%u bytes)", (unsigned)dictBufferSize); 750*3117ece4Schristos BMK_loadFiles(dictBuffer, dictBufferSize, fileSizes, &dictFileName, 1); 751*3117ece4Schristos } 752*3117ece4Schristos 753*3117ece4Schristos /* Memory allocation & restrictions */ 754*3117ece4Schristos benchedSize = BMK_findMaxMem(totalSizeToLoad * 3) / 3; 755*3117ece4Schristos if ((U64)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; 756*3117ece4Schristos if (benchedSize < totalSizeToLoad) 757*3117ece4Schristos DISPLAY("Not enough memory; testing %u MB only...\n", (unsigned)(benchedSize >> 20)); 758*3117ece4Schristos srcBuffer = malloc(benchedSize + !benchedSize); 759*3117ece4Schristos if (!srcBuffer) EXM_THROW(12, "not enough memory"); 760*3117ece4Schristos 761*3117ece4Schristos /* Load input buffer */ 762*3117ece4Schristos BMK_loadFiles(srcBuffer, benchedSize, fileSizes, fileNamesTable, nbFiles); 763*3117ece4Schristos 764*3117ece4Schristos /* Bench */ 765*3117ece4Schristos snprintf (mfName, sizeof(mfName), " %u files", nbFiles); 766*3117ece4Schristos { const char* displayName = (nbFiles > 1) ? mfName : fileNamesTable[0]; 767*3117ece4Schristos BMK_benchCLevel(srcBuffer, benchedSize, 768*3117ece4Schristos displayName, cLevel, cLevelLast, 769*3117ece4Schristos fileSizes, nbFiles, 770*3117ece4Schristos dictBuffer, dictBufferSize); 771*3117ece4Schristos } 772*3117ece4Schristos 773*3117ece4Schristos /* clean up */ 774*3117ece4Schristos free(srcBuffer); 775*3117ece4Schristos free(dictBuffer); 776*3117ece4Schristos free(fileSizes); 777*3117ece4Schristos } 778*3117ece4Schristos 779*3117ece4Schristos 780*3117ece4Schristos static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility) 781*3117ece4Schristos { 782*3117ece4Schristos char name[20] = {0}; 783*3117ece4Schristos size_t benchedSize = 10000000; 784*3117ece4Schristos void* const srcBuffer = malloc(benchedSize); 785*3117ece4Schristos 786*3117ece4Schristos /* Memory allocation */ 787*3117ece4Schristos if (!srcBuffer) EXM_THROW(21, "not enough memory"); 788*3117ece4Schristos 789*3117ece4Schristos /* Fill input buffer */ 790*3117ece4Schristos RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0); 791*3117ece4Schristos 792*3117ece4Schristos /* Bench */ 793*3117ece4Schristos snprintf (name, sizeof(name), "Synthetic %2u%%", (unsigned)(compressibility*100)); 794*3117ece4Schristos BMK_benchCLevel(srcBuffer, benchedSize, name, cLevel, cLevelLast, &benchedSize, 1, NULL, 0); 795*3117ece4Schristos 796*3117ece4Schristos /* clean up */ 797*3117ece4Schristos free(srcBuffer); 798*3117ece4Schristos } 799*3117ece4Schristos 800*3117ece4Schristos 801*3117ece4Schristos static int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, 802*3117ece4Schristos const char* dictFileName, int cLevel, int cLevelLast) 803*3117ece4Schristos { 804*3117ece4Schristos double const compressibility = (double)g_compressibilityDefault / 100; 805*3117ece4Schristos 806*3117ece4Schristos if (nbFiles == 0) 807*3117ece4Schristos BMK_syntheticTest(cLevel, cLevelLast, compressibility); 808*3117ece4Schristos else 809*3117ece4Schristos BMK_benchFileTable(fileNamesTable, nbFiles, dictFileName, cLevel, cLevelLast); 810*3117ece4Schristos return 0; 811*3117ece4Schristos } 812*3117ece4Schristos 813*3117ece4Schristos 814*3117ece4Schristos 815*3117ece4Schristos 816*3117ece4Schristos /*-************************************ 817*3117ece4Schristos * Command Line 818*3117ece4Schristos **************************************/ 819*3117ece4Schristos static int usage(const char* programName) 820*3117ece4Schristos { 821*3117ece4Schristos DISPLAY(WELCOME_MESSAGE); 822*3117ece4Schristos DISPLAY( "Usage :\n"); 823*3117ece4Schristos DISPLAY( " %s [args] [FILE(s)] [-o file]\n", programName); 824*3117ece4Schristos DISPLAY( "\n"); 825*3117ece4Schristos DISPLAY( "FILE : a filename\n"); 826*3117ece4Schristos DISPLAY( " with no FILE, or when FILE is - , read standard input\n"); 827*3117ece4Schristos DISPLAY( "Arguments :\n"); 828*3117ece4Schristos DISPLAY( " -D file: use `file` as Dictionary \n"); 829*3117ece4Schristos DISPLAY( " -h/-H : display help/long help and exit\n"); 830*3117ece4Schristos DISPLAY( " -V : display Version number and exit\n"); 831*3117ece4Schristos DISPLAY( " -v : verbose mode; specify multiple times to increase log level (default:%d)\n", DEFAULT_DISPLAY_LEVEL); 832*3117ece4Schristos DISPLAY( " -q : suppress warnings; specify twice to suppress errors too\n"); 833*3117ece4Schristos #ifdef UTIL_HAS_CREATEFILELIST 834*3117ece4Schristos DISPLAY( " -r : operate recursively on directories\n"); 835*3117ece4Schristos #endif 836*3117ece4Schristos DISPLAY( "\n"); 837*3117ece4Schristos DISPLAY( "Benchmark arguments :\n"); 838*3117ece4Schristos DISPLAY( " -b# : benchmark file(s), using # compression level (default : %d) \n", ZSTDCLI_CLEVEL_DEFAULT); 839*3117ece4Schristos DISPLAY( " -e# : test all compression levels from -bX to # (default: %d)\n", ZSTDCLI_CLEVEL_DEFAULT); 840*3117ece4Schristos DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s)\n"); 841*3117ece4Schristos DISPLAY( " -B# : cut file into independent chunks of size # (default: no chunking)\n"); 842*3117ece4Schristos return 0; 843*3117ece4Schristos } 844*3117ece4Schristos 845*3117ece4Schristos static int badusage(const char* programName) 846*3117ece4Schristos { 847*3117ece4Schristos DISPLAYLEVEL(1, "Incorrect parameters\n"); 848*3117ece4Schristos if (g_displayLevel >= 1) usage(programName); 849*3117ece4Schristos return 1; 850*3117ece4Schristos } 851*3117ece4Schristos 852*3117ece4Schristos static void waitEnter(void) 853*3117ece4Schristos { 854*3117ece4Schristos int unused; 855*3117ece4Schristos DISPLAY("Press enter to continue...\n"); 856*3117ece4Schristos unused = getchar(); 857*3117ece4Schristos (void)unused; 858*3117ece4Schristos } 859*3117ece4Schristos 860*3117ece4Schristos /*! readU32FromChar() : 861*3117ece4Schristos @return : unsigned integer value reach from input in `char` format 862*3117ece4Schristos Will also modify `*stringPtr`, advancing it to position where it stopped reading. 863*3117ece4Schristos Note : this function can overflow if digit string > MAX_UINT */ 864*3117ece4Schristos static unsigned readU32FromChar(const char** stringPtr) 865*3117ece4Schristos { 866*3117ece4Schristos unsigned result = 0; 867*3117ece4Schristos while ((**stringPtr >='0') && (**stringPtr <='9')) 868*3117ece4Schristos result *= 10, result += (unsigned)(**stringPtr - '0'), (*stringPtr)++ ; 869*3117ece4Schristos return result; 870*3117ece4Schristos } 871*3117ece4Schristos 872*3117ece4Schristos 873*3117ece4Schristos #define CLEAN_RETURN(i) { operationResult = (i); goto _end; } 874*3117ece4Schristos 875*3117ece4Schristos int main(int argCount, char** argv) 876*3117ece4Schristos { 877*3117ece4Schristos int argNb, 878*3117ece4Schristos main_pause=0, 879*3117ece4Schristos nextEntryIsDictionary=0, 880*3117ece4Schristos operationResult=0, 881*3117ece4Schristos nextArgumentIsFile=0; 882*3117ece4Schristos int cLevel = ZSTDCLI_CLEVEL_DEFAULT; 883*3117ece4Schristos int cLevelLast = 1; 884*3117ece4Schristos unsigned recursive = 0; 885*3117ece4Schristos FileNamesTable* filenames = UTIL_allocateFileNamesTable((size_t)argCount); 886*3117ece4Schristos const char* programName = argv[0]; 887*3117ece4Schristos const char* dictFileName = NULL; 888*3117ece4Schristos char* dynNameSpace = NULL; 889*3117ece4Schristos 890*3117ece4Schristos /* init */ 891*3117ece4Schristos if (filenames==NULL) { DISPLAY("zstd: %s \n", strerror(errno)); exit(1); } 892*3117ece4Schristos displayOut = stderr; 893*3117ece4Schristos 894*3117ece4Schristos /* Pick out program name from path. Don't rely on stdlib because of conflicting behavior */ 895*3117ece4Schristos { size_t pos; 896*3117ece4Schristos for (pos = strlen(programName); pos > 0; pos--) { if (programName[pos] == '/') { pos++; break; } } 897*3117ece4Schristos programName += pos; 898*3117ece4Schristos } 899*3117ece4Schristos 900*3117ece4Schristos /* command switches */ 901*3117ece4Schristos for(argNb=1; argNb<argCount; argNb++) { 902*3117ece4Schristos const char* argument = argv[argNb]; 903*3117ece4Schristos if(!argument) continue; /* Protection if argument empty */ 904*3117ece4Schristos 905*3117ece4Schristos if (nextArgumentIsFile==0) { 906*3117ece4Schristos 907*3117ece4Schristos /* long commands (--long-word) */ 908*3117ece4Schristos if (!strcmp(argument, "--")) { nextArgumentIsFile=1; continue; } 909*3117ece4Schristos if (!strcmp(argument, "--version")) { displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); } 910*3117ece4Schristos if (!strcmp(argument, "--help")) { displayOut=stdout; CLEAN_RETURN(usage(programName)); } 911*3117ece4Schristos if (!strcmp(argument, "--verbose")) { g_displayLevel++; continue; } 912*3117ece4Schristos if (!strcmp(argument, "--quiet")) { g_displayLevel--; continue; } 913*3117ece4Schristos 914*3117ece4Schristos /* Decode commands (note : aggregated commands are allowed) */ 915*3117ece4Schristos if (argument[0]=='-') { 916*3117ece4Schristos argument++; 917*3117ece4Schristos 918*3117ece4Schristos while (argument[0]!=0) { 919*3117ece4Schristos switch(argument[0]) 920*3117ece4Schristos { 921*3117ece4Schristos /* Display help */ 922*3117ece4Schristos case 'V': displayOut=stdout; DISPLAY(WELCOME_MESSAGE); CLEAN_RETURN(0); /* Version Only */ 923*3117ece4Schristos case 'H': 924*3117ece4Schristos case 'h': displayOut=stdout; CLEAN_RETURN(usage(programName)); 925*3117ece4Schristos 926*3117ece4Schristos /* Use file content as dictionary */ 927*3117ece4Schristos case 'D': nextEntryIsDictionary = 1; argument++; break; 928*3117ece4Schristos 929*3117ece4Schristos /* Verbose mode */ 930*3117ece4Schristos case 'v': g_displayLevel++; argument++; break; 931*3117ece4Schristos 932*3117ece4Schristos /* Quiet mode */ 933*3117ece4Schristos case 'q': g_displayLevel--; argument++; break; 934*3117ece4Schristos 935*3117ece4Schristos #ifdef UTIL_HAS_CREATEFILELIST 936*3117ece4Schristos /* recursive */ 937*3117ece4Schristos case 'r': recursive=1; argument++; break; 938*3117ece4Schristos #endif 939*3117ece4Schristos 940*3117ece4Schristos /* Benchmark */ 941*3117ece4Schristos case 'b': 942*3117ece4Schristos /* first compression Level */ 943*3117ece4Schristos argument++; 944*3117ece4Schristos cLevel = (int)readU32FromChar(&argument); 945*3117ece4Schristos break; 946*3117ece4Schristos 947*3117ece4Schristos /* range bench (benchmark only) */ 948*3117ece4Schristos case 'e': 949*3117ece4Schristos /* last compression Level */ 950*3117ece4Schristos argument++; 951*3117ece4Schristos cLevelLast = (int)readU32FromChar(&argument); 952*3117ece4Schristos break; 953*3117ece4Schristos 954*3117ece4Schristos /* Modify Nb Iterations (benchmark only) */ 955*3117ece4Schristos case 'i': 956*3117ece4Schristos argument++; 957*3117ece4Schristos { U32 const iters = readU32FromChar(&argument); 958*3117ece4Schristos BMK_setNotificationLevel(g_displayLevel); 959*3117ece4Schristos BMK_SetNbIterations(iters); 960*3117ece4Schristos } 961*3117ece4Schristos break; 962*3117ece4Schristos 963*3117ece4Schristos /* cut input into blocks (benchmark only) */ 964*3117ece4Schristos case 'B': 965*3117ece4Schristos argument++; 966*3117ece4Schristos { size_t bSize = readU32FromChar(&argument); 967*3117ece4Schristos if (toupper(*argument)=='K') bSize<<=10, argument++; /* allows using KB notation */ 968*3117ece4Schristos if (toupper(*argument)=='M') bSize<<=20, argument++; 969*3117ece4Schristos if (toupper(*argument)=='B') argument++; 970*3117ece4Schristos BMK_setNotificationLevel(g_displayLevel); 971*3117ece4Schristos BMK_SetBlockSize(bSize); 972*3117ece4Schristos } 973*3117ece4Schristos break; 974*3117ece4Schristos 975*3117ece4Schristos /* Pause at the end (-p) or set an additional param (-p#) (hidden option) */ 976*3117ece4Schristos case 'p': argument++; 977*3117ece4Schristos if ((*argument>='0') && (*argument<='9')) { 978*3117ece4Schristos BMK_setAdditionalParam((int)readU32FromChar(&argument)); 979*3117ece4Schristos } else 980*3117ece4Schristos main_pause=1; 981*3117ece4Schristos break; 982*3117ece4Schristos /* unknown command */ 983*3117ece4Schristos default : CLEAN_RETURN(badusage(programName)); 984*3117ece4Schristos } 985*3117ece4Schristos } 986*3117ece4Schristos continue; 987*3117ece4Schristos } /* if (argument[0]=='-') */ 988*3117ece4Schristos 989*3117ece4Schristos } /* if (nextArgumentIsAFile==0) */ 990*3117ece4Schristos 991*3117ece4Schristos if (nextEntryIsDictionary) { 992*3117ece4Schristos nextEntryIsDictionary = 0; 993*3117ece4Schristos dictFileName = argument; 994*3117ece4Schristos continue; 995*3117ece4Schristos } 996*3117ece4Schristos 997*3117ece4Schristos /* add filename to list */ 998*3117ece4Schristos UTIL_refFilename(filenames, argument); 999*3117ece4Schristos } 1000*3117ece4Schristos 1001*3117ece4Schristos /* Welcome message (if verbose) */ 1002*3117ece4Schristos DISPLAYLEVEL(3, WELCOME_MESSAGE); 1003*3117ece4Schristos 1004*3117ece4Schristos #ifdef UTIL_HAS_CREATEFILELIST 1005*3117ece4Schristos if (recursive) { 1006*3117ece4Schristos UTIL_expandFNT(&filenames, 1); 1007*3117ece4Schristos } 1008*3117ece4Schristos #endif 1009*3117ece4Schristos 1010*3117ece4Schristos BMK_setNotificationLevel(g_displayLevel); 1011*3117ece4Schristos BMK_benchFiles(filenames->fileNames, (unsigned)filenames->tableSize, dictFileName, cLevel, cLevelLast); 1012*3117ece4Schristos 1013*3117ece4Schristos _end: 1014*3117ece4Schristos if (main_pause) waitEnter(); 1015*3117ece4Schristos free(dynNameSpace); 1016*3117ece4Schristos UTIL_freeFileNamesTable(filenames); 1017*3117ece4Schristos return operationResult; 1018*3117ece4Schristos } 1019