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 * Tuning parameters 14 ****************************************/ 15 #ifndef BMK_TIMETEST_DEFAULT_S /* default minimum time per test */ 16 #define BMK_TIMETEST_DEFAULT_S 3 17 #endif 18 19 20 /* ************************************* 21 * Includes 22 ***************************************/ 23 #include "platform.h" /* Large Files support */ 24 #include "util.h" /* UTIL_getFileSize, UTIL_sleep */ 25 #include <stdlib.h> /* malloc, free */ 26 #include <string.h> /* memset, strerror */ 27 #include <stdio.h> /* fprintf, fopen */ 28 #include <errno.h> 29 #include <assert.h> /* assert */ 30 31 #include "benchfn.h" 32 #include "mem.h" 33 #define ZSTD_STATIC_LINKING_ONLY 34 #include "zstd.h" 35 #include "datagen.h" /* RDG_genBuffer */ 36 #include "xxhash.h" 37 #include "benchzstd.h" 38 #include "zstd_errors.h" 39 40 41 /* ************************************* 42 * Constants 43 ***************************************/ 44 #ifndef ZSTD_GIT_COMMIT 45 # define ZSTD_GIT_COMMIT_STRING "" 46 #else 47 # define ZSTD_GIT_COMMIT_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_GIT_COMMIT) 48 #endif 49 50 #define TIMELOOP_MICROSEC (1*1000000ULL) /* 1 second */ 51 #define TIMELOOP_NANOSEC (1*1000000000ULL) /* 1 second */ 52 #define ACTIVEPERIOD_MICROSEC (70*TIMELOOP_MICROSEC) /* 70 seconds */ 53 #define COOLPERIOD_SEC 10 54 55 #define KB *(1 <<10) 56 #define MB *(1 <<20) 57 #define GB *(1U<<30) 58 59 #define BMK_RUNTEST_DEFAULT_MS 1000 60 61 static const size_t maxMemory = (sizeof(size_t)==4) ? 62 /* 32-bit */ (2 GB - 64 MB) : 63 /* 64-bit */ (size_t)(1ULL << ((sizeof(size_t)*8)-31)); 64 65 66 /* ************************************* 67 * console display 68 ***************************************/ 69 #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) 70 #define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); } 71 /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */ 72 73 static const U64 g_refreshRate = SEC_TO_MICRO / 6; 74 static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; 75 76 #define DISPLAYUPDATE(l, ...) { if (displayLevel>=l) { \ 77 if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (displayLevel>=4)) \ 78 { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ 79 if (displayLevel>=4) fflush(stderr); } } } 80 81 82 /* ************************************* 83 * Exceptions 84 ***************************************/ 85 #ifndef DEBUG 86 # define DEBUG 0 87 #endif 88 #define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); } 89 90 #define EXM_THROW_INT(errorNum, ...) { \ 91 DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \ 92 DISPLAYLEVEL(1, "Error %i : ", errorNum); \ 93 DISPLAYLEVEL(1, __VA_ARGS__); \ 94 DISPLAYLEVEL(1, " \n"); \ 95 return errorNum; \ 96 } 97 98 #define CHECK_Z(zf) { \ 99 size_t const zerr = zf; \ 100 if (ZSTD_isError(zerr)) { \ 101 DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \ 102 DISPLAY("Error : "); \ 103 DISPLAY("%s failed : %s", \ 104 #zf, ZSTD_getErrorName(zerr)); \ 105 DISPLAY(" \n"); \ 106 exit(1); \ 107 } \ 108 } 109 110 #define RETURN_ERROR(errorNum, retType, ...) { \ 111 retType r; \ 112 memset(&r, 0, sizeof(retType)); \ 113 DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \ 114 DISPLAYLEVEL(1, "Error %i : ", errorNum); \ 115 DISPLAYLEVEL(1, __VA_ARGS__); \ 116 DISPLAYLEVEL(1, " \n"); \ 117 r.tag = errorNum; \ 118 return r; \ 119 } 120 121 122 /* ************************************* 123 * Benchmark Parameters 124 ***************************************/ 125 126 BMK_advancedParams_t BMK_initAdvancedParams(void) { 127 BMK_advancedParams_t const res = { 128 BMK_both, /* mode */ 129 BMK_TIMETEST_DEFAULT_S, /* nbSeconds */ 130 0, /* blockSize */ 131 0, /* nbWorkers */ 132 0, /* realTime */ 133 0, /* additionalParam */ 134 0, /* ldmFlag */ 135 0, /* ldmMinMatch */ 136 0, /* ldmHashLog */ 137 0, /* ldmBuckSizeLog */ 138 0 /* ldmHashRateLog */ 139 }; 140 return res; 141 } 142 143 144 /* ******************************************************** 145 * Bench functions 146 **********************************************************/ 147 typedef struct { 148 const void* srcPtr; 149 size_t srcSize; 150 void* cPtr; 151 size_t cRoom; 152 size_t cSize; 153 void* resPtr; 154 size_t resSize; 155 } blockParam_t; 156 157 #undef MIN 158 #undef MAX 159 #define MIN(a,b) ((a) < (b) ? (a) : (b)) 160 #define MAX(a,b) ((a) > (b) ? (a) : (b)) 161 162 static void BMK_initCCtx(ZSTD_CCtx* ctx, 163 const void* dictBuffer, size_t dictBufferSize, int cLevel, 164 const ZSTD_compressionParameters* comprParams, const BMK_advancedParams_t* adv) { 165 ZSTD_CCtx_reset(ctx, ZSTD_reset_session_and_parameters); 166 if (adv->nbWorkers==1) { 167 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_nbWorkers, 0)); 168 } else { 169 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_nbWorkers, adv->nbWorkers)); 170 } 171 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, cLevel)); 172 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_enableLongDistanceMatching, adv->ldmFlag)); 173 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_ldmMinMatch, adv->ldmMinMatch)); 174 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_ldmHashLog, adv->ldmHashLog)); 175 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_ldmBucketSizeLog, adv->ldmBucketSizeLog)); 176 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_ldmHashRateLog, adv->ldmHashRateLog)); 177 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_windowLog, comprParams->windowLog)); 178 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_hashLog, comprParams->hashLog)); 179 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_chainLog, comprParams->chainLog)); 180 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_searchLog, comprParams->searchLog)); 181 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_minMatch, comprParams->minMatch)); 182 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_targetLength, comprParams->targetLength)); 183 CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_strategy, comprParams->strategy)); 184 CHECK_Z(ZSTD_CCtx_loadDictionary(ctx, dictBuffer, dictBufferSize)); 185 } 186 187 static void BMK_initDCtx(ZSTD_DCtx* dctx, 188 const void* dictBuffer, size_t dictBufferSize) { 189 CHECK_Z(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters)); 190 CHECK_Z(ZSTD_DCtx_loadDictionary(dctx, dictBuffer, dictBufferSize)); 191 } 192 193 194 typedef struct { 195 ZSTD_CCtx* cctx; 196 const void* dictBuffer; 197 size_t dictBufferSize; 198 int cLevel; 199 const ZSTD_compressionParameters* comprParams; 200 const BMK_advancedParams_t* adv; 201 } BMK_initCCtxArgs; 202 203 static size_t local_initCCtx(void* payload) { 204 BMK_initCCtxArgs* ag = (BMK_initCCtxArgs*)payload; 205 BMK_initCCtx(ag->cctx, ag->dictBuffer, ag->dictBufferSize, ag->cLevel, ag->comprParams, ag->adv); 206 return 0; 207 } 208 209 typedef struct { 210 ZSTD_DCtx* dctx; 211 const void* dictBuffer; 212 size_t dictBufferSize; 213 } BMK_initDCtxArgs; 214 215 static size_t local_initDCtx(void* payload) { 216 BMK_initDCtxArgs* ag = (BMK_initDCtxArgs*)payload; 217 BMK_initDCtx(ag->dctx, ag->dictBuffer, ag->dictBufferSize); 218 return 0; 219 } 220 221 222 /* `addArgs` is the context */ 223 static size_t local_defaultCompress( 224 const void* srcBuffer, size_t srcSize, 225 void* dstBuffer, size_t dstSize, 226 void* addArgs) 227 { 228 ZSTD_CCtx* const cctx = (ZSTD_CCtx*)addArgs; 229 return ZSTD_compress2(cctx, dstBuffer, dstSize, srcBuffer, srcSize); 230 } 231 232 /* `addArgs` is the context */ 233 static size_t local_defaultDecompress( 234 const void* srcBuffer, size_t srcSize, 235 void* dstBuffer, size_t dstCapacity, 236 void* addArgs) 237 { 238 size_t moreToFlush = 1; 239 ZSTD_DCtx* const dctx = (ZSTD_DCtx*)addArgs; 240 ZSTD_inBuffer in; 241 ZSTD_outBuffer out; 242 in.src = srcBuffer; in.size = srcSize; in.pos = 0; 243 out.dst = dstBuffer; out.size = dstCapacity; out.pos = 0; 244 while (moreToFlush) { 245 if(out.pos == out.size) { 246 return (size_t)-ZSTD_error_dstSize_tooSmall; 247 } 248 moreToFlush = ZSTD_decompressStream(dctx, &out, &in); 249 if (ZSTD_isError(moreToFlush)) { 250 return moreToFlush; 251 } 252 } 253 return out.pos; 254 255 } 256 257 258 /* ================================================================= */ 259 /* Benchmark Zstandard, mem-to-mem scenarios */ 260 /* ================================================================= */ 261 262 int BMK_isSuccessful_benchOutcome(BMK_benchOutcome_t outcome) 263 { 264 return outcome.tag == 0; 265 } 266 267 BMK_benchResult_t BMK_extract_benchResult(BMK_benchOutcome_t outcome) 268 { 269 assert(outcome.tag == 0); 270 return outcome.internal_never_use_directly; 271 } 272 273 static BMK_benchOutcome_t BMK_benchOutcome_error(void) 274 { 275 BMK_benchOutcome_t b; 276 memset(&b, 0, sizeof(b)); 277 b.tag = 1; 278 return b; 279 } 280 281 static BMK_benchOutcome_t BMK_benchOutcome_setValidResult(BMK_benchResult_t result) 282 { 283 BMK_benchOutcome_t b; 284 b.tag = 0; 285 b.internal_never_use_directly = result; 286 return b; 287 } 288 289 290 /* benchMem with no allocation */ 291 static BMK_benchOutcome_t 292 BMK_benchMemAdvancedNoAlloc( 293 const void** srcPtrs, size_t* srcSizes, 294 void** cPtrs, size_t* cCapacities, size_t* cSizes, 295 void** resPtrs, size_t* resSizes, 296 void** resultBufferPtr, void* compressedBuffer, 297 size_t maxCompressedSize, 298 BMK_timedFnState_t* timeStateCompress, 299 BMK_timedFnState_t* timeStateDecompress, 300 301 const void* srcBuffer, size_t srcSize, 302 const size_t* fileSizes, unsigned nbFiles, 303 const int cLevel, 304 const ZSTD_compressionParameters* comprParams, 305 const void* dictBuffer, size_t dictBufferSize, 306 ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, 307 int displayLevel, const char* displayName, 308 const BMK_advancedParams_t* adv) 309 { 310 size_t const blockSize = ((adv->blockSize>=32 && (adv->mode != BMK_decodeOnly)) ? adv->blockSize : srcSize) + (!srcSize); /* avoid div by 0 */ 311 BMK_benchResult_t benchResult; 312 size_t const loadedCompressedSize = srcSize; 313 size_t cSize = 0; 314 double ratio = 0.; 315 U32 nbBlocks; 316 317 assert(cctx != NULL); assert(dctx != NULL); 318 319 /* init */ 320 memset(&benchResult, 0, sizeof(benchResult)); 321 if (strlen(displayName)>17) displayName += strlen(displayName) - 17; /* display last 17 characters */ 322 if (adv->mode == BMK_decodeOnly) { /* benchmark only decompression : source must be already compressed */ 323 const char* srcPtr = (const char*)srcBuffer; 324 U64 totalDSize64 = 0; 325 U32 fileNb; 326 for (fileNb=0; fileNb<nbFiles; fileNb++) { 327 U64 const fSize64 = ZSTD_findDecompressedSize(srcPtr, fileSizes[fileNb]); 328 if (fSize64==0) RETURN_ERROR(32, BMK_benchOutcome_t, "Impossible to determine original size "); 329 totalDSize64 += fSize64; 330 srcPtr += fileSizes[fileNb]; 331 } 332 { size_t const decodedSize = (size_t)totalDSize64; 333 assert((U64)decodedSize == totalDSize64); /* check overflow */ 334 free(*resultBufferPtr); 335 *resultBufferPtr = malloc(decodedSize); 336 if (!(*resultBufferPtr)) { 337 RETURN_ERROR(33, BMK_benchOutcome_t, "not enough memory"); 338 } 339 if (totalDSize64 > decodedSize) { /* size_t overflow */ 340 free(*resultBufferPtr); 341 RETURN_ERROR(32, BMK_benchOutcome_t, "original size is too large"); 342 } 343 cSize = srcSize; 344 srcSize = decodedSize; 345 ratio = (double)srcSize / (double)cSize; 346 } 347 } 348 349 /* Init data blocks */ 350 { const char* srcPtr = (const char*)srcBuffer; 351 char* cPtr = (char*)compressedBuffer; 352 char* resPtr = (char*)(*resultBufferPtr); 353 U32 fileNb; 354 for (nbBlocks=0, fileNb=0; fileNb<nbFiles; fileNb++) { 355 size_t remaining = fileSizes[fileNb]; 356 U32 const nbBlocksforThisFile = (adv->mode == BMK_decodeOnly) ? 1 : (U32)((remaining + (blockSize-1)) / blockSize); 357 U32 const blockEnd = nbBlocks + nbBlocksforThisFile; 358 for ( ; nbBlocks<blockEnd; nbBlocks++) { 359 size_t const thisBlockSize = MIN(remaining, blockSize); 360 srcPtrs[nbBlocks] = srcPtr; 361 srcSizes[nbBlocks] = thisBlockSize; 362 cPtrs[nbBlocks] = cPtr; 363 cCapacities[nbBlocks] = (adv->mode == BMK_decodeOnly) ? thisBlockSize : ZSTD_compressBound(thisBlockSize); 364 resPtrs[nbBlocks] = resPtr; 365 resSizes[nbBlocks] = (adv->mode == BMK_decodeOnly) ? (size_t) ZSTD_findDecompressedSize(srcPtr, thisBlockSize) : thisBlockSize; 366 srcPtr += thisBlockSize; 367 cPtr += cCapacities[nbBlocks]; 368 resPtr += thisBlockSize; 369 remaining -= thisBlockSize; 370 if (adv->mode == BMK_decodeOnly) { 371 assert(nbBlocks==0); 372 cSizes[nbBlocks] = thisBlockSize; 373 benchResult.cSize = thisBlockSize; 374 } 375 } 376 } 377 } 378 379 /* warmimg up `compressedBuffer` */ 380 if (adv->mode == BMK_decodeOnly) { 381 memcpy(compressedBuffer, srcBuffer, loadedCompressedSize); 382 } else { 383 RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1); 384 } 385 386 /* Bench */ 387 { U64 const crcOrig = (adv->mode == BMK_decodeOnly) ? 0 : XXH64(srcBuffer, srcSize, 0); 388 # define NB_MARKS 4 389 const char* marks[NB_MARKS] = { " |", " /", " =", " \\" }; 390 U32 markNb = 0; 391 int compressionCompleted = (adv->mode == BMK_decodeOnly); 392 int decompressionCompleted = (adv->mode == BMK_compressOnly); 393 BMK_benchParams_t cbp, dbp; 394 BMK_initCCtxArgs cctxprep; 395 BMK_initDCtxArgs dctxprep; 396 397 cbp.benchFn = local_defaultCompress; 398 cbp.benchPayload = cctx; 399 cbp.initFn = local_initCCtx; 400 cbp.initPayload = &cctxprep; 401 cbp.errorFn = ZSTD_isError; 402 cbp.blockCount = nbBlocks; 403 cbp.srcBuffers = srcPtrs; 404 cbp.srcSizes = srcSizes; 405 cbp.dstBuffers = cPtrs; 406 cbp.dstCapacities = cCapacities; 407 cbp.blockResults = cSizes; 408 409 cctxprep.cctx = cctx; 410 cctxprep.dictBuffer = dictBuffer; 411 cctxprep.dictBufferSize = dictBufferSize; 412 cctxprep.cLevel = cLevel; 413 cctxprep.comprParams = comprParams; 414 cctxprep.adv = adv; 415 416 dbp.benchFn = local_defaultDecompress; 417 dbp.benchPayload = dctx; 418 dbp.initFn = local_initDCtx; 419 dbp.initPayload = &dctxprep; 420 dbp.errorFn = ZSTD_isError; 421 dbp.blockCount = nbBlocks; 422 dbp.srcBuffers = (const void* const *) cPtrs; 423 dbp.srcSizes = cSizes; 424 dbp.dstBuffers = resPtrs; 425 dbp.dstCapacities = resSizes; 426 dbp.blockResults = NULL; 427 428 dctxprep.dctx = dctx; 429 dctxprep.dictBuffer = dictBuffer; 430 dctxprep.dictBufferSize = dictBufferSize; 431 432 DISPLAYLEVEL(2, "\r%70s\r", ""); /* blank line */ 433 DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->\r", marks[markNb], displayName, (unsigned)srcSize); 434 435 while (!(compressionCompleted && decompressionCompleted)) { 436 if (!compressionCompleted) { 437 BMK_runOutcome_t const cOutcome = BMK_benchTimedFn( timeStateCompress, cbp); 438 439 if (!BMK_isSuccessful_runOutcome(cOutcome)) { 440 return BMK_benchOutcome_error(); 441 } 442 443 { BMK_runTime_t const cResult = BMK_extract_runTime(cOutcome); 444 cSize = cResult.sumOfReturn; 445 ratio = (double)srcSize / cSize; 446 { BMK_benchResult_t newResult; 447 newResult.cSpeed = ((U64)srcSize * TIMELOOP_NANOSEC / cResult.nanoSecPerRun); 448 benchResult.cSize = cSize; 449 if (newResult.cSpeed > benchResult.cSpeed) 450 benchResult.cSpeed = newResult.cSpeed; 451 } } 452 453 { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; 454 DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s\r", 455 marks[markNb], displayName, 456 (unsigned)srcSize, (unsigned)cSize, 457 ratioAccuracy, ratio, 458 benchResult.cSpeed < (10 MB) ? 2 : 1, (double)benchResult.cSpeed / MB_UNIT); 459 } 460 compressionCompleted = BMK_isCompleted_TimedFn(timeStateCompress); 461 } 462 463 if(!decompressionCompleted) { 464 BMK_runOutcome_t const dOutcome = BMK_benchTimedFn(timeStateDecompress, dbp); 465 466 if(!BMK_isSuccessful_runOutcome(dOutcome)) { 467 return BMK_benchOutcome_error(); 468 } 469 470 { BMK_runTime_t const dResult = BMK_extract_runTime(dOutcome); 471 U64 const newDSpeed = (srcSize * TIMELOOP_NANOSEC / dResult.nanoSecPerRun); 472 if (newDSpeed > benchResult.dSpeed) 473 benchResult.dSpeed = newDSpeed; 474 } 475 476 { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; 477 DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s ,%6.1f MB/s \r", 478 marks[markNb], displayName, 479 (unsigned)srcSize, (unsigned)benchResult.cSize, 480 ratioAccuracy, ratio, 481 benchResult.cSpeed < (10 MB) ? 2 : 1, (double)benchResult.cSpeed / MB_UNIT, 482 (double)benchResult.dSpeed / MB_UNIT); 483 } 484 decompressionCompleted = BMK_isCompleted_TimedFn(timeStateDecompress); 485 } 486 markNb = (markNb+1) % NB_MARKS; 487 } /* while (!(compressionCompleted && decompressionCompleted)) */ 488 489 /* CRC Checking */ 490 { const BYTE* resultBuffer = (const BYTE*)(*resultBufferPtr); 491 U64 const crcCheck = XXH64(resultBuffer, srcSize, 0); 492 if ((adv->mode == BMK_both) && (crcOrig!=crcCheck)) { 493 size_t u; 494 DISPLAY("!!! WARNING !!! %14s : Invalid Checksum : %x != %x \n", 495 displayName, (unsigned)crcOrig, (unsigned)crcCheck); 496 for (u=0; u<srcSize; u++) { 497 if (((const BYTE*)srcBuffer)[u] != resultBuffer[u]) { 498 unsigned segNb, bNb, pos; 499 size_t bacc = 0; 500 DISPLAY("Decoding error at pos %u ", (unsigned)u); 501 for (segNb = 0; segNb < nbBlocks; segNb++) { 502 if (bacc + srcSizes[segNb] > u) break; 503 bacc += srcSizes[segNb]; 504 } 505 pos = (U32)(u - bacc); 506 bNb = pos / (128 KB); 507 DISPLAY("(sample %u, block %u, pos %u) \n", segNb, bNb, pos); 508 if (u>5) { 509 int n; 510 DISPLAY("origin: "); 511 for (n=-5; n<0; n++) DISPLAY("%02X ", ((const BYTE*)srcBuffer)[u+n]); 512 DISPLAY(" :%02X: ", ((const BYTE*)srcBuffer)[u]); 513 for (n=1; n<3; n++) DISPLAY("%02X ", ((const BYTE*)srcBuffer)[u+n]); 514 DISPLAY(" \n"); 515 DISPLAY("decode: "); 516 for (n=-5; n<0; n++) DISPLAY("%02X ", resultBuffer[u+n]); 517 DISPLAY(" :%02X: ", resultBuffer[u]); 518 for (n=1; n<3; n++) DISPLAY("%02X ", resultBuffer[u+n]); 519 DISPLAY(" \n"); 520 } 521 break; 522 } 523 if (u==srcSize-1) { /* should never happen */ 524 DISPLAY("no difference detected\n"); 525 } 526 } 527 } 528 } /* CRC Checking */ 529 530 if (displayLevel == 1) { /* hidden display mode -q, used by python speed benchmark */ 531 double const cSpeed = (double)benchResult.cSpeed / MB_UNIT; 532 double const dSpeed = (double)benchResult.dSpeed / MB_UNIT; 533 if (adv->additionalParam) { 534 DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName, adv->additionalParam); 535 } else { 536 DISPLAY("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s\n", cLevel, (int)cSize, ratio, cSpeed, dSpeed, displayName); 537 } 538 } 539 540 DISPLAYLEVEL(2, "%2i#\n", cLevel); 541 } /* Bench */ 542 543 benchResult.cMem = (1ULL << (comprParams->windowLog)) + ZSTD_sizeof_CCtx(cctx); 544 return BMK_benchOutcome_setValidResult(benchResult); 545 } 546 547 BMK_benchOutcome_t BMK_benchMemAdvanced(const void* srcBuffer, size_t srcSize, 548 void* dstBuffer, size_t dstCapacity, 549 const size_t* fileSizes, unsigned nbFiles, 550 int cLevel, const ZSTD_compressionParameters* comprParams, 551 const void* dictBuffer, size_t dictBufferSize, 552 int displayLevel, const char* displayName, const BMK_advancedParams_t* adv) 553 554 { 555 int const dstParamsError = !dstBuffer ^ !dstCapacity; /* must be both NULL or none */ 556 557 size_t const blockSize = ((adv->blockSize>=32 && (adv->mode != BMK_decodeOnly)) ? adv->blockSize : srcSize) + (!srcSize) /* avoid div by 0 */ ; 558 U32 const maxNbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize) + nbFiles; 559 560 /* these are the blockTable parameters, just split up */ 561 const void ** const srcPtrs = (const void**)malloc(maxNbBlocks * sizeof(void*)); 562 size_t* const srcSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); 563 564 565 void ** const cPtrs = (void**)malloc(maxNbBlocks * sizeof(void*)); 566 size_t* const cSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); 567 size_t* const cCapacities = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); 568 569 void ** const resPtrs = (void**)malloc(maxNbBlocks * sizeof(void*)); 570 size_t* const resSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); 571 572 BMK_timedFnState_t* timeStateCompress = BMK_createTimedFnState(adv->nbSeconds * 1000, BMK_RUNTEST_DEFAULT_MS); 573 BMK_timedFnState_t* timeStateDecompress = BMK_createTimedFnState(adv->nbSeconds * 1000, BMK_RUNTEST_DEFAULT_MS); 574 575 ZSTD_CCtx* const cctx = ZSTD_createCCtx(); 576 ZSTD_DCtx* const dctx = ZSTD_createDCtx(); 577 578 const size_t maxCompressedSize = dstCapacity ? dstCapacity : ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); 579 580 void* const internalDstBuffer = dstBuffer ? NULL : malloc(maxCompressedSize); 581 void* const compressedBuffer = dstBuffer ? dstBuffer : internalDstBuffer; 582 583 BMK_benchOutcome_t outcome = BMK_benchOutcome_error(); /* error by default */ 584 585 void* resultBuffer = srcSize ? malloc(srcSize) : NULL; 586 587 int allocationincomplete = !srcPtrs || !srcSizes || !cPtrs || 588 !cSizes || !cCapacities || !resPtrs || !resSizes || 589 !timeStateCompress || !timeStateDecompress || 590 !cctx || !dctx || 591 !compressedBuffer || !resultBuffer; 592 593 594 if (!allocationincomplete && !dstParamsError) { 595 outcome = BMK_benchMemAdvancedNoAlloc(srcPtrs, srcSizes, 596 cPtrs, cCapacities, cSizes, 597 resPtrs, resSizes, 598 &resultBuffer, 599 compressedBuffer, maxCompressedSize, 600 timeStateCompress, timeStateDecompress, 601 srcBuffer, srcSize, 602 fileSizes, nbFiles, 603 cLevel, comprParams, 604 dictBuffer, dictBufferSize, 605 cctx, dctx, 606 displayLevel, displayName, adv); 607 } 608 609 /* clean up */ 610 BMK_freeTimedFnState(timeStateCompress); 611 BMK_freeTimedFnState(timeStateDecompress); 612 613 ZSTD_freeCCtx(cctx); 614 ZSTD_freeDCtx(dctx); 615 616 free(internalDstBuffer); 617 free(resultBuffer); 618 619 free((void*)srcPtrs); 620 free(srcSizes); 621 free(cPtrs); 622 free(cSizes); 623 free(cCapacities); 624 free(resPtrs); 625 free(resSizes); 626 627 if(allocationincomplete) { 628 RETURN_ERROR(31, BMK_benchOutcome_t, "allocation error : not enough memory"); 629 } 630 631 if(dstParamsError) { 632 RETURN_ERROR(32, BMK_benchOutcome_t, "Dst parameters not coherent"); 633 } 634 return outcome; 635 } 636 637 BMK_benchOutcome_t BMK_benchMem(const void* srcBuffer, size_t srcSize, 638 const size_t* fileSizes, unsigned nbFiles, 639 int cLevel, const ZSTD_compressionParameters* comprParams, 640 const void* dictBuffer, size_t dictBufferSize, 641 int displayLevel, const char* displayName) { 642 643 BMK_advancedParams_t const adv = BMK_initAdvancedParams(); 644 return BMK_benchMemAdvanced(srcBuffer, srcSize, 645 NULL, 0, 646 fileSizes, nbFiles, 647 cLevel, comprParams, 648 dictBuffer, dictBufferSize, 649 displayLevel, displayName, &adv); 650 } 651 652 static BMK_benchOutcome_t BMK_benchCLevel(const void* srcBuffer, size_t benchedSize, 653 const size_t* fileSizes, unsigned nbFiles, 654 int cLevel, const ZSTD_compressionParameters* comprParams, 655 const void* dictBuffer, size_t dictBufferSize, 656 int displayLevel, const char* displayName, 657 BMK_advancedParams_t const * const adv) 658 { 659 const char* pch = strrchr(displayName, '\\'); /* Windows */ 660 if (!pch) pch = strrchr(displayName, '/'); /* Linux */ 661 if (pch) displayName = pch+1; 662 663 if (adv->realTime) { 664 DISPLAYLEVEL(2, "Note : switching to real-time priority \n"); 665 SET_REALTIME_PRIORITY; 666 } 667 668 if (displayLevel == 1 && !adv->additionalParam) /* --quiet mode */ 669 DISPLAY("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n", 670 ZSTD_VERSION_STRING, ZSTD_GIT_COMMIT_STRING, 671 (unsigned)benchedSize, adv->nbSeconds, (unsigned)(adv->blockSize>>10)); 672 673 return BMK_benchMemAdvanced(srcBuffer, benchedSize, 674 NULL, 0, 675 fileSizes, nbFiles, 676 cLevel, comprParams, 677 dictBuffer, dictBufferSize, 678 displayLevel, displayName, adv); 679 } 680 681 BMK_benchOutcome_t BMK_syntheticTest(int cLevel, double compressibility, 682 const ZSTD_compressionParameters* compressionParams, 683 int displayLevel, const BMK_advancedParams_t* adv) 684 { 685 char name[20] = {0}; 686 size_t const benchedSize = 10000000; 687 void* srcBuffer; 688 BMK_benchOutcome_t res; 689 690 if (cLevel > ZSTD_maxCLevel()) { 691 RETURN_ERROR(15, BMK_benchOutcome_t, "Invalid Compression Level"); 692 } 693 694 /* Memory allocation */ 695 srcBuffer = malloc(benchedSize); 696 if (!srcBuffer) RETURN_ERROR(21, BMK_benchOutcome_t, "not enough memory"); 697 698 /* Fill input buffer */ 699 RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0); 700 701 /* Bench */ 702 snprintf (name, sizeof(name), "Synthetic %2u%%", (unsigned)(compressibility*100)); 703 res = BMK_benchCLevel(srcBuffer, benchedSize, 704 &benchedSize /* ? */, 1 /* ? */, 705 cLevel, compressionParams, 706 NULL, 0, /* dictionary */ 707 displayLevel, name, adv); 708 709 /* clean up */ 710 free(srcBuffer); 711 712 return res; 713 } 714 715 716 717 static size_t BMK_findMaxMem(U64 requiredMem) 718 { 719 size_t const step = 64 MB; 720 BYTE* testmem = NULL; 721 722 requiredMem = (((requiredMem >> 26) + 1) << 26); 723 requiredMem += step; 724 if (requiredMem > maxMemory) requiredMem = maxMemory; 725 726 do { 727 testmem = (BYTE*)malloc((size_t)requiredMem); 728 requiredMem -= step; 729 } while (!testmem && requiredMem > 0); 730 731 free(testmem); 732 return (size_t)(requiredMem); 733 } 734 735 /*! BMK_loadFiles() : 736 * Loads `buffer` with content of files listed within `fileNamesTable`. 737 * At most, fills `buffer` entirely. */ 738 static int BMK_loadFiles(void* buffer, size_t bufferSize, 739 size_t* fileSizes, 740 const char* const * fileNamesTable, unsigned nbFiles, 741 int displayLevel) 742 { 743 size_t pos = 0, totalSize = 0; 744 unsigned n; 745 for (n=0; n<nbFiles; n++) { 746 FILE* f; 747 U64 fileSize = UTIL_getFileSize(fileNamesTable[n]); 748 if (UTIL_isDirectory(fileNamesTable[n])) { 749 DISPLAYLEVEL(2, "Ignoring %s directory... \n", fileNamesTable[n]); 750 fileSizes[n] = 0; 751 continue; 752 } 753 if (fileSize == UTIL_FILESIZE_UNKNOWN) { 754 DISPLAYLEVEL(2, "Cannot evaluate size of %s, ignoring ... \n", fileNamesTable[n]); 755 fileSizes[n] = 0; 756 continue; 757 } 758 f = fopen(fileNamesTable[n], "rb"); 759 if (f==NULL) EXM_THROW_INT(10, "impossible to open file %s", fileNamesTable[n]); 760 DISPLAYUPDATE(2, "Loading %s... \r", fileNamesTable[n]); 761 if (fileSize > bufferSize-pos) fileSize = bufferSize-pos, nbFiles=n; /* buffer too small - stop after this file */ 762 { size_t const readSize = fread(((char*)buffer)+pos, 1, (size_t)fileSize, f); 763 if (readSize != (size_t)fileSize) EXM_THROW_INT(11, "could not read %s", fileNamesTable[n]); 764 pos += readSize; 765 } 766 fileSizes[n] = (size_t)fileSize; 767 totalSize += (size_t)fileSize; 768 fclose(f); 769 } 770 771 if (totalSize == 0) EXM_THROW_INT(12, "no data to bench"); 772 return 0; 773 } 774 775 BMK_benchOutcome_t BMK_benchFilesAdvanced( 776 const char* const * fileNamesTable, unsigned nbFiles, 777 const char* dictFileName, int cLevel, 778 const ZSTD_compressionParameters* compressionParams, 779 int displayLevel, const BMK_advancedParams_t* adv) 780 { 781 void* srcBuffer = NULL; 782 size_t benchedSize; 783 void* dictBuffer = NULL; 784 size_t dictBufferSize = 0; 785 size_t* fileSizes = NULL; 786 BMK_benchOutcome_t res; 787 U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles); 788 789 if (!nbFiles) { 790 RETURN_ERROR(14, BMK_benchOutcome_t, "No Files to Benchmark"); 791 } 792 793 if (cLevel > ZSTD_maxCLevel()) { 794 RETURN_ERROR(15, BMK_benchOutcome_t, "Invalid Compression Level"); 795 } 796 797 fileSizes = (size_t*)calloc(nbFiles, sizeof(size_t)); 798 if (!fileSizes) RETURN_ERROR(12, BMK_benchOutcome_t, "not enough memory for fileSizes"); 799 800 /* Load dictionary */ 801 if (dictFileName != NULL) { 802 U64 const dictFileSize = UTIL_getFileSize(dictFileName); 803 if (dictFileSize == UTIL_FILESIZE_UNKNOWN) { 804 DISPLAYLEVEL(1, "error loading %s : %s \n", dictFileName, strerror(errno)); 805 free(fileSizes); 806 RETURN_ERROR(9, BMK_benchOutcome_t, "benchmark aborted"); 807 } 808 if (dictFileSize > 64 MB) { 809 free(fileSizes); 810 RETURN_ERROR(10, BMK_benchOutcome_t, "dictionary file %s too large", dictFileName); 811 } 812 dictBufferSize = (size_t)dictFileSize; 813 dictBuffer = malloc(dictBufferSize); 814 if (dictBuffer==NULL) { 815 free(fileSizes); 816 RETURN_ERROR(11, BMK_benchOutcome_t, "not enough memory for dictionary (%u bytes)", 817 (unsigned)dictBufferSize); 818 } 819 820 { int const errorCode = BMK_loadFiles(dictBuffer, dictBufferSize, 821 fileSizes, &dictFileName /*?*/, 822 1 /*?*/, displayLevel); 823 if (errorCode) { 824 res = BMK_benchOutcome_error(); 825 goto _cleanUp; 826 } } 827 } 828 829 /* Memory allocation & restrictions */ 830 benchedSize = BMK_findMaxMem(totalSizeToLoad * 3) / 3; 831 if ((U64)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; 832 if (benchedSize < totalSizeToLoad) 833 DISPLAY("Not enough memory; testing %u MB only...\n", (unsigned)(benchedSize >> 20)); 834 835 srcBuffer = benchedSize ? malloc(benchedSize) : NULL; 836 if (!srcBuffer) { 837 free(dictBuffer); 838 free(fileSizes); 839 RETURN_ERROR(12, BMK_benchOutcome_t, "not enough memory"); 840 } 841 842 /* Load input buffer */ 843 { int const errorCode = BMK_loadFiles(srcBuffer, benchedSize, 844 fileSizes, fileNamesTable, nbFiles, 845 displayLevel); 846 if (errorCode) { 847 res = BMK_benchOutcome_error(); 848 goto _cleanUp; 849 } } 850 851 /* Bench */ 852 { char mfName[20] = {0}; 853 snprintf (mfName, sizeof(mfName), " %u files", nbFiles); 854 { const char* const displayName = (nbFiles > 1) ? mfName : fileNamesTable[0]; 855 res = BMK_benchCLevel(srcBuffer, benchedSize, 856 fileSizes, nbFiles, 857 cLevel, compressionParams, 858 dictBuffer, dictBufferSize, 859 displayLevel, displayName, 860 adv); 861 } } 862 863 _cleanUp: 864 free(srcBuffer); 865 free(dictBuffer); 866 free(fileSizes); 867 return res; 868 } 869 870 871 BMK_benchOutcome_t BMK_benchFiles( 872 const char* const * fileNamesTable, unsigned nbFiles, 873 const char* dictFileName, 874 int cLevel, const ZSTD_compressionParameters* compressionParams, 875 int displayLevel) 876 { 877 BMK_advancedParams_t const adv = BMK_initAdvancedParams(); 878 return BMK_benchFilesAdvanced(fileNamesTable, nbFiles, dictFileName, cLevel, compressionParams, displayLevel, &adv); 879 } 880