1 1.1 christos /* 2 1.1 christos * Copyright (c) Meta Platforms, Inc. and affiliates. 3 1.1 christos * All rights reserved. 4 1.1 christos * 5 1.1 christos * This source code is licensed under both the BSD-style license (found in the 6 1.1 christos * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 1.1 christos * in the COPYING file in the root directory of this source tree). 8 1.1 christos * You may select, at your option, one of the above-listed licenses. 9 1.1 christos */ 10 1.1 christos 11 1.1 christos /* ************************************** 12 1.1 christos * Tuning parameters 13 1.1 christos ****************************************/ 14 1.1 christos #ifndef BMK_TIMETEST_DEFAULT_S /* default minimum time per test */ 15 1.1 christos # define BMK_TIMETEST_DEFAULT_S 3 16 1.1 christos #endif 17 1.1 christos 18 1.1 christos /* ************************************* 19 1.1 christos * Includes 20 1.1 christos ***************************************/ 21 1.1 christos /* this must be included first */ 22 1.1 christos #include "platform.h" /* Large Files support, compiler specifics */ 23 1.1 christos 24 1.1 christos /* then following system includes */ 25 1.1 christos #include <assert.h> /* assert */ 26 1.1 christos #include <errno.h> 27 1.1 christos #include <stdio.h> /* fprintf, fopen */ 28 1.1 christos #include <stdlib.h> /* malloc, free */ 29 1.1 christos #include <string.h> /* memset, strerror */ 30 1.1 christos #include "util.h" /* UTIL_getFileSize, UTIL_sleep */ 31 1.1 christos #include "../lib/common/mem.h" 32 1.1 christos #include "benchfn.h" 33 1.1 christos #include "timefn.h" /* UTIL_time_t */ 34 1.1 christos #ifndef ZSTD_STATIC_LINKING_ONLY 35 1.1 christos # define ZSTD_STATIC_LINKING_ONLY 36 1.1 christos #endif 37 1.1 christos #include "../lib/zstd.h" 38 1.1 christos #include "datagen.h" /* RDG_genBuffer */ 39 1.1 christos #include "lorem.h" /* LOREM_genBuffer */ 40 1.1 christos #ifndef XXH_INLINE_ALL 41 1.1 christos # define XXH_INLINE_ALL 42 1.1 christos #endif 43 1.1 christos #include "../lib/common/xxhash.h" 44 1.1 christos #include "../lib/zstd_errors.h" 45 1.1 christos #include "benchzstd.h" 46 1.1 christos 47 1.1 christos /* ************************************* 48 1.1 christos * Constants 49 1.1 christos ***************************************/ 50 1.1 christos #ifndef ZSTD_GIT_COMMIT 51 1.1 christos # define ZSTD_GIT_COMMIT_STRING "" 52 1.1 christos #else 53 1.1 christos # define ZSTD_GIT_COMMIT_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_GIT_COMMIT) 54 1.1 christos #endif 55 1.1 christos 56 1.1 christos #define TIMELOOP_MICROSEC (1 * 1000000ULL) /* 1 second */ 57 1.1 christos #define TIMELOOP_NANOSEC (1 * 1000000000ULL) /* 1 second */ 58 1.1 christos #define ACTIVEPERIOD_MICROSEC (70 * TIMELOOP_MICROSEC) /* 70 seconds */ 59 1.1 christos #define COOLPERIOD_SEC 10 60 1.1 christos 61 1.1 christos #define KB *(1 << 10) 62 1.1 christos #define MB *(1 << 20) 63 1.1 christos #define GB *(1U << 30) 64 1.1 christos 65 1.1 christos #define BMK_RUNTEST_DEFAULT_MS 1000 66 1.1 christos 67 1.1 christos static const size_t maxMemory = (sizeof(size_t) == 4) 68 1.1 christos ? 69 1.1 christos /* 32-bit */ (2 GB - 64 MB) 70 1.1 christos : 71 1.1 christos /* 64-bit */ (size_t)(1ULL << ((sizeof(size_t) * 8) - 31)); 72 1.1 christos 73 1.1 christos /* ************************************* 74 1.1 christos * console display 75 1.1 christos ***************************************/ 76 1.1 christos #define DISPLAY(...) \ 77 1.1 christos { \ 78 1.1 christos fprintf(stderr, __VA_ARGS__); \ 79 1.1 christos fflush(NULL); \ 80 1.1 christos } 81 1.1 christos #define DISPLAYLEVEL(l, ...) \ 82 1.1 christos if (displayLevel >= l) { \ 83 1.1 christos DISPLAY(__VA_ARGS__); \ 84 1.1 christos } 85 1.1 christos /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + 86 1.1 christos * progression; 4 : + information */ 87 1.1 christos #define OUTPUT(...) \ 88 1.1 christos { \ 89 1.1 christos fprintf(stdout, __VA_ARGS__); \ 90 1.1 christos fflush(NULL); \ 91 1.1 christos } 92 1.1 christos #define OUTPUTLEVEL(l, ...) \ 93 1.1 christos if (displayLevel >= l) { \ 94 1.1 christos OUTPUT(__VA_ARGS__); \ 95 1.1 christos } 96 1.1 christos 97 1.1 christos /* ************************************* 98 1.1 christos * Exceptions 99 1.1 christos ***************************************/ 100 1.1 christos #ifndef DEBUG 101 1.1 christos # define DEBUG 0 102 1.1 christos #endif 103 1.1 christos #define DEBUGOUTPUT(...) \ 104 1.1 christos { \ 105 1.1 christos if (DEBUG) \ 106 1.1 christos DISPLAY(__VA_ARGS__); \ 107 1.1 christos } 108 1.1 christos 109 1.1 christos #define RETURN_ERROR_INT(errorNum, ...) \ 110 1.1 christos { \ 111 1.1 christos DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \ 112 1.1 christos DISPLAYLEVEL(1, "Error %i : ", errorNum); \ 113 1.1 christos DISPLAYLEVEL(1, __VA_ARGS__); \ 114 1.1 christos DISPLAYLEVEL(1, " \n"); \ 115 1.1 christos return errorNum; \ 116 1.1 christos } 117 1.1 christos 118 1.1 christos #define CHECK_Z(zf) \ 119 1.1 christos { \ 120 1.1 christos size_t const zerr = zf; \ 121 1.1 christos if (ZSTD_isError(zerr)) { \ 122 1.1 christos DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \ 123 1.1 christos DISPLAY("Error : "); \ 124 1.1 christos DISPLAY("%s failed : %s", #zf, ZSTD_getErrorName(zerr)); \ 125 1.1 christos DISPLAY(" \n"); \ 126 1.1 christos exit(1); \ 127 1.1 christos } \ 128 1.1 christos } 129 1.1 christos 130 1.1 christos #define RETURN_ERROR(errorNum, retType, ...) \ 131 1.1 christos { \ 132 1.1 christos retType r; \ 133 1.1 christos memset(&r, 0, sizeof(retType)); \ 134 1.1 christos DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \ 135 1.1 christos DISPLAYLEVEL(1, "Error %i : ", errorNum); \ 136 1.1 christos DISPLAYLEVEL(1, __VA_ARGS__); \ 137 1.1 christos DISPLAYLEVEL(1, " \n"); \ 138 1.1 christos r.tag = errorNum; \ 139 1.1 christos return r; \ 140 1.1 christos } 141 1.1 christos 142 1.1 christos /* replacement for snprintf(), which is not supported by C89 143 1.1 christos * sprintf() would be the supported one, but it's labelled unsafe, 144 1.1 christos * so some modern static analyzer will flag it as such, making it unusable. 145 1.1 christos * formatString_u() replaces snprintf() for the specific case where there are only %u arguments */ 146 1.1 christos static int formatString_u(char* buffer, size_t buffer_size, const char* formatString, unsigned int value) 147 1.1 christos { 148 1.1 christos size_t written = 0; 149 1.1 christos int i; 150 1.1 christos assert(value <= 100); 151 1.1 christos 152 1.1 christos for (i = 0; formatString[i] != '\0' && written < buffer_size - 1; ++i) { 153 1.1 christos if (formatString[i] != '%') { 154 1.1 christos buffer[written++] = formatString[i]; 155 1.1 christos continue; 156 1.1 christos } 157 1.1 christos 158 1.1 christos if (formatString[++i] == 'u') { 159 1.1 christos /* Handle single digit */ 160 1.1 christos if (value < 10) { 161 1.1 christos buffer[written++] = '0' + (char)value; 162 1.1 christos } else if (value < 100) { 163 1.1 christos /* Handle two digits */ 164 1.1 christos if (written >= buffer_size - 2) { 165 1.1 christos return -1; /* buffer overflow */ 166 1.1 christos } 167 1.1 christos buffer[written++] = '0' + (char)(value / 10); 168 1.1 christos buffer[written++] = '0' + (char)(value % 10); 169 1.1 christos } else { /* 100 */ 170 1.1 christos if (written >= buffer_size - 3) { 171 1.1 christos return -1; /* buffer overflow */ 172 1.1 christos } 173 1.1 christos buffer[written++] = '1'; 174 1.1 christos buffer[written++] = '0'; 175 1.1 christos buffer[written++] = '0'; 176 1.1 christos } 177 1.1 christos } else if (formatString[i] == '%') { /* Check for escaped percent sign */ 178 1.1 christos buffer[written++] = '%'; 179 1.1 christos } else { 180 1.1 christos return -1; /* unsupported format */ 181 1.1 christos } 182 1.1 christos } 183 1.1 christos 184 1.1 christos if (written < buffer_size) { 185 1.1 christos buffer[written] = '\0'; 186 1.1 christos } else { 187 1.1 christos buffer[0] = '\0'; /* Handle truncation */ 188 1.1 christos } 189 1.1 christos 190 1.1 christos return (int)written; 191 1.1 christos } 192 1.1 christos 193 1.1 christos /* ************************************* 194 1.1 christos * Benchmark Parameters 195 1.1 christos ***************************************/ 196 1.1 christos 197 1.1 christos BMK_advancedParams_t BMK_initAdvancedParams(void) 198 1.1 christos { 199 1.1 christos BMK_advancedParams_t const res = { 200 1.1 christos BMK_both, /* mode */ 201 1.1 christos BMK_TIMETEST_DEFAULT_S, /* nbSeconds */ 202 1.1 christos 0, /* blockSize */ 203 1.1 christos 0, /* targetCBlockSize */ 204 1.1 christos 0, /* nbWorkers */ 205 1.1 christos 0, /* realTime */ 206 1.1 christos 0, /* additionalParam */ 207 1.1 christos 0, /* ldmFlag */ 208 1.1 christos 0, /* ldmMinMatch */ 209 1.1 christos 0, /* ldmHashLog */ 210 1.1 christos 0, /* ldmBuckSizeLog */ 211 1.1 christos 0, /* ldmHashRateLog */ 212 1.1 christos ZSTD_ps_auto, /* literalCompressionMode */ 213 1.1 christos 0 /* useRowMatchFinder */ 214 1.1 christos }; 215 1.1 christos return res; 216 1.1 christos } 217 1.1 christos 218 1.1 christos /* ******************************************************** 219 1.1 christos * Bench functions 220 1.1 christos **********************************************************/ 221 1.1 christos typedef struct { 222 1.1 christos const void* srcPtr; 223 1.1 christos size_t srcSize; 224 1.1 christos void* cPtr; 225 1.1 christos size_t cRoom; 226 1.1 christos size_t cSize; 227 1.1 christos void* resPtr; 228 1.1 christos size_t resSize; 229 1.1 christos } blockParam_t; 230 1.1 christos 231 1.1 christos #undef MIN 232 1.1 christos #undef MAX 233 1.1 christos #define MIN(a, b) ((a) < (b) ? (a) : (b)) 234 1.1 christos #define MAX(a, b) ((a) > (b) ? (a) : (b)) 235 1.1 christos 236 1.1 christos static void BMK_initCCtx( 237 1.1 christos ZSTD_CCtx* ctx, 238 1.1 christos const void* dictBuffer, 239 1.1 christos size_t dictBufferSize, 240 1.1 christos int cLevel, 241 1.1 christos const ZSTD_compressionParameters* comprParams, 242 1.1 christos const BMK_advancedParams_t* adv) 243 1.1 christos { 244 1.1 christos ZSTD_CCtx_reset(ctx, ZSTD_reset_session_and_parameters); 245 1.1 christos if (adv->nbWorkers == 1) { 246 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_nbWorkers, 0)); 247 1.1 christos } else { 248 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_nbWorkers, adv->nbWorkers)); 249 1.1 christos } 250 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, cLevel)); 251 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 252 1.1 christos ctx, ZSTD_c_useRowMatchFinder, adv->useRowMatchFinder)); 253 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 254 1.1 christos ctx, ZSTD_c_enableLongDistanceMatching, adv->ldmFlag)); 255 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_ldmMinMatch, adv->ldmMinMatch)); 256 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter(ctx, ZSTD_c_ldmHashLog, adv->ldmHashLog)); 257 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 258 1.1 christos ctx, ZSTD_c_ldmBucketSizeLog, adv->ldmBucketSizeLog)); 259 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 260 1.1 christos ctx, ZSTD_c_ldmHashRateLog, adv->ldmHashRateLog)); 261 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 262 1.1 christos ctx, ZSTD_c_windowLog, (int)comprParams->windowLog)); 263 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 264 1.1 christos ctx, ZSTD_c_hashLog, (int)comprParams->hashLog)); 265 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 266 1.1 christos ctx, ZSTD_c_chainLog, (int)comprParams->chainLog)); 267 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 268 1.1 christos ctx, ZSTD_c_searchLog, (int)comprParams->searchLog)); 269 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 270 1.1 christos ctx, ZSTD_c_minMatch, (int)comprParams->minMatch)); 271 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 272 1.1 christos ctx, ZSTD_c_targetLength, (int)comprParams->targetLength)); 273 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 274 1.1 christos ctx, 275 1.1 christos ZSTD_c_literalCompressionMode, 276 1.1 christos (int)adv->literalCompressionMode)); 277 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 278 1.1 christos ctx, ZSTD_c_strategy, (int)comprParams->strategy)); 279 1.1 christos CHECK_Z(ZSTD_CCtx_setParameter( 280 1.1 christos ctx, ZSTD_c_targetCBlockSize, (int)adv->targetCBlockSize)); 281 1.1 christos CHECK_Z(ZSTD_CCtx_loadDictionary(ctx, dictBuffer, dictBufferSize)); 282 1.1 christos } 283 1.1 christos 284 1.1 christos static void 285 1.1 christos BMK_initDCtx(ZSTD_DCtx* dctx, const void* dictBuffer, size_t dictBufferSize) 286 1.1 christos { 287 1.1 christos CHECK_Z(ZSTD_DCtx_reset(dctx, ZSTD_reset_session_and_parameters)); 288 1.1 christos CHECK_Z(ZSTD_DCtx_loadDictionary(dctx, dictBuffer, dictBufferSize)); 289 1.1 christos } 290 1.1 christos 291 1.1 christos typedef struct { 292 1.1 christos ZSTD_CCtx* cctx; 293 1.1 christos const void* dictBuffer; 294 1.1 christos size_t dictBufferSize; 295 1.1 christos int cLevel; 296 1.1 christos const ZSTD_compressionParameters* comprParams; 297 1.1 christos const BMK_advancedParams_t* adv; 298 1.1 christos } BMK_initCCtxArgs; 299 1.1 christos 300 1.1 christos static size_t local_initCCtx(void* payload) 301 1.1 christos { 302 1.1 christos BMK_initCCtxArgs* ag = (BMK_initCCtxArgs*)payload; 303 1.1 christos BMK_initCCtx( 304 1.1 christos ag->cctx, 305 1.1 christos ag->dictBuffer, 306 1.1 christos ag->dictBufferSize, 307 1.1 christos ag->cLevel, 308 1.1 christos ag->comprParams, 309 1.1 christos ag->adv); 310 1.1 christos return 0; 311 1.1 christos } 312 1.1 christos 313 1.1 christos typedef struct { 314 1.1 christos ZSTD_DCtx* dctx; 315 1.1 christos const void* dictBuffer; 316 1.1 christos size_t dictBufferSize; 317 1.1 christos } BMK_initDCtxArgs; 318 1.1 christos 319 1.1 christos static size_t local_initDCtx(void* payload) 320 1.1 christos { 321 1.1 christos BMK_initDCtxArgs* ag = (BMK_initDCtxArgs*)payload; 322 1.1 christos BMK_initDCtx(ag->dctx, ag->dictBuffer, ag->dictBufferSize); 323 1.1 christos return 0; 324 1.1 christos } 325 1.1 christos 326 1.1 christos /* `addArgs` is the context */ 327 1.1 christos static size_t local_defaultCompress( 328 1.1 christos const void* srcBuffer, 329 1.1 christos size_t srcSize, 330 1.1 christos void* dstBuffer, 331 1.1 christos size_t dstSize, 332 1.1 christos void* addArgs) 333 1.1 christos { 334 1.1 christos ZSTD_CCtx* const cctx = (ZSTD_CCtx*)addArgs; 335 1.1 christos return ZSTD_compress2(cctx, dstBuffer, dstSize, srcBuffer, srcSize); 336 1.1 christos } 337 1.1 christos 338 1.1 christos /* `addArgs` is the context */ 339 1.1 christos static size_t local_defaultDecompress( 340 1.1 christos const void* srcBuffer, 341 1.1 christos size_t srcSize, 342 1.1 christos void* dstBuffer, 343 1.1 christos size_t dstCapacity, 344 1.1 christos void* addArgs) 345 1.1 christos { 346 1.1 christos size_t moreToFlush = 1; 347 1.1 christos ZSTD_DCtx* const dctx = (ZSTD_DCtx*)addArgs; 348 1.1 christos ZSTD_inBuffer in; 349 1.1 christos ZSTD_outBuffer out; 350 1.1 christos in.src = srcBuffer; 351 1.1 christos in.size = srcSize; 352 1.1 christos in.pos = 0; 353 1.1 christos out.dst = dstBuffer; 354 1.1 christos out.size = dstCapacity; 355 1.1 christos out.pos = 0; 356 1.1 christos while (moreToFlush) { 357 1.1 christos if (out.pos == out.size) { 358 1.1 christos return (size_t)-ZSTD_error_dstSize_tooSmall; 359 1.1 christos } 360 1.1 christos moreToFlush = ZSTD_decompressStream(dctx, &out, &in); 361 1.1 christos if (ZSTD_isError(moreToFlush)) { 362 1.1 christos return moreToFlush; 363 1.1 christos } 364 1.1 christos } 365 1.1 christos return out.pos; 366 1.1 christos } 367 1.1 christos 368 1.1 christos /* ================================================================= */ 369 1.1 christos /* Benchmark Zstandard, mem-to-mem scenarios */ 370 1.1 christos /* ================================================================= */ 371 1.1 christos 372 1.1 christos int BMK_isSuccessful_benchOutcome(BMK_benchOutcome_t outcome) 373 1.1 christos { 374 1.1 christos return outcome.tag == 0; 375 1.1 christos } 376 1.1 christos 377 1.1 christos BMK_benchResult_t BMK_extract_benchResult(BMK_benchOutcome_t outcome) 378 1.1 christos { 379 1.1 christos assert(outcome.tag == 0); 380 1.1 christos return outcome.internal_never_use_directly; 381 1.1 christos } 382 1.1 christos 383 1.1 christos static BMK_benchOutcome_t BMK_benchOutcome_error(void) 384 1.1 christos { 385 1.1 christos BMK_benchOutcome_t b; 386 1.1 christos memset(&b, 0, sizeof(b)); 387 1.1 christos b.tag = 1; 388 1.1 christos return b; 389 1.1 christos } 390 1.1 christos 391 1.1 christos static BMK_benchOutcome_t BMK_benchOutcome_setValidResult( 392 1.1 christos BMK_benchResult_t result) 393 1.1 christos { 394 1.1 christos BMK_benchOutcome_t b; 395 1.1 christos b.tag = 0; 396 1.1 christos b.internal_never_use_directly = result; 397 1.1 christos return b; 398 1.1 christos } 399 1.1 christos 400 1.1 christos /* benchMem with no allocation */ 401 1.1 christos static BMK_benchOutcome_t BMK_benchMemAdvancedNoAlloc( 402 1.1 christos const void** srcPtrs, 403 1.1 christos size_t* srcSizes, 404 1.1 christos void** cPtrs, 405 1.1 christos size_t* cCapacities, 406 1.1 christos size_t* cSizes, 407 1.1 christos void** resPtrs, 408 1.1 christos size_t* resSizes, 409 1.1 christos void** resultBufferPtr, 410 1.1 christos void* compressedBuffer, 411 1.1 christos size_t maxCompressedSize, 412 1.1 christos BMK_timedFnState_t* timeStateCompress, 413 1.1 christos BMK_timedFnState_t* timeStateDecompress, 414 1.1 christos 415 1.1 christos const void* srcBuffer, 416 1.1 christos size_t srcSize, 417 1.1 christos const size_t* fileSizes, 418 1.1 christos unsigned nbFiles, 419 1.1 christos const int cLevel, 420 1.1 christos const ZSTD_compressionParameters* comprParams, 421 1.1 christos const void* dictBuffer, 422 1.1 christos size_t dictBufferSize, 423 1.1 christos ZSTD_CCtx* cctx, 424 1.1 christos ZSTD_DCtx* dctx, 425 1.1 christos int displayLevel, 426 1.1 christos const char* displayName, 427 1.1 christos const BMK_advancedParams_t* adv) 428 1.1 christos { 429 1.1 christos size_t const blockSize = 430 1.1 christos ((adv->blockSize >= 32 && (adv->mode != BMK_decodeOnly)) 431 1.1 christos ? adv->blockSize 432 1.1 christos : srcSize) 433 1.1 christos + (!srcSize); /* avoid div by 0 */ 434 1.1 christos BMK_benchResult_t benchResult; 435 1.1 christos size_t const loadedCompressedSize = srcSize; 436 1.1 christos size_t cSize = 0; 437 1.1 christos double ratio = 0.; 438 1.1 christos U32 nbBlocks; 439 1.1 christos 440 1.1 christos assert(cctx != NULL); 441 1.1 christos assert(dctx != NULL); 442 1.1 christos 443 1.1 christos /* init */ 444 1.1 christos memset(&benchResult, 0, sizeof(benchResult)); 445 1.1 christos if (strlen(displayName) > 17) 446 1.1 christos displayName += 447 1.1 christos strlen(displayName) - 17; /* display last 17 characters */ 448 1.1 christos if (adv->mode == BMK_decodeOnly) { 449 1.1 christos /* benchmark only decompression : source must be already compressed */ 450 1.1 christos const char* srcPtr = (const char*)srcBuffer; 451 1.1 christos U64 totalDSize64 = 0; 452 1.1 christos U32 fileNb; 453 1.1 christos for (fileNb = 0; fileNb < nbFiles; fileNb++) { 454 1.1 christos U64 const fSize64 = 455 1.1 christos ZSTD_findDecompressedSize(srcPtr, fileSizes[fileNb]); 456 1.1 christos if (fSize64 == ZSTD_CONTENTSIZE_UNKNOWN) { 457 1.1 christos RETURN_ERROR( 458 1.1 christos 32, 459 1.1 christos BMK_benchOutcome_t, 460 1.1 christos "Decompressed size cannot be determined: cannot benchmark"); 461 1.1 christos } 462 1.1 christos if (fSize64 == ZSTD_CONTENTSIZE_ERROR) { 463 1.1 christos RETURN_ERROR( 464 1.1 christos 32, 465 1.1 christos BMK_benchOutcome_t, 466 1.1 christos "Error while trying to assess decompressed size: data may be invalid"); 467 1.1 christos } 468 1.1 christos totalDSize64 += fSize64; 469 1.1 christos srcPtr += fileSizes[fileNb]; 470 1.1 christos } 471 1.1 christos { 472 1.1 christos size_t const decodedSize = (size_t)totalDSize64; 473 1.1 christos assert((U64)decodedSize == totalDSize64); /* check overflow */ 474 1.1 christos free(*resultBufferPtr); 475 1.1 christos if (totalDSize64 > decodedSize) { /* size_t overflow */ 476 1.1 christos RETURN_ERROR( 477 1.1 christos 32, 478 1.1 christos BMK_benchOutcome_t, 479 1.1 christos "decompressed size is too large for local system"); 480 1.1 christos } 481 1.1 christos *resultBufferPtr = malloc(decodedSize); 482 1.1 christos if (!(*resultBufferPtr)) { 483 1.1 christos RETURN_ERROR( 484 1.1 christos 33, 485 1.1 christos BMK_benchOutcome_t, 486 1.1 christos "allocation error: not enough memory"); 487 1.1 christos } 488 1.1 christos cSize = srcSize; 489 1.1 christos srcSize = decodedSize; 490 1.1 christos ratio = (double)srcSize / (double)cSize; 491 1.1 christos } 492 1.1 christos } 493 1.1 christos 494 1.1 christos /* Init data blocks */ 495 1.1 christos { 496 1.1 christos const char* srcPtr = (const char*)srcBuffer; 497 1.1 christos char* cPtr = (char*)compressedBuffer; 498 1.1 christos char* resPtr = (char*)(*resultBufferPtr); 499 1.1 christos U32 fileNb; 500 1.1 christos for (nbBlocks = 0, fileNb = 0; fileNb < nbFiles; fileNb++) { 501 1.1 christos size_t remaining = fileSizes[fileNb]; 502 1.1 christos U32 const nbBlocksforThisFile = (adv->mode == BMK_decodeOnly) 503 1.1 christos ? 1 504 1.1 christos : (U32)((remaining + (blockSize - 1)) / blockSize); 505 1.1 christos U32 const blockEnd = nbBlocks + nbBlocksforThisFile; 506 1.1 christos for (; nbBlocks < blockEnd; nbBlocks++) { 507 1.1 christos size_t const thisBlockSize = MIN(remaining, blockSize); 508 1.1 christos srcPtrs[nbBlocks] = srcPtr; 509 1.1 christos srcSizes[nbBlocks] = thisBlockSize; 510 1.1 christos cPtrs[nbBlocks] = cPtr; 511 1.1 christos cCapacities[nbBlocks] = (adv->mode == BMK_decodeOnly) 512 1.1 christos ? thisBlockSize 513 1.1 christos : ZSTD_compressBound(thisBlockSize); 514 1.1 christos resPtrs[nbBlocks] = resPtr; 515 1.1 christos resSizes[nbBlocks] = (adv->mode == BMK_decodeOnly) 516 1.1 christos ? (size_t)ZSTD_findDecompressedSize( 517 1.1 christos srcPtr, thisBlockSize) 518 1.1 christos : thisBlockSize; 519 1.1 christos srcPtr += thisBlockSize; 520 1.1 christos cPtr += cCapacities[nbBlocks]; 521 1.1 christos resPtr += thisBlockSize; 522 1.1 christos remaining -= thisBlockSize; 523 1.1 christos if (adv->mode == BMK_decodeOnly) { 524 1.1 christos cSizes[nbBlocks] = thisBlockSize; 525 1.1 christos benchResult.cSize = thisBlockSize; 526 1.1 christos } 527 1.1 christos } 528 1.1 christos } 529 1.1 christos } 530 1.1 christos 531 1.1 christos /* warming up `compressedBuffer` */ 532 1.1 christos if (adv->mode == BMK_decodeOnly) { 533 1.1 christos memcpy(compressedBuffer, srcBuffer, loadedCompressedSize); 534 1.1 christos } else { 535 1.1 christos RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1); 536 1.1 christos } 537 1.1 christos 538 1.1 christos if (!UTIL_support_MT_measurements() && adv->nbWorkers > 1) { 539 1.1 christos OUTPUTLEVEL( 540 1.1 christos 2, 541 1.1 christos "Warning : time measurements may be incorrect in multithreading mode... \n") 542 1.1 christos } 543 1.1 christos 544 1.1 christos /* Bench */ 545 1.1 christos { 546 1.1 christos U64 const crcOrig = (adv->mode == BMK_decodeOnly) 547 1.1 christos ? 0 548 1.1 christos : XXH64(srcBuffer, srcSize, 0); 549 1.1 christos #define NB_MARKS 4 550 1.1 christos const char* marks[NB_MARKS] = { " |", " /", " =", " \\" }; 551 1.1 christos U32 markNb = 0; 552 1.1 christos int compressionCompleted = (adv->mode == BMK_decodeOnly); 553 1.1 christos int decompressionCompleted = (adv->mode == BMK_compressOnly); 554 1.1 christos BMK_benchParams_t cbp, dbp; 555 1.1 christos BMK_initCCtxArgs cctxprep; 556 1.1 christos BMK_initDCtxArgs dctxprep; 557 1.1 christos 558 1.1 christos cbp.benchFn = local_defaultCompress; /* ZSTD_compress2 */ 559 1.1 christos cbp.benchPayload = cctx; 560 1.1 christos cbp.initFn = local_initCCtx; /* BMK_initCCtx */ 561 1.1 christos cbp.initPayload = &cctxprep; 562 1.1 christos cbp.errorFn = ZSTD_isError; 563 1.1 christos cbp.blockCount = nbBlocks; 564 1.1 christos cbp.srcBuffers = srcPtrs; 565 1.1 christos cbp.srcSizes = srcSizes; 566 1.1 christos cbp.dstBuffers = cPtrs; 567 1.1 christos cbp.dstCapacities = cCapacities; 568 1.1 christos cbp.blockResults = cSizes; 569 1.1 christos 570 1.1 christos cctxprep.cctx = cctx; 571 1.1 christos cctxprep.dictBuffer = dictBuffer; 572 1.1 christos cctxprep.dictBufferSize = dictBufferSize; 573 1.1 christos cctxprep.cLevel = cLevel; 574 1.1 christos cctxprep.comprParams = comprParams; 575 1.1 christos cctxprep.adv = adv; 576 1.1 christos 577 1.1 christos dbp.benchFn = local_defaultDecompress; 578 1.1 christos dbp.benchPayload = dctx; 579 1.1 christos dbp.initFn = local_initDCtx; 580 1.1 christos dbp.initPayload = &dctxprep; 581 1.1 christos dbp.errorFn = ZSTD_isError; 582 1.1 christos dbp.blockCount = nbBlocks; 583 1.1 christos dbp.srcBuffers = (const void* const*)cPtrs; 584 1.1 christos dbp.srcSizes = cSizes; 585 1.1 christos dbp.dstBuffers = resPtrs; 586 1.1 christos dbp.dstCapacities = resSizes; 587 1.1 christos dbp.blockResults = NULL; 588 1.1 christos 589 1.1 christos dctxprep.dctx = dctx; 590 1.1 christos dctxprep.dictBuffer = dictBuffer; 591 1.1 christos dctxprep.dictBufferSize = dictBufferSize; 592 1.1 christos 593 1.1 christos OUTPUTLEVEL(2, "\r%70s\r", ""); /* blank line */ 594 1.1 christos assert(srcSize < UINT_MAX); 595 1.1 christos OUTPUTLEVEL( 596 1.1 christos 2, 597 1.1 christos "%2s-%-17.17s :%10u -> \r", 598 1.1 christos marks[markNb], 599 1.1 christos displayName, 600 1.1 christos (unsigned)srcSize); 601 1.1 christos 602 1.1 christos while (!(compressionCompleted && decompressionCompleted)) { 603 1.1 christos if (!compressionCompleted) { 604 1.1 christos BMK_runOutcome_t const cOutcome = 605 1.1 christos BMK_benchTimedFn(timeStateCompress, cbp); 606 1.1 christos 607 1.1 christos if (!BMK_isSuccessful_runOutcome(cOutcome)) { 608 1.1 christos RETURN_ERROR(30, BMK_benchOutcome_t, "compression error"); 609 1.1 christos } 610 1.1 christos 611 1.1 christos { 612 1.1 christos BMK_runTime_t const cResult = BMK_extract_runTime(cOutcome); 613 1.1 christos cSize = cResult.sumOfReturn; 614 1.1 christos ratio = (double)srcSize / (double)cSize; 615 1.1 christos { 616 1.1 christos BMK_benchResult_t newResult; 617 1.1 christos newResult.cSpeed = 618 1.1 christos (U64)((double)srcSize * TIMELOOP_NANOSEC 619 1.1 christos / cResult.nanoSecPerRun); 620 1.1 christos benchResult.cSize = cSize; 621 1.1 christos if (newResult.cSpeed > benchResult.cSpeed) 622 1.1 christos benchResult.cSpeed = newResult.cSpeed; 623 1.1 christos } 624 1.1 christos } 625 1.1 christos 626 1.1 christos { 627 1.1 christos int const ratioAccuracy = (ratio < 10.) ? 3 : 2; 628 1.1 christos assert(cSize < UINT_MAX); 629 1.1 christos OUTPUTLEVEL( 630 1.1 christos 2, 631 1.1 christos "%2s-%-17.17s :%10u ->%10u (x%5.*f), %6.*f MB/s \r", 632 1.1 christos marks[markNb], 633 1.1 christos displayName, 634 1.1 christos (unsigned)srcSize, 635 1.1 christos (unsigned)cSize, 636 1.1 christos ratioAccuracy, 637 1.1 christos ratio, 638 1.1 christos benchResult.cSpeed < (10 * MB_UNIT) ? 2 : 1, 639 1.1 christos (double)benchResult.cSpeed / MB_UNIT); 640 1.1 christos } 641 1.1 christos compressionCompleted = 642 1.1 christos BMK_isCompleted_TimedFn(timeStateCompress); 643 1.1 christos } 644 1.1 christos 645 1.1 christos if (!decompressionCompleted) { 646 1.1 christos BMK_runOutcome_t const dOutcome = 647 1.1 christos BMK_benchTimedFn(timeStateDecompress, dbp); 648 1.1 christos 649 1.1 christos if (!BMK_isSuccessful_runOutcome(dOutcome)) { 650 1.1 christos RETURN_ERROR(30, BMK_benchOutcome_t, "decompression error"); 651 1.1 christos } 652 1.1 christos 653 1.1 christos { 654 1.1 christos BMK_runTime_t const dResult = BMK_extract_runTime(dOutcome); 655 1.1 christos U64 const newDSpeed = 656 1.1 christos (U64)((double)srcSize * TIMELOOP_NANOSEC 657 1.1 christos / dResult.nanoSecPerRun); 658 1.1 christos if (newDSpeed > benchResult.dSpeed) 659 1.1 christos benchResult.dSpeed = newDSpeed; 660 1.1 christos } 661 1.1 christos 662 1.1 christos { 663 1.1 christos int const ratioAccuracy = (ratio < 10.) ? 3 : 2; 664 1.1 christos OUTPUTLEVEL( 665 1.1 christos 2, 666 1.1 christos "%2s-%-17.17s :%10u ->%10u (x%5.*f), %6.*f MB/s, %6.1f MB/s\r", 667 1.1 christos marks[markNb], 668 1.1 christos displayName, 669 1.1 christos (unsigned)srcSize, 670 1.1 christos (unsigned)cSize, 671 1.1 christos ratioAccuracy, 672 1.1 christos ratio, 673 1.1 christos benchResult.cSpeed < (10 * MB_UNIT) ? 2 : 1, 674 1.1 christos (double)benchResult.cSpeed / MB_UNIT, 675 1.1 christos (double)benchResult.dSpeed / MB_UNIT); 676 1.1 christos } 677 1.1 christos decompressionCompleted = 678 1.1 christos BMK_isCompleted_TimedFn(timeStateDecompress); 679 1.1 christos } 680 1.1 christos markNb = (markNb + 1) % NB_MARKS; 681 1.1 christos } /* while (!(compressionCompleted && decompressionCompleted)) */ 682 1.1 christos 683 1.1 christos /* CRC Checking */ 684 1.1 christos { 685 1.1 christos const BYTE* resultBuffer = (const BYTE*)(*resultBufferPtr); 686 1.1 christos U64 const crcCheck = XXH64(resultBuffer, srcSize, 0); 687 1.1 christos if ((adv->mode == BMK_both) && (crcOrig != crcCheck)) { 688 1.1 christos size_t u; 689 1.1 christos DISPLAY("!!! WARNING !!! %14s : Invalid Checksum : %x != %x \n", 690 1.1 christos displayName, 691 1.1 christos (unsigned)crcOrig, 692 1.1 christos (unsigned)crcCheck); 693 1.1 christos for (u = 0; u < srcSize; u++) { 694 1.1 christos if (((const BYTE*)srcBuffer)[u] != resultBuffer[u]) { 695 1.1 christos unsigned segNb, bNb, pos; 696 1.1 christos size_t bacc = 0; 697 1.1 christos DISPLAY("Decoding error at pos %u ", (unsigned)u); 698 1.1 christos for (segNb = 0; segNb < nbBlocks; segNb++) { 699 1.1 christos if (bacc + srcSizes[segNb] > u) 700 1.1 christos break; 701 1.1 christos bacc += srcSizes[segNb]; 702 1.1 christos } 703 1.1 christos pos = (U32)(u - bacc); 704 1.1 christos bNb = pos / (128 KB); 705 1.1 christos DISPLAY("(sample %u, block %u, pos %u) \n", 706 1.1 christos segNb, 707 1.1 christos bNb, 708 1.1 christos pos); 709 1.1 christos { 710 1.1 christos size_t const lowest = (u > 5) ? 5 : u; 711 1.1 christos size_t n; 712 1.1 christos DISPLAY("origin: "); 713 1.1 christos for (n = lowest; n > 0; n--) 714 1.1 christos DISPLAY("%02X ", 715 1.1 christos ((const BYTE*)srcBuffer)[u - n]); 716 1.1 christos DISPLAY(" :%02X: ", ((const BYTE*)srcBuffer)[u]); 717 1.1 christos for (n = 1; n < 3; n++) 718 1.1 christos DISPLAY("%02X ", 719 1.1 christos ((const BYTE*)srcBuffer)[u + n]); 720 1.1 christos DISPLAY(" \n"); 721 1.1 christos DISPLAY("decode: "); 722 1.1 christos for (n = lowest; n > 0; n--) 723 1.1 christos DISPLAY("%02X ", resultBuffer[u - n]); 724 1.1 christos DISPLAY(" :%02X: ", resultBuffer[u]); 725 1.1 christos for (n = 1; n < 3; n++) 726 1.1 christos DISPLAY("%02X ", resultBuffer[u + n]); 727 1.1 christos DISPLAY(" \n"); 728 1.1 christos } 729 1.1 christos break; 730 1.1 christos } 731 1.1 christos if (u == srcSize - 1) { /* should never happen */ 732 1.1 christos DISPLAY("no difference detected\n"); 733 1.1 christos } 734 1.1 christos } /* for (u=0; u<srcSize; u++) */ 735 1.1 christos } /* if ((adv->mode == BMK_both) && (crcOrig!=crcCheck)) */ 736 1.1 christos } /* CRC Checking */ 737 1.1 christos 738 1.1 christos if (displayLevel 739 1.1 christos == 1) { /* hidden display mode -q, used by python speed benchmark */ 740 1.1 christos double const cSpeed = (double)benchResult.cSpeed / MB_UNIT; 741 1.1 christos double const dSpeed = (double)benchResult.dSpeed / MB_UNIT; 742 1.1 christos if (adv->additionalParam) { 743 1.1 christos OUTPUT("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s (param=%d)\n", 744 1.1 christos cLevel, 745 1.1 christos (int)cSize, 746 1.1 christos ratio, 747 1.1 christos cSpeed, 748 1.1 christos dSpeed, 749 1.1 christos displayName, 750 1.1 christos adv->additionalParam); 751 1.1 christos } else { 752 1.1 christos OUTPUT("-%-3i%11i (%5.3f) %6.2f MB/s %6.1f MB/s %s\n", 753 1.1 christos cLevel, 754 1.1 christos (int)cSize, 755 1.1 christos ratio, 756 1.1 christos cSpeed, 757 1.1 christos dSpeed, 758 1.1 christos displayName); 759 1.1 christos } 760 1.1 christos } 761 1.1 christos 762 1.1 christos OUTPUTLEVEL(2, "%2i#\n", cLevel); 763 1.1 christos } /* Bench */ 764 1.1 christos 765 1.1 christos benchResult.cMem = 766 1.1 christos (1ULL << (comprParams->windowLog)) + ZSTD_sizeof_CCtx(cctx); 767 1.1 christos return BMK_benchOutcome_setValidResult(benchResult); 768 1.1 christos } 769 1.1 christos 770 1.1 christos BMK_benchOutcome_t BMK_benchMemAdvanced( 771 1.1 christos const void* srcBuffer, 772 1.1 christos size_t srcSize, 773 1.1 christos void* dstBuffer, 774 1.1 christos size_t dstCapacity, 775 1.1 christos const size_t* fileSizes, 776 1.1 christos unsigned nbFiles, 777 1.1 christos int cLevel, 778 1.1 christos const ZSTD_compressionParameters* comprParams, 779 1.1 christos const void* dictBuffer, 780 1.1 christos size_t dictBufferSize, 781 1.1 christos int displayLevel, 782 1.1 christos const char* displayName, 783 1.1 christos const BMK_advancedParams_t* adv) 784 1.1 christos 785 1.1 christos { 786 1.1 christos int const dstParamsError = 787 1.1 christos !dstBuffer ^ !dstCapacity; /* must be both NULL or none */ 788 1.1 christos 789 1.1 christos size_t const blockSize = 790 1.1 christos ((adv->blockSize >= 32 && (adv->mode != BMK_decodeOnly)) 791 1.1 christos ? adv->blockSize 792 1.1 christos : srcSize) 793 1.1 christos + (!srcSize) /* avoid div by 0 */; 794 1.1 christos U32 const maxNbBlocks = 795 1.1 christos (U32)((srcSize + (blockSize - 1)) / blockSize) + nbFiles; 796 1.1 christos 797 1.1 christos /* these are the blockTable parameters, just split up */ 798 1.1 christos const void** const srcPtrs = 799 1.1 christos (const void**)malloc(maxNbBlocks * sizeof(void*)); 800 1.1 christos size_t* const srcSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); 801 1.1 christos 802 1.1 christos void** const cPtrs = (void**)malloc(maxNbBlocks * sizeof(void*)); 803 1.1 christos size_t* const cSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); 804 1.1 christos size_t* const cCapacities = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); 805 1.1 christos 806 1.1 christos void** const resPtrs = (void**)malloc(maxNbBlocks * sizeof(void*)); 807 1.1 christos size_t* const resSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); 808 1.1 christos 809 1.1 christos BMK_timedFnState_t* timeStateCompress = BMK_createTimedFnState( 810 1.1 christos adv->nbSeconds * 1000, BMK_RUNTEST_DEFAULT_MS); 811 1.1 christos BMK_timedFnState_t* timeStateDecompress = BMK_createTimedFnState( 812 1.1 christos adv->nbSeconds * 1000, BMK_RUNTEST_DEFAULT_MS); 813 1.1 christos 814 1.1 christos ZSTD_CCtx* const cctx = ZSTD_createCCtx(); 815 1.1 christos ZSTD_DCtx* const dctx = ZSTD_createDCtx(); 816 1.1 christos 817 1.1 christos const size_t maxCompressedSize = dstCapacity 818 1.1 christos ? dstCapacity 819 1.1 christos : ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); 820 1.1 christos 821 1.1 christos void* const internalDstBuffer = 822 1.1 christos dstBuffer ? NULL : malloc(maxCompressedSize); 823 1.1 christos void* const compressedBuffer = dstBuffer ? dstBuffer : internalDstBuffer; 824 1.1 christos 825 1.1 christos BMK_benchOutcome_t outcome = 826 1.1 christos BMK_benchOutcome_error(); /* error by default */ 827 1.1 christos 828 1.1 christos void* resultBuffer = srcSize ? malloc(srcSize) : NULL; 829 1.1 christos 830 1.1 christos int const allocationincomplete = !srcPtrs || !srcSizes || !cPtrs || !cSizes 831 1.1 christos || !cCapacities || !resPtrs || !resSizes || !timeStateCompress 832 1.1 christos || !timeStateDecompress || !cctx || !dctx || !compressedBuffer 833 1.1 christos || !resultBuffer; 834 1.1 christos 835 1.1 christos if (!allocationincomplete && !dstParamsError) { 836 1.1 christos outcome = BMK_benchMemAdvancedNoAlloc( 837 1.1 christos srcPtrs, 838 1.1 christos srcSizes, 839 1.1 christos cPtrs, 840 1.1 christos cCapacities, 841 1.1 christos cSizes, 842 1.1 christos resPtrs, 843 1.1 christos resSizes, 844 1.1 christos &resultBuffer, 845 1.1 christos compressedBuffer, 846 1.1 christos maxCompressedSize, 847 1.1 christos timeStateCompress, 848 1.1 christos timeStateDecompress, 849 1.1 christos srcBuffer, 850 1.1 christos srcSize, 851 1.1 christos fileSizes, 852 1.1 christos nbFiles, 853 1.1 christos cLevel, 854 1.1 christos comprParams, 855 1.1 christos dictBuffer, 856 1.1 christos dictBufferSize, 857 1.1 christos cctx, 858 1.1 christos dctx, 859 1.1 christos displayLevel, 860 1.1 christos displayName, 861 1.1 christos adv); 862 1.1 christos } 863 1.1 christos 864 1.1 christos /* clean up */ 865 1.1 christos BMK_freeTimedFnState(timeStateCompress); 866 1.1 christos BMK_freeTimedFnState(timeStateDecompress); 867 1.1 christos 868 1.1 christos ZSTD_freeCCtx(cctx); 869 1.1 christos ZSTD_freeDCtx(dctx); 870 1.1 christos 871 1.1 christos free(internalDstBuffer); 872 1.1 christos free(resultBuffer); 873 1.1 christos 874 1.1 christos free((void*)srcPtrs); 875 1.1 christos free(srcSizes); 876 1.1 christos free(cPtrs); 877 1.1 christos free(cSizes); 878 1.1 christos free(cCapacities); 879 1.1 christos free(resPtrs); 880 1.1 christos free(resSizes); 881 1.1 christos 882 1.1 christos if (allocationincomplete) { 883 1.1 christos RETURN_ERROR( 884 1.1 christos 31, BMK_benchOutcome_t, "allocation error : not enough memory"); 885 1.1 christos } 886 1.1 christos 887 1.1 christos if (dstParamsError) { 888 1.1 christos RETURN_ERROR(32, BMK_benchOutcome_t, "Dst parameters not coherent"); 889 1.1 christos } 890 1.1 christos return outcome; 891 1.1 christos } 892 1.1 christos 893 1.1 christos BMK_benchOutcome_t BMK_benchMem( 894 1.1 christos const void* srcBuffer, 895 1.1 christos size_t srcSize, 896 1.1 christos const size_t* fileSizes, 897 1.1 christos unsigned nbFiles, 898 1.1 christos int cLevel, 899 1.1 christos const ZSTD_compressionParameters* comprParams, 900 1.1 christos const void* dictBuffer, 901 1.1 christos size_t dictBufferSize, 902 1.1 christos int displayLevel, 903 1.1 christos const char* displayName) 904 1.1 christos { 905 1.1 christos BMK_advancedParams_t const adv = BMK_initAdvancedParams(); 906 1.1 christos return BMK_benchMemAdvanced( 907 1.1 christos srcBuffer, 908 1.1 christos srcSize, 909 1.1 christos NULL, 910 1.1 christos 0, 911 1.1 christos fileSizes, 912 1.1 christos nbFiles, 913 1.1 christos cLevel, 914 1.1 christos comprParams, 915 1.1 christos dictBuffer, 916 1.1 christos dictBufferSize, 917 1.1 christos displayLevel, 918 1.1 christos displayName, 919 1.1 christos &adv); 920 1.1 christos } 921 1.1 christos 922 1.1 christos static BMK_benchOutcome_t BMK_benchCLevel( 923 1.1 christos const void* srcBuffer, 924 1.1 christos size_t benchedSize, 925 1.1 christos const size_t* fileSizes, 926 1.1 christos unsigned nbFiles, 927 1.1 christos int cLevel, 928 1.1 christos const ZSTD_compressionParameters* comprParams, 929 1.1 christos const void* dictBuffer, 930 1.1 christos size_t dictBufferSize, 931 1.1 christos int displayLevel, 932 1.1 christos const char* displayName, 933 1.1 christos BMK_advancedParams_t const* const adv) 934 1.1 christos { 935 1.1 christos const char* pch = strrchr(displayName, '\\'); /* Windows */ 936 1.1 christos if (!pch) 937 1.1 christos pch = strrchr(displayName, '/'); /* Linux */ 938 1.1 christos if (pch) 939 1.1 christos displayName = pch + 1; 940 1.1 christos 941 1.1 christos if (adv->realTime) { 942 1.1 christos DISPLAYLEVEL(2, "Note : switching to real-time priority \n"); 943 1.1 christos SET_REALTIME_PRIORITY; 944 1.1 christos } 945 1.1 christos 946 1.1 christos if (displayLevel == 1 && !adv->additionalParam) /* --quiet mode */ 947 1.1 christos OUTPUT("bench %s %s: input %u bytes, %u seconds, %u KB blocks\n", 948 1.1 christos ZSTD_VERSION_STRING, 949 1.1 christos ZSTD_GIT_COMMIT_STRING, 950 1.1 christos (unsigned)benchedSize, 951 1.1 christos adv->nbSeconds, 952 1.1 christos (unsigned)(adv->blockSize >> 10)); 953 1.1 christos 954 1.1 christos return BMK_benchMemAdvanced( 955 1.1 christos srcBuffer, 956 1.1 christos benchedSize, 957 1.1 christos NULL, 958 1.1 christos 0, 959 1.1 christos fileSizes, 960 1.1 christos nbFiles, 961 1.1 christos cLevel, 962 1.1 christos comprParams, 963 1.1 christos dictBuffer, 964 1.1 christos dictBufferSize, 965 1.1 christos displayLevel, 966 1.1 christos displayName, 967 1.1 christos adv); 968 1.1 christos } 969 1.1 christos 970 1.1 christos int BMK_syntheticTest( 971 1.1 christos int cLevel, 972 1.1 christos double compressibility, 973 1.1 christos const ZSTD_compressionParameters* compressionParams, 974 1.1 christos int displayLevel, 975 1.1 christos const BMK_advancedParams_t* adv) 976 1.1 christos { 977 1.1 christos char nameBuff[20] = { 0 }; 978 1.1 christos const char* name = nameBuff; 979 1.1 christos size_t const benchedSize = adv->blockSize ? adv->blockSize : 10000000; 980 1.1 christos void* srcBuffer; 981 1.1 christos BMK_benchOutcome_t res; 982 1.1 christos 983 1.1 christos if (cLevel > ZSTD_maxCLevel()) { 984 1.1 christos DISPLAYLEVEL(1, "Invalid Compression Level"); 985 1.1 christos return 15; 986 1.1 christos } 987 1.1 christos 988 1.1 christos /* Memory allocation */ 989 1.1 christos srcBuffer = malloc(benchedSize); 990 1.1 christos if (!srcBuffer) { 991 1.1 christos DISPLAYLEVEL(1, "allocation error : not enough memory"); 992 1.1 christos return 16; 993 1.1 christos } 994 1.1 christos 995 1.1 christos /* Fill input buffer */ 996 1.1 christos if (compressibility < 0.0) { 997 1.1 christos LOREM_genBuffer(srcBuffer, benchedSize, 0); 998 1.1 christos name = "Lorem ipsum"; 999 1.1 christos } else { 1000 1.1 christos RDG_genBuffer(srcBuffer, benchedSize, compressibility, 0.0, 0); 1001 1.1 christos formatString_u( 1002 1.1 christos nameBuff, 1003 1.1 christos sizeof(nameBuff), 1004 1.1 christos "Synthetic %u%%", 1005 1.1 christos (unsigned)(compressibility * 100)); 1006 1.1 christos } 1007 1.1 christos 1008 1.1 christos /* Bench */ 1009 1.1 christos res = BMK_benchCLevel( 1010 1.1 christos srcBuffer, 1011 1.1 christos benchedSize, 1012 1.1 christos &benchedSize /* ? */, 1013 1.1 christos 1 /* ? */, 1014 1.1 christos cLevel, 1015 1.1 christos compressionParams, 1016 1.1 christos NULL, 1017 1.1 christos 0, /* dictionary */ 1018 1.1 christos displayLevel, 1019 1.1 christos name, 1020 1.1 christos adv); 1021 1.1 christos 1022 1.1 christos /* clean up */ 1023 1.1 christos free(srcBuffer); 1024 1.1 christos 1025 1.1 christos return !BMK_isSuccessful_benchOutcome(res); 1026 1.1 christos } 1027 1.1 christos 1028 1.1 christos static size_t BMK_findMaxMem(U64 requiredMem) 1029 1.1 christos { 1030 1.1 christos size_t const step = 64 MB; 1031 1.1 christos BYTE* testmem = NULL; 1032 1.1 christos 1033 1.1 christos requiredMem = (((requiredMem >> 26) + 1) << 26); 1034 1.1 christos requiredMem += step; 1035 1.1 christos if (requiredMem > maxMemory) 1036 1.1 christos requiredMem = maxMemory; 1037 1.1 christos 1038 1.1 christos do { 1039 1.1 christos testmem = (BYTE*)malloc((size_t)requiredMem); 1040 1.1 christos requiredMem -= step; 1041 1.1 christos } while (!testmem && requiredMem > 0); 1042 1.1 christos 1043 1.1 christos free(testmem); 1044 1.1 christos return (size_t)(requiredMem); 1045 1.1 christos } 1046 1.1 christos 1047 1.1 christos /*! BMK_loadFiles() : 1048 1.1 christos * Loads `buffer` with content of files listed within `fileNamesTable`. 1049 1.1 christos * At most, fills `buffer` entirely. */ 1050 1.1 christos static int BMK_loadFiles( 1051 1.1 christos void* buffer, 1052 1.1 christos size_t bufferSize, 1053 1.1 christos size_t* fileSizes, 1054 1.1 christos const char* const* fileNamesTable, 1055 1.1 christos unsigned nbFiles, 1056 1.1 christos int displayLevel) 1057 1.1 christos { 1058 1.1 christos size_t pos = 0, totalSize = 0; 1059 1.1 christos unsigned n; 1060 1.1 christos for (n = 0; n < nbFiles; n++) { 1061 1.1 christos U64 fileSize = UTIL_getFileSize( 1062 1.1 christos fileNamesTable[n]); /* last file may be shortened */ 1063 1.1 christos if (UTIL_isDirectory(fileNamesTable[n])) { 1064 1.1 christos DISPLAYLEVEL( 1065 1.1 christos 2, "Ignoring %s directory... \n", fileNamesTable[n]); 1066 1.1 christos fileSizes[n] = 0; 1067 1.1 christos continue; 1068 1.1 christos } 1069 1.1 christos if (fileSize == UTIL_FILESIZE_UNKNOWN) { 1070 1.1 christos DISPLAYLEVEL( 1071 1.1 christos 2, 1072 1.1 christos "Cannot evaluate size of %s, ignoring ... \n", 1073 1.1 christos fileNamesTable[n]); 1074 1.1 christos fileSizes[n] = 0; 1075 1.1 christos continue; 1076 1.1 christos } 1077 1.1 christos { 1078 1.1 christos FILE* const f = fopen(fileNamesTable[n], "rb"); 1079 1.1 christos if (f == NULL) 1080 1.1 christos RETURN_ERROR_INT( 1081 1.1 christos 10, "impossible to open file %s", fileNamesTable[n]); 1082 1.1 christos OUTPUTLEVEL(2, "Loading %s... \r", fileNamesTable[n]); 1083 1.1 christos if (fileSize > bufferSize - pos) 1084 1.1 christos fileSize = bufferSize - pos, 1085 1.1 christos nbFiles = n; /* buffer too small - stop after this file */ 1086 1.1 christos { 1087 1.1 christos size_t const readSize = 1088 1.1 christos fread(((char*)buffer) + pos, 1, (size_t)fileSize, f); 1089 1.1 christos if (readSize != (size_t)fileSize) 1090 1.1 christos RETURN_ERROR_INT( 1091 1.1 christos 11, "could not read %s", fileNamesTable[n]); 1092 1.1 christos pos += readSize; 1093 1.1 christos } 1094 1.1 christos fileSizes[n] = (size_t)fileSize; 1095 1.1 christos totalSize += (size_t)fileSize; 1096 1.1 christos fclose(f); 1097 1.1 christos } 1098 1.1 christos } 1099 1.1 christos 1100 1.1 christos if (totalSize == 0) 1101 1.1 christos RETURN_ERROR_INT(12, "no data to bench"); 1102 1.1 christos return 0; 1103 1.1 christos } 1104 1.1 christos 1105 1.1 christos int BMK_benchFilesAdvanced( 1106 1.1 christos const char* const* fileNamesTable, 1107 1.1 christos unsigned nbFiles, 1108 1.1 christos const char* dictFileName, 1109 1.1 christos int cLevel, 1110 1.1 christos const ZSTD_compressionParameters* compressionParams, 1111 1.1 christos int displayLevel, 1112 1.1 christos const BMK_advancedParams_t* adv) 1113 1.1 christos { 1114 1.1 christos void* srcBuffer = NULL; 1115 1.1 christos size_t benchedSize; 1116 1.1 christos void* dictBuffer = NULL; 1117 1.1 christos size_t dictBufferSize = 0; 1118 1.1 christos size_t* fileSizes = NULL; 1119 1.1 christos BMK_benchOutcome_t res; 1120 1.1 christos U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, nbFiles); 1121 1.1 christos 1122 1.1 christos if (!nbFiles) { 1123 1.1 christos DISPLAYLEVEL(1, "No Files to Benchmark"); 1124 1.1 christos return 13; 1125 1.1 christos } 1126 1.1 christos 1127 1.1 christos if (cLevel > ZSTD_maxCLevel()) { 1128 1.1 christos DISPLAYLEVEL(1, "Invalid Compression Level"); 1129 1.1 christos return 14; 1130 1.1 christos } 1131 1.1 christos 1132 1.1 christos if (totalSizeToLoad == UTIL_FILESIZE_UNKNOWN) { 1133 1.1 christos DISPLAYLEVEL(1, "Error loading files"); 1134 1.1 christos return 15; 1135 1.1 christos } 1136 1.1 christos 1137 1.1 christos fileSizes = (size_t*)calloc(nbFiles, sizeof(size_t)); 1138 1.1 christos if (!fileSizes) { 1139 1.1 christos DISPLAYLEVEL(1, "not enough memory for fileSizes"); 1140 1.1 christos return 16; 1141 1.1 christos } 1142 1.1 christos 1143 1.1 christos /* Load dictionary */ 1144 1.1 christos if (dictFileName != NULL) { 1145 1.1 christos U64 const dictFileSize = UTIL_getFileSize(dictFileName); 1146 1.1 christos if (dictFileSize == UTIL_FILESIZE_UNKNOWN) { 1147 1.1 christos DISPLAYLEVEL( 1148 1.1 christos 1, 1149 1.1 christos "error loading %s : %s \n", 1150 1.1 christos dictFileName, 1151 1.1 christos strerror(errno)); 1152 1.1 christos free(fileSizes); 1153 1.1 christos DISPLAYLEVEL(1, "benchmark aborted"); 1154 1.1 christos return 17; 1155 1.1 christos } 1156 1.1 christos if (dictFileSize > 64 MB) { 1157 1.1 christos free(fileSizes); 1158 1.1 christos DISPLAYLEVEL(1, "dictionary file %s too large", dictFileName); 1159 1.1 christos return 18; 1160 1.1 christos } 1161 1.1 christos dictBufferSize = (size_t)dictFileSize; 1162 1.1 christos dictBuffer = malloc(dictBufferSize); 1163 1.1 christos if (dictBuffer == NULL) { 1164 1.1 christos free(fileSizes); 1165 1.1 christos DISPLAYLEVEL( 1166 1.1 christos 1, 1167 1.1 christos "not enough memory for dictionary (%u bytes)", 1168 1.1 christos (unsigned)dictBufferSize); 1169 1.1 christos return 19; 1170 1.1 christos } 1171 1.1 christos 1172 1.1 christos { 1173 1.1 christos int const errorCode = BMK_loadFiles( 1174 1.1 christos dictBuffer, 1175 1.1 christos dictBufferSize, 1176 1.1 christos fileSizes, 1177 1.1 christos &dictFileName /*?*/, 1178 1.1 christos 1 /*?*/, 1179 1.1 christos displayLevel); 1180 1.1 christos if (errorCode) { 1181 1.1 christos res = BMK_benchOutcome_error(); 1182 1.1 christos goto _cleanUp; 1183 1.1 christos } 1184 1.1 christos } 1185 1.1 christos } 1186 1.1 christos 1187 1.1 christos /* Memory allocation & restrictions */ 1188 1.1 christos benchedSize = BMK_findMaxMem(totalSizeToLoad * 3) / 3; 1189 1.1 christos if ((U64)benchedSize > totalSizeToLoad) 1190 1.1 christos benchedSize = (size_t)totalSizeToLoad; 1191 1.1 christos if (benchedSize < totalSizeToLoad) 1192 1.1 christos DISPLAY("Not enough memory; testing %u MB only...\n", 1193 1.1 christos (unsigned)(benchedSize >> 20)); 1194 1.1 christos 1195 1.1 christos srcBuffer = benchedSize ? malloc(benchedSize) : NULL; 1196 1.1 christos if (!srcBuffer) { 1197 1.1 christos free(dictBuffer); 1198 1.1 christos free(fileSizes); 1199 1.1 christos DISPLAYLEVEL(1, "not enough memory for srcBuffer"); 1200 1.1 christos return 20; 1201 1.1 christos } 1202 1.1 christos 1203 1.1 christos /* Load input buffer */ 1204 1.1 christos { 1205 1.1 christos int const errorCode = BMK_loadFiles( 1206 1.1 christos srcBuffer, 1207 1.1 christos benchedSize, 1208 1.1 christos fileSizes, 1209 1.1 christos fileNamesTable, 1210 1.1 christos nbFiles, 1211 1.1 christos displayLevel); 1212 1.1 christos if (errorCode) { 1213 1.1 christos res = BMK_benchOutcome_error(); 1214 1.1 christos goto _cleanUp; 1215 1.1 christos } 1216 1.1 christos } 1217 1.1 christos 1218 1.1 christos /* Bench */ 1219 1.1 christos { 1220 1.1 christos char mfName[20] = { 0 }; 1221 1.1 christos formatString_u(mfName, sizeof(mfName), " %u files", nbFiles); 1222 1.1 christos { 1223 1.1 christos const char* const displayName = 1224 1.1 christos (nbFiles > 1) ? mfName : fileNamesTable[0]; 1225 1.1 christos res = BMK_benchCLevel( 1226 1.1 christos srcBuffer, 1227 1.1 christos benchedSize, 1228 1.1 christos fileSizes, 1229 1.1 christos nbFiles, 1230 1.1 christos cLevel, 1231 1.1 christos compressionParams, 1232 1.1 christos dictBuffer, 1233 1.1 christos dictBufferSize, 1234 1.1 christos displayLevel, 1235 1.1 christos displayName, 1236 1.1 christos adv); 1237 1.1 christos } 1238 1.1 christos } 1239 1.1 christos 1240 1.1 christos _cleanUp: 1241 1.1 christos free(srcBuffer); 1242 1.1 christos free(dictBuffer); 1243 1.1 christos free(fileSizes); 1244 1.1 christos return !BMK_isSuccessful_benchOutcome(res); 1245 1.1 christos } 1246 1.1 christos 1247 1.1 christos int BMK_benchFiles( 1248 1.1 christos const char* const* fileNamesTable, 1249 1.1 christos unsigned nbFiles, 1250 1.1 christos const char* dictFileName, 1251 1.1 christos int cLevel, 1252 1.1 christos const ZSTD_compressionParameters* compressionParams, 1253 1.1 christos int displayLevel) 1254 1.1 christos { 1255 1.1 christos BMK_advancedParams_t const adv = BMK_initAdvancedParams(); 1256 1.1 christos return BMK_benchFilesAdvanced( 1257 1.1 christos fileNamesTable, 1258 1.1 christos nbFiles, 1259 1.1 christos dictFileName, 1260 1.1 christos cLevel, 1261 1.1 christos compressionParams, 1262 1.1 christos displayLevel, 1263 1.1 christos &adv); 1264 1.1 christos } 1265