1 /* 2 * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. 3 * All rights reserved. 4 * 5 * This source code is licensed under both the BSD-style license (found in the 6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 * in the COPYING file in the root directory of this source tree). 8 * You may select, at your option, one of the above-listed licenses. 9 */ 10 11 12 13 /* ************************************** 14 * Tuning parameters 15 ****************************************/ 16 #ifndef BMK_TIMETEST_DEFAULT_S /* default minimum time per test */ 17 #define BMK_TIMETEST_DEFAULT_S 3 18 #endif 19 20 21 /* ************************************** 22 * Compiler Warnings 23 ****************************************/ 24 #ifdef _MSC_VER 25 # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ 26 #endif 27 28 29 /* ************************************* 30 * Includes 31 ***************************************/ 32 #include "platform.h" /* Large Files support */ 33 #include "util.h" /* UTIL_getFileSize, UTIL_sleep */ 34 #include <stdlib.h> /* malloc, free */ 35 #include <string.h> /* memset */ 36 #include <stdio.h> /* fprintf, fopen */ 37 #include <time.h> /* clock_t, clock, CLOCKS_PER_SEC */ 38 39 #include "mem.h" 40 #define ZSTD_STATIC_LINKING_ONLY 41 #include "zstd.h" 42 #include "datagen.h" /* RDG_genBuffer */ 43 #include "xxhash.h" 44 #include "zstdmt_compress.h" 45 46 47 /* ************************************* 48 * Constants 49 ***************************************/ 50 #ifndef ZSTD_GIT_COMMIT 51 # define ZSTD_GIT_COMMIT_STRING "" 52 #else 53 # define ZSTD_GIT_COMMIT_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_GIT_COMMIT) 54 #endif 55 56 #define TIMELOOP_MICROSEC 1*1000000ULL /* 1 second */ 57 #define ACTIVEPERIOD_MICROSEC 70*1000000ULL /* 70 seconds */ 58 #define COOLPERIOD_SEC 10 59 60 #define KB *(1 <<10) 61 #define MB *(1 <<20) 62 #define GB *(1U<<30) 63 64 static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31)); 65 66 static U32 g_compressibilityDefault = 50; 67 68 69 /* ************************************* 70 * console display 71 ***************************************/ 72 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) 73 #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } 74 static int g_displayLevel = 2; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */ 75 76 #define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ 77 if ((clock() - g_time > refreshRate) || (g_displayLevel>=4)) \ 78 { g_time = clock(); DISPLAY(__VA_ARGS__); \ 79 if (g_displayLevel>=4) fflush(stderr); } } 80 static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; 81 static clock_t g_time = 0; 82 83 84 /* ************************************* 85 * Exceptions 86 ***************************************/ 87 #ifndef DEBUG 88 # define DEBUG 0 89 #endif 90 #define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); } 91 #define EXM_THROW(error, ...) { \ 92 DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \ 93 DISPLAYLEVEL(1, "Error %i : ", error); \ 94 DISPLAYLEVEL(1, __VA_ARGS__); \ 95 DISPLAYLEVEL(1, " \n"); \ 96 exit(error); \ 97 } 98 99 100 /* ************************************* 101 * Benchmark Parameters 102 ***************************************/ 103 static int g_additionalParam = 0; 104 static U32 g_decodeOnly = 0; 105 106 void BMK_setNotificationLevel(unsigned level) { g_displayLevel=level; } 107 108 void BMK_setAdditionalParam(int additionalParam) { g_additionalParam=additionalParam; } 109 110 static U32 g_nbSeconds = BMK_TIMETEST_DEFAULT_S; 111 void BMK_setNbSeconds(unsigned nbSeconds) 112 { 113 g_nbSeconds = nbSeconds; 114 DISPLAYLEVEL(3, "- test >= %u seconds per compression / decompression - \n", g_nbSeconds); 115 } 116 117 static size_t g_blockSize = 0; 118 void BMK_setBlockSize(size_t blockSize) 119 { 120 g_blockSize = blockSize; 121 if (g_blockSize) DISPLAYLEVEL(2, "using blocks of size %u KB \n", (U32)(blockSize>>10)); 122 } 123 124 void BMK_setDecodeOnlyMode(unsigned decodeFlag) { g_decodeOnly = (decodeFlag>0); } 125 126 static U32 g_nbThreads = 1; 127 void BMK_setNbThreads(unsigned nbThreads) { 128 #ifndef ZSTD_MULTITHREAD 129 if (nbThreads > 1) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n"); 130 #endif 131 g_nbThreads = nbThreads; 132 } 133 static U32 g_ldmFlag = 0; 134 void BMK_setLdmFlag(unsigned ldmFlag) { 135 g_ldmFlag = ldmFlag; 136 } 137 138 static U32 g_ldmMinMatch = 0; 139 void BMK_setLdmMinMatch(unsigned ldmMinMatch) { 140 g_ldmMinMatch = ldmMinMatch; 141 } 142 143 static U32 g_ldmHashLog = 0; 144 void BMK_setLdmHashLog(unsigned ldmHashLog) { 145 g_ldmHashLog = ldmHashLog; 146 } 147 148 #define BMK_LDM_PARAM_NOTSET 9999 149 static U32 g_ldmBucketSizeLog = BMK_LDM_PARAM_NOTSET; 150 void BMK_setLdmBucketSizeLog(unsigned ldmBucketSizeLog) { 151 g_ldmBucketSizeLog = ldmBucketSizeLog; 152 } 153 154 static U32 g_ldmHashEveryLog = BMK_LDM_PARAM_NOTSET; 155 void BMK_setLdmHashEveryLog(unsigned ldmHashEveryLog) { 156 g_ldmHashEveryLog = ldmHashEveryLog; 157 } 158 159 160 /* ******************************************************** 161 * Bench functions 162 **********************************************************/ 163 typedef struct { 164 const void* srcPtr; 165 size_t srcSize; 166 void* cPtr; 167 size_t cRoom; 168 size_t cSize; 169 void* resPtr; 170 size_t resSize; 171 } blockParam_t; 172 173 174 175 #undef MIN 176 #undef MAX 177 #define MIN(a,b) ((a) < (b) ? (a) : (b)) 178 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 179 180 static int BMK_benchMem(const void* srcBuffer, size_t srcSize, 181 const char* displayName, int cLevel, 182 const size_t* fileSizes, U32 nbFiles, 183 const void* dictBuffer, size_t dictBufferSize, 184 const ZSTD_compressionParameters* comprParams) 185 { 186 size_t const blockSize = ((g_blockSize>=32 && !g_decodeOnly) ? g_blockSize : srcSize) + (!srcSize) /* avoid div by 0 */ ; 187 U32 const maxNbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize) + nbFiles; 188 blockParam_t* const blockTable = (blockParam_t*) malloc(maxNbBlocks * sizeof(blockParam_t)); 189 size_t const maxCompressedSize = ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); /* add some room for safety */ 190 void* const compressedBuffer = malloc(maxCompressedSize); 191 void* resultBuffer = malloc(srcSize); 192 ZSTDMT_CCtx* const mtctx = ZSTDMT_createCCtx(g_nbThreads); 193 ZSTD_CCtx* const ctx = ZSTD_createCCtx(); 194 ZSTD_DCtx* const dctx = ZSTD_createDCtx(); 195 size_t const loadedCompressedSize = srcSize; 196 size_t cSize = 0; 197 double ratio = 0.; 198 U32 nbBlocks; 199 200 /* checks */ 201 if (!compressedBuffer || !resultBuffer || !blockTable || !ctx || !dctx) 202 EXM_THROW(31, "allocation error : not enough memory"); 203 204 /* init */ 205 if (strlen(displayName)>17) displayName += strlen(displayName)-17; /* display last 17 characters */ 206 207 if (g_decodeOnly) { /* benchmark only decompression : source must be already compressed */ 208 const char* srcPtr = (const char*)srcBuffer; 209 U64 totalDSize64 = 0; 210 U32 fileNb; 211 for (fileNb=0; fileNb<nbFiles; fileNb++) { 212 U64 const fSize64 = ZSTD_findDecompressedSize(srcPtr, fileSizes[fileNb]); 213 if (fSize64==0) EXM_THROW(32, "Impossible to determine original size "); 214 totalDSize64 += fSize64; 215 srcPtr += fileSizes[fileNb]; 216 } 217 { size_t const decodedSize = (size_t)totalDSize64; 218 if (totalDSize64 > decodedSize) EXM_THROW(32, "original size is too large"); /* size_t overflow */ 219 free(resultBuffer); 220 resultBuffer = malloc(decodedSize); 221 if (!resultBuffer) EXM_THROW(33, "not enough memory"); 222 cSize = srcSize; 223 srcSize = decodedSize; 224 ratio = (double)srcSize / (double)cSize; 225 } } 226 227 /* Init blockTable data */ 228 { const char* srcPtr = (const char*)srcBuffer; 229 char* cPtr = (char*)compressedBuffer; 230 char* resPtr = (char*)resultBuffer; 231 U32 fileNb; 232 for (nbBlocks=0, fileNb=0; fileNb<nbFiles; fileNb++) { 233 size_t remaining = fileSizes[fileNb]; 234 U32 const nbBlocksforThisFile = g_decodeOnly ? 1 : (U32)((remaining + (blockSize-1)) / blockSize); 235 U32 const blockEnd = nbBlocks + nbBlocksforThisFile; 236 for ( ; nbBlocks<blockEnd; nbBlocks++) { 237 size_t const thisBlockSize = MIN(remaining, blockSize); 238 blockTable[nbBlocks].srcPtr = (const void*)srcPtr; 239 blockTable[nbBlocks].srcSize = thisBlockSize; 240 blockTable[nbBlocks].cPtr = (void*)cPtr; 241 blockTable[nbBlocks].cRoom = g_decodeOnly ? thisBlockSize : ZSTD_compressBound(thisBlockSize); 242 blockTable[nbBlocks].cSize = blockTable[nbBlocks].cRoom; 243 blockTable[nbBlocks].resPtr = (void*)resPtr; 244 blockTable[nbBlocks].resSize = g_decodeOnly ? (size_t) ZSTD_findDecompressedSize(srcPtr, thisBlockSize) : thisBlockSize; 245 srcPtr += thisBlockSize; 246 cPtr += blockTable[nbBlocks].cRoom; 247 resPtr += thisBlockSize; 248 remaining -= thisBlockSize; 249 } } } 250 251 /* warmimg up memory */ 252 RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1); 253 254 /* Bench */ 255 { U64 fastestC = (U64)(-1LL), fastestD = (U64)(-1LL); 256 U64 const crcOrig = g_decodeOnly ? 0 : XXH64(srcBuffer, srcSize, 0); 257 UTIL_time_t coolTime; 258 U64 const maxTime = (g_nbSeconds * TIMELOOP_MICROSEC) + 1; 259 U64 totalCTime=0, totalDTime=0; 260 U32 cCompleted=g_decodeOnly, dCompleted=0; 261 # define NB_MARKS 4 262 const char* const marks[NB_MARKS] = { " |", " /", " =", "\\" }; 263 U32 markNb = 0; 264 265 coolTime = UTIL_getTime(); 266 DISPLAYLEVEL(2, "\r%79s\r", ""); 267 while (!cCompleted || !dCompleted) { 268 269 /* overheat protection */ 270 if (UTIL_clockSpanMicro(coolTime) > ACTIVEPERIOD_MICROSEC) { 271 DISPLAYLEVEL(2, "\rcooling down ... \r"); 272 UTIL_sleep(COOLPERIOD_SEC); 273 coolTime = UTIL_getTime(); 274 } 275 276 if (!g_decodeOnly) { 277 UTIL_time_t clockStart; 278 /* Compression */ 279 DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (U32)srcSize); 280 if (!cCompleted) memset(compressedBuffer, 0xE5, maxCompressedSize); /* warm up and erase result buffer */ 281 282 UTIL_sleepMilli(1); /* give processor time to other processes */ 283 UTIL_waitForNextTick(); 284 clockStart = UTIL_getTime(); 285 286 if (!cCompleted) { /* still some time to do compression tests */ 287 U64 const clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; 288 U32 nbLoops = 0; 289 ZSTD_CDict* cdict = NULL; 290 #ifdef ZSTD_NEWAPI 291 ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbThreads, g_nbThreads); 292 ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionLevel, cLevel); 293 ZSTD_CCtx_setParameter(ctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag); 294 ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmMinMatch, g_ldmMinMatch); 295 ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmHashLog, g_ldmHashLog); 296 if (g_ldmBucketSizeLog != BMK_LDM_PARAM_NOTSET) { 297 ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmBucketSizeLog, g_ldmBucketSizeLog); 298 } 299 if (g_ldmHashEveryLog != BMK_LDM_PARAM_NOTSET) { 300 ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmHashEveryLog, g_ldmHashEveryLog); 301 } 302 ZSTD_CCtx_setParameter(ctx, ZSTD_p_windowLog, comprParams->windowLog); 303 ZSTD_CCtx_setParameter(ctx, ZSTD_p_chainLog, comprParams->chainLog); 304 ZSTD_CCtx_setParameter(ctx, ZSTD_p_searchLog, comprParams->searchLog); 305 ZSTD_CCtx_setParameter(ctx, ZSTD_p_minMatch, comprParams->searchLength); 306 ZSTD_CCtx_setParameter(ctx, ZSTD_p_targetLength, comprParams->targetLength); 307 ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionStrategy, comprParams->strategy); 308 ZSTD_CCtx_loadDictionary(ctx, dictBuffer, dictBufferSize); 309 #else 310 size_t const avgSize = MIN(blockSize, (srcSize / nbFiles)); 311 ZSTD_parameters zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); 312 ZSTD_customMem const cmem = { NULL, NULL, NULL }; 313 if (comprParams->windowLog) zparams.cParams.windowLog = comprParams->windowLog; 314 if (comprParams->chainLog) zparams.cParams.chainLog = comprParams->chainLog; 315 if (comprParams->hashLog) zparams.cParams.hashLog = comprParams->hashLog; 316 if (comprParams->searchLog) zparams.cParams.searchLog = comprParams->searchLog; 317 if (comprParams->searchLength) zparams.cParams.searchLength = comprParams->searchLength; 318 if (comprParams->targetLength) zparams.cParams.targetLength = comprParams->targetLength; 319 if (comprParams->strategy) zparams.cParams.strategy = comprParams->strategy; 320 cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dm_auto, zparams.cParams, cmem); 321 if (cdict==NULL) EXM_THROW(1, "ZSTD_createCDict_advanced() allocation failure"); 322 #endif 323 do { 324 U32 blockNb; 325 for (blockNb=0; blockNb<nbBlocks; blockNb++) { 326 size_t rSize; 327 #ifdef ZSTD_NEWAPI 328 ZSTD_outBuffer out = { blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, 0 }; 329 ZSTD_inBuffer in = { blockTable[blockNb].srcPtr, blockTable[blockNb].srcSize, 0 }; 330 size_t cError = 1; 331 while (cError) { 332 cError = ZSTD_compress_generic(ctx, 333 &out, &in, ZSTD_e_end); 334 if (ZSTD_isError(cError)) 335 EXM_THROW(1, "ZSTD_compress_generic() error : %s", 336 ZSTD_getErrorName(cError)); 337 } 338 rSize = out.pos; 339 #else /* ! ZSTD_NEWAPI */ 340 if (dictBufferSize) { 341 rSize = ZSTD_compress_usingCDict(ctx, 342 blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, 343 blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, 344 cdict); 345 } else { 346 # ifdef ZSTD_MULTITHREAD /* note : limitation : MT single-pass does not support compression with dictionary */ 347 rSize = ZSTDMT_compressCCtx(mtctx, 348 blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, 349 blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, 350 cLevel); 351 # else 352 rSize = ZSTD_compress_advanced (ctx, 353 blockTable[blockNb].cPtr, blockTable[blockNb].cRoom, 354 blockTable[blockNb].srcPtr,blockTable[blockNb].srcSize, 355 NULL, 0, zparams); 356 # endif 357 } 358 if (ZSTD_isError(rSize)) 359 EXM_THROW(1, "ZSTD_compress_usingCDict() failed : %s", 360 ZSTD_getErrorName(rSize)); 361 #endif /* ZSTD_NEWAPI */ 362 blockTable[blockNb].cSize = rSize; 363 } 364 nbLoops++; 365 } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 366 ZSTD_freeCDict(cdict); 367 { U64 const clockSpanMicro = UTIL_clockSpanMicro(clockStart); 368 if (clockSpanMicro < fastestC*nbLoops) fastestC = clockSpanMicro / nbLoops; 369 totalCTime += clockSpanMicro; 370 cCompleted = (totalCTime >= maxTime); 371 } } 372 373 cSize = 0; 374 { U32 blockNb; for (blockNb=0; blockNb<nbBlocks; blockNb++) cSize += blockTable[blockNb].cSize; } 375 ratio = (double)srcSize / (double)cSize; 376 markNb = (markNb+1) % NB_MARKS; 377 DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s\r", 378 marks[markNb], displayName, (U32)srcSize, (U32)cSize, ratio, 379 (double)srcSize / fastestC ); 380 } else { /* g_decodeOnly */ 381 memcpy(compressedBuffer, srcBuffer, loadedCompressedSize); 382 } 383 384 #if 0 /* disable decompression test */ 385 dCompleted=1; 386 (void)totalDTime; (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ 387 #else 388 /* Decompression */ 389 if (!dCompleted) memset(resultBuffer, 0xD6, srcSize); /* warm result buffer */ 390 391 UTIL_sleepMilli(1); /* give processor time to other processes */ 392 UTIL_waitForNextTick(); 393 394 if (!dCompleted) { 395 U64 clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; 396 U32 nbLoops = 0; 397 ZSTD_DDict* const ddict = ZSTD_createDDict(dictBuffer, dictBufferSize); 398 UTIL_time_t const clockStart = UTIL_getTime(); 399 if (!ddict) EXM_THROW(2, "ZSTD_createDDict() allocation failure"); 400 do { 401 U32 blockNb; 402 for (blockNb=0; blockNb<nbBlocks; blockNb++) { 403 size_t const regenSize = ZSTD_decompress_usingDDict(dctx, 404 blockTable[blockNb].resPtr, blockTable[blockNb].resSize, 405 blockTable[blockNb].cPtr, blockTable[blockNb].cSize, 406 ddict); 407 if (ZSTD_isError(regenSize)) { 408 EXM_THROW(2, "ZSTD_decompress_usingDDict() failed on block %u of size %u : %s \n", 409 blockNb, (U32)blockTable[blockNb].cSize, ZSTD_getErrorName(regenSize)); 410 } 411 blockTable[blockNb].resSize = regenSize; 412 } 413 nbLoops++; 414 } while (UTIL_clockSpanMicro(clockStart) < clockLoop); 415 ZSTD_freeDDict(ddict); 416 { U64 const clockSpanMicro = UTIL_clockSpanMicro(clockStart); 417 if (clockSpanMicro < fastestD*nbLoops) fastestD = clockSpanMicro / nbLoops; 418 totalDTime += clockSpanMicro; 419 dCompleted = (totalDTime >= maxTime); 420 } } 421 422 markNb = (markNb+1) % NB_MARKS; 423 DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s ,%6.1f MB/s\r", 424 marks[markNb], displayName, (U32)srcSize, (U32)cSize, ratio, 425 (double)srcSize / fastestC, 426 (double)srcSize / fastestD ); 427 428 /* CRC Checking */ 429 { U64 const crcCheck = XXH64(resultBuffer, srcSize, 0); 430 if (!g_decodeOnly && (crcOrig!=crcCheck)) { 431 size_t u; 432 DISPLAY("!!! WARNING !!! %14s : Invalid Checksum : %x != %x \n", displayName, (unsigned)crcOrig, (unsigned)crcCheck); 433 for (u=0; u<srcSize; u++) { 434 if (((const BYTE*)srcBuffer)[u] != ((const BYTE*)resultBuffer)[u]) { 435 U32 segNb, bNb, pos; 436 size_t bacc = 0; 437 DISPLAY("Decoding error at pos %u ", (U32)u); 438 for (segNb = 0; segNb < nbBlocks; segNb++) { 439 if (bacc + blockTable[segNb].srcSize > u) break; 440 bacc += blockTable[segNb].srcSize; 441 } 442 pos = (U32)(u - bacc); 443 bNb = pos / (128 KB); 444 DISPLAY("(sample %u, block %u, pos %u) \n", segNb, bNb, pos); 445 if (u>5) { 446 int n; 447 for (n=-5; n<0; n++) DISPLAY("%02X ", ((const BYTE*)srcBuffer)[u+n]); 448 DISPLAY(" :%02X: ", ((const BYTE*)srcBuffer)[u]); 449 for (n=1; n<3; n++) DISPLAY("%02X ", ((const BYTE*)srcBuffer)[u+n]); 450 DISPLAY(" \n"); 451 for (n=-5; n<0; n++) DISPLAY("%02X ", ((const BYTE*)resultBuffer)[u+n]); 452 DISPLAY(" :%02X: ", ((const BYTE*)resultBuffer)[u]); 453 for (n=1; n<3; n++) DISPLAY("%02X ", ((const BYTE*)resultBuffer)[u+n]); 454 DISPLAY(" \n"); 455 } 456 break; 457 } 458 if (u==srcSize-1) { /* should never happen */ 459 DISPLAY("no difference detected\n"); 460 } } 461 break; 462 } } /* CRC Checking */ 463 #endif 464 } /* for (testNb = 1; testNb <= (g_nbSeconds + !g_nbSeconds); testNb++) */ 465 466 if (g_displayLevel == 1) { 467 double cSpeed = (double)srcSize / fastestC; 468 double dSpeed = (double)srcSize / fastestD; 469 if (g_additionalParam) 470 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); 471 else 472 DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName); 473 } 474 DISPLAYLEVEL(2, "%2i#\n", cLevel); 475 } /* Bench */ 476 477 /* clean up */ 478 free(blockTable); 479 free(compressedBuffer); 480 free(resultBuffer); 481 ZSTDMT_freeCCtx(mtctx); 482 ZSTD_freeCCtx(ctx); 483 ZSTD_freeDCtx(dctx); 484 return 0; 485 } 486 487 488 static size_t BMK_findMaxMem(U64 requiredMem) 489 { 490 size_t const step = 64 MB; 491 BYTE* testmem = NULL; 492 493 requiredMem = (((requiredMem >> 26) + 1) << 26); 494 requiredMem += step; 495 if (requiredMem > maxMemory) requiredMem = maxMemory; 496 497 do { 498 testmem = (BYTE*)malloc((size_t)requiredMem); 499 requiredMem -= step; 500 } while (!testmem); 501 502 free(testmem); 503 return (size_t)(requiredMem); 504 } 505 506 static void BMK_benchCLevel(void* srcBuffer, size_t benchedSize, 507 const char* displayName, int cLevel, int cLevelLast, 508 const size_t* fileSizes, unsigned nbFiles, 509 const void* dictBuffer, size_t dictBufferSize, 510 ZSTD_compressionParameters *compressionParams, int setRealTimePrio) 511 { 512 int l; 513 514 const char* pch = strrchr(displayName, '\\'); /* Windows */ 515 if (!pch) pch = strrchr(displayName, '/'); /* Linux */ 516 if (pch) displayName = pch+1; 517 518 if (setRealTimePrio) { 519 DISPLAYLEVEL(2, "Note : switching to a real-time priority \n"); 520 SET_REALTIME_PRIORITY; 521 } 522 523 if (g_displayLevel == 1 && !g_additionalParam) 524 DISPLAY("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n", ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING, (U32)benchedSize, g_nbSeconds, (U32)(g_blockSize>>10)); 525 526 if (cLevelLast < cLevel) cLevelLast = cLevel; 527 528 for (l=cLevel; l <= cLevelLast; l++) { 529 BMK_benchMem(srcBuffer, benchedSize, 530 displayName, l, 531 fileSizes, nbFiles, 532 dictBuffer, dictBufferSize, compressionParams); 533 } 534 } 535 536 537 /*! BMK_loadFiles() : 538 Loads `buffer` with content of files listed within `fileNamesTable`. 539 At most, fills `buffer` entirely */ 540 static void BMK_loadFiles(void* buffer, size_t bufferSize, 541 size_t* fileSizes, 542 const char** fileNamesTable, unsigned nbFiles) 543 { 544 size_t pos = 0, totalSize = 0; 545 unsigned n; 546 for (n=0; n<nbFiles; n++) { 547 FILE* f; 548 U64 fileSize = UTIL_getFileSize(fileNamesTable[n]); 549 if (UTIL_isDirectory(fileNamesTable[n])) { 550 DISPLAYLEVEL(2, "Ignoring %s directory... \n", fileNamesTable[n]); 551 fileSizes[n] = 0; 552 continue; 553 } 554 f = fopen(fileNamesTable[n], "rb"); 555 if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]); 556 DISPLAYUPDATE(2, "Loading %s... \r", fileNamesTable[n]); 557 if (fileSize > bufferSize-pos) fileSize = bufferSize-pos, nbFiles=n; /* buffer too small - stop after this file */ 558 { size_t const readSize = fread(((char*)buffer)+pos, 1, (size_t)fileSize, f); 559 if (readSize != (size_t)fileSize) EXM_THROW(11, "could not read %s", fileNamesTable[n]); 560 pos += readSize; } 561 fileSizes[n] = (size_t)fileSize; 562 totalSize += (size_t)fileSize; 563 fclose(f); 564 } 565 566 if (totalSize == 0) EXM_THROW(12, "no data to bench"); 567 } 568 569 static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, int cLevel, 570 int cLevelLast, ZSTD_compressionParameters *compressionParams, int setRealTimePrio) 571 { 572 void* srcBuffer; 573 size_t benchedSize; 574 void* dictBuffer = NULL; 575 size_t dictBufferSize = 0; 576 size_t* fileSizes = (size_t*)malloc(nbFiles * sizeof(size_t)); 577 U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles); 578 char mfName[20] = {0}; 579 580 if (!fileSizes) EXM_THROW(12, "not enough memory for fileSizes"); 581 582 /* Load dictionary */ 583 if (dictFileName != NULL) { 584 U64 dictFileSize = UTIL_getFileSize(dictFileName); 585 if (dictFileSize > 64 MB) EXM_THROW(10, "dictionary file %s too large", dictFileName); 586 dictBufferSize = (size_t)dictFileSize; 587 dictBuffer = malloc(dictBufferSize); 588 if (dictBuffer==NULL) EXM_THROW(11, "not enough memory for dictionary (%u bytes)", (U32)dictBufferSize); 589 BMK_loadFiles(dictBuffer, dictBufferSize, fileSizes, &dictFileName, 1); 590 } 591 592 /* Memory allocation & restrictions */ 593 benchedSize = BMK_findMaxMem(totalSizeToLoad * 3) / 3; 594 if ((U64)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; 595 if (benchedSize < totalSizeToLoad) 596 DISPLAY("Not enough memory; testing %u MB only...\n", (U32)(benchedSize >> 20)); 597 srcBuffer = malloc(benchedSize); 598 if (!srcBuffer) EXM_THROW(12, "not enough memory"); 599 600 /* Load input buffer */ 601 BMK_loadFiles(srcBuffer, benchedSize, fileSizes, fileNamesTable, nbFiles); 602 603 /* Bench */ 604 snprintf (mfName, sizeof(mfName), " %u files", nbFiles); 605 { const char* displayName = (nbFiles > 1) ? mfName : fileNamesTable[0]; 606 BMK_benchCLevel(srcBuffer, benchedSize, 607 displayName, cLevel, cLevelLast, 608 fileSizes, nbFiles, 609 dictBuffer, dictBufferSize, compressionParams, setRealTimePrio); 610 } 611 612 /* clean up */ 613 free(srcBuffer); 614 free(dictBuffer); 615 free(fileSizes); 616 } 617 618 619 static void BMK_syntheticTest(int cLevel, int cLevelLast, double compressibility, ZSTD_compressionParameters* compressionParams, int setRealTimePrio) 620 { 621 char name[20] = {0}; 622 size_t benchedSize = 10000000; 623 void* const srcBuffer = malloc(benchedSize); 624 625 /* Memory allocation */ 626 if (!srcBuffer) EXM_THROW(21, "not enough memory"); 627 628 /* Fill input buffer */ 629 RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0); 630 631 /* Bench */ 632 snprintf (name, sizeof(name), "Synthetic %2u%%", (unsigned)(compressibility*100)); 633 BMK_benchCLevel(srcBuffer, benchedSize, name, cLevel, cLevelLast, &benchedSize, 1, NULL, 0, compressionParams, setRealTimePrio); 634 635 /* clean up */ 636 free(srcBuffer); 637 } 638 639 640 int BMK_benchFiles(const char** fileNamesTable, unsigned nbFiles, const char* dictFileName, 641 int cLevel, int cLevelLast, ZSTD_compressionParameters* compressionParams, int setRealTimePrio) 642 { 643 double const compressibility = (double)g_compressibilityDefault / 100; 644 645 if (cLevel < 1) cLevel = 1; /* minimum compression level */ 646 if (cLevel > ZSTD_maxCLevel()) cLevel = ZSTD_maxCLevel(); 647 if (cLevelLast > ZSTD_maxCLevel()) cLevelLast = ZSTD_maxCLevel(); 648 if (cLevelLast < cLevel) cLevelLast = cLevel; 649 if (cLevelLast > cLevel) DISPLAYLEVEL(2, "Benchmarking levels from %d to %d\n", cLevel, cLevelLast); 650 651 if (nbFiles == 0) 652 BMK_syntheticTest(cLevel, cLevelLast, compressibility, compressionParams, setRealTimePrio); 653 else 654 BMK_benchFileTable(fileNamesTable, nbFiles, dictFileName, cLevel, cLevelLast, compressionParams, setRealTimePrio); 655 return 0; 656 } 657