Home | History | Annotate | Line # | Download | only in programs
      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