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