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