diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 74f9dc29c..27071ed79 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -320,7 +320,8 @@ static void ZSTDMT_setNbSeq(ZSTDMT_seqPool* const seqPool, size_t const nbSeq) static ZSTDMT_seqPool* ZSTDMT_createSeqPool(unsigned nbWorkers, ZSTD_customMem cMem) { - ZSTDMT_seqPool* seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + ZSTDMT_seqPool* const seqPool = ZSTDMT_createBufferPool(nbWorkers, cMem); + if (seqPool == NULL) return NULL; ZSTDMT_setNbSeq(seqPool, 0); return seqPool; } diff --git a/programs/bench.c b/programs/bench.c index 7662678ab..79ef42caa 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -597,15 +597,16 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( results.error = compressionResults.error; return results; } + if(compressionResults.result.nanoSecPerRun == 0) { results.result.cSpeed = 0; } else { results.result.cSpeed = srcSize * TIMELOOP_NANOSEC / compressionResults.result.nanoSecPerRun; } + results.result.cSize = compressionResults.result.sumOfReturn; { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; - results.result.cSpeed = (srcSize * TIMELOOP_NANOSEC / compressionResults.result.nanoSecPerRun); cSize = compressionResults.result.sumOfReturn; results.result.cSize = cSize; ratio = (double)srcSize / results.result.cSize; @@ -626,6 +627,7 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( results.error = decompressionResults.error; return results; } + if(decompressionResults.result.nanoSecPerRun == 0) { results.result.dSpeed = 0; } else { @@ -634,7 +636,6 @@ static BMK_return_t BMK_benchMemAdvancedNoAlloc( { int const ratioAccuracy = (ratio < 10.) ? 3 : 2; - results.result.dSpeed = (srcSize * TIMELOOP_NANOSEC/ decompressionResults.result.nanoSecPerRun); markNb = (markNb+1) % NB_MARKS; DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.*f),%6.*f MB/s ,%6.1f MB/s \r", marks[markNb], displayName, (U32)srcSize, (U32)results.result.cSize, @@ -737,14 +738,16 @@ BMK_return_t BMK_benchMemAdvanced(const void* srcBuffer, size_t srcSize, void* const internalDstBuffer = dstBuffer ? NULL : malloc(maxCompressedSize); void* const compressedBuffer = dstBuffer ? dstBuffer : internalDstBuffer; - void* resultBuffer = malloc(srcSize); - BMK_return_t results = { { 0, 0, 0, 0 }, 0 }; + + int parametersConflict = !dstBuffer ^ !dstCapacity; + + void* resultBuffer = srcSize ? malloc(srcSize) : NULL; + int allocationincomplete = !srcPtrs || !srcSizes || !cPtrs || !cSizes || !cCapacities || !resPtrs || !resSizes || !timeStateCompress || !timeStateDecompress || !compressedBuffer || !resultBuffer; - int parametersConflict = !dstBuffer ^ !dstCapacity; if (!allocationincomplete && !parametersConflict) { @@ -809,7 +812,7 @@ static size_t BMK_findMaxMem(U64 requiredMem) do { testmem = (BYTE*)malloc((size_t)requiredMem); requiredMem -= step; - } while (!testmem); + } while (!testmem && requiredMem > 0); free(testmem); return (size_t)(requiredMem); @@ -937,7 +940,8 @@ BMK_return_t BMK_benchFilesAdvanced(const char* const * const fileNamesTable, un if ((U64)benchedSize > totalSizeToLoad) benchedSize = (size_t)totalSizeToLoad; if (benchedSize < totalSizeToLoad) DISPLAY("Not enough memory; testing %u MB only...\n", (U32)(benchedSize >> 20)); - srcBuffer = malloc(benchedSize); + + srcBuffer = benchedSize ? malloc(benchedSize) : NULL; if (!srcBuffer) { free(dictBuffer); free(fileSizes); diff --git a/tests/Makefile b/tests/Makefile index 81e685780..88a5d763c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -129,7 +129,7 @@ zstdmt_d_%.o : $(ZSTDDIR)/decompress/%.c fullbench32: CPPFLAGS += -m32 fullbench fullbench32 : CPPFLAGS += $(MULTITHREAD_CPP) fullbench fullbench32 : LDFLAGS += $(MULTITHREAD_LD) -fullbench fullbench32 : DEBUGFLAGS = # turn off assert() for speed measurements +fullbench fullbench32 : DEBUGFLAGS = -DNDEBUG # turn off assert() for speed measurements fullbench fullbench32 : $(ZSTD_FILES) fullbench fullbench32 : $(PRGDIR)/datagen.c $(PRGDIR)/bench.c fullbench.c $(CC) $(FLAGS) $^ -o $@$(EXT) @@ -200,7 +200,7 @@ zstreamtest-dll : $(ZSTDDIR)/common/xxhash.c # xxh symbols not exposed from dll zstreamtest-dll : $(ZSTREAM_LOCAL_FILES) $(CC) $(CPPFLAGS) $(CFLAGS) $(filter %.c,$^) $(LDFLAGS) -o $@$(EXT) -paramgrill : DEBUGFLAGS = # turn off assert() for speed measurements +paramgrill : DEBUGFLAGS = -DNDEBUG # turn off assert() for speed measurements paramgrill : $(ZSTD_FILES) $(PRGDIR)/bench.c $(PRGDIR)/datagen.c paramgrill.c $(CC) $(FLAGS) $^ -lm -o $@$(EXT) diff --git a/tests/README.md b/tests/README.md index 2f0026fda..3c35ecf01 100644 --- a/tests/README.md +++ b/tests/README.md @@ -105,17 +105,27 @@ Full list of arguments t# - targetLength S# - strategy L# - level - --zstd= : Single run, parameter selection syntax same as zstdcli + --zstd= : Single run, parameter selection syntax same as zstdcli. + When invoked with --optimize, this represents the sample to exceed. --optimize= : find parameters to maximize compression ratio given parameters - Can use all --zstd= commands to constrain the type of solution found in addition to the following constraints - cSpeed= - Minimum compression speed - dSpeed= - Minimum decompression speed - cMem= - compression memory - lvl= - Automatically sets compression speed constraint to the speed of that level + Can use all --zstd= commands to constrain the type of solution found in addition to the following constraints + cSpeed= : Minimum compression speed + dSpeed= : Minimum decompression speed + cMem= : Maximum compression memory + lvl= : Searches for solutions which are strictly better than that compression lvl in ratio and cSpeed, + stc= : When invoked with lvl=, represents percentage slack in ratio/cSpeed allowed for a solution to be considered (Default 99%) + : In normal operation, represents percentage slack in choosing viable starting strategy selection in choosing the default parameters + (Lower value will begin with stronger strategies) (Default 90%) + preferSpeed= / preferRatio= + : Only affects lvl = invocations. Defines value placed on compression speed or ratio + when determining overall winner (default speed = 1, ratio = 5 for both, higher = more valued). + tries= : Maximum number of random restarts on a single strategy before switching (Default 3) + Higher values will make optimizer run longer, more chances to find better solution. -P# : generated sample compressibility -t# : Caps runtime of operation in seconds (default : 99999 seconds (about 27 hours )) -v : Prints Benchmarking output -D : Next argument dictionary file + -s : Benchmark all files separately ``` Any inputs afterwards are treated as files to benchmark. diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 6d57afa16..66b33c5ee 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -179,13 +179,9 @@ static void FUZ_displayMallocStats(mallocCounter_t count) (U32)(count.totalMalloc >> 10)); } -static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) +static int FUZ_mallocTests_internal(unsigned seed, double compressibility, unsigned part, + void* inBuffer, size_t inSize, void* outBuffer, size_t outSize) { - size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */ - size_t const outSize = ZSTD_compressBound(inSize); - void* const inBuffer = malloc(inSize); - void* const outBuffer = malloc(outSize); - /* test only played in verbose mode, as they are long */ if (g_displayLevel<3) return 0; @@ -270,6 +266,28 @@ static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) return 0; } +static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) +{ + size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */ + size_t const outSize = ZSTD_compressBound(inSize); + void* const inBuffer = malloc(inSize); + void* const outBuffer = malloc(outSize); + int result; + + /* Create compressible noise */ + if (!inBuffer || !outBuffer) { + DISPLAY("Not enough memory, aborting \n"); + exit(1); + } + + result = FUZ_mallocTests_internal(seed, compressibility, part, + inBuffer, inSize, outBuffer, outSize); + + free(inBuffer); + free(outBuffer); + return result; +} + #else static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) @@ -1535,7 +1553,6 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD size_t const dstBufferSize = (size_t)1< /* fprintf, fopen, ftello64 */ #include /* strcmp */ #include /* log */ -#include #include #include "mem.h" @@ -67,21 +66,10 @@ static const int g_maxNbVariations = 64; #define CUSTOM_LEVEL 99 #define BASE_CLEVEL 1 -/* indices for each of the variables */ -typedef enum { - wlog_ind = 0, - clog_ind = 1, - hlog_ind = 2, - slog_ind = 3, - slen_ind = 4, - tlen_ind = 5 -} varInds_t; - -#define NUM_PARAMS 6 -/* just don't use strategy as a param. */ - #undef ZSTD_WINDOWLOG_MAX #define ZSTD_WINDOWLOG_MAX 27 //no long range stuff for now. +#define FADT_MIN 0 +#define FADT_MAX ((U32)-1) #define ZSTD_TARGETLENGTH_MIN 0 #define ZSTD_TARGETLENGTH_MAX 999 @@ -92,39 +80,221 @@ typedef enum { #define SLOG_RANGE (ZSTD_SEARCHLOG_MAX - ZSTD_SEARCHLOG_MIN + 1) #define SLEN_RANGE (ZSTD_SEARCHLENGTH_MAX - ZSTD_SEARCHLENGTH_MIN + 1) #define TLEN_RANGE 17 -/* TLEN_RANGE picked manually */ +#define STRT_RANGE (ZSTD_btultra - ZSTD_fast + 1) +#define FADT_RANGE 3 + +#define CHECKTIME(r) { if(BMK_timeSpan(g_time) > g_timeLimit_s) { DEBUGOUTPUT("Time Limit Reached\n"); return r; } } +#define CHECKTIMEGT(ret, val, _gototag) {if(BMK_timeSpan(g_time) > g_timeLimit_s) { DEBUGOUTPUT("Time Limit Reached\n"); ret = val; goto _gototag; } } + +#define PARAM_UNSET ((U32)-2) /* can't be -1 b/c fadt */ -static const int rangetable[NUM_PARAMS] = { WLOG_RANGE, CLOG_RANGE, HLOG_RANGE, SLOG_RANGE, SLEN_RANGE, TLEN_RANGE }; -static const U32 tlen_table[TLEN_RANGE] = { 0, 1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 256, 512, 999 }; /*-************************************ -* Benchmark Parameters +* Setup for Adding new params +**************************************/ + +/* indices for each of the variables */ +typedef enum { + wlog_ind = 0, + clog_ind = 1, + hlog_ind = 2, + slog_ind = 3, + slen_ind = 4, + tlen_ind = 5, + strt_ind = 6, + fadt_ind = 7, /* forceAttachDict */ + NUM_PARAMS = 8 +} varInds_t; + +/* maximum value of parameters */ +static const U32 mintable[NUM_PARAMS] = + { ZSTD_WINDOWLOG_MIN, ZSTD_CHAINLOG_MIN, ZSTD_HASHLOG_MIN, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLENGTH_MIN, ZSTD_TARGETLENGTH_MIN, ZSTD_fast, FADT_MIN }; + +/* minimum value of parameters */ +static const U32 maxtable[NUM_PARAMS] = + { ZSTD_WINDOWLOG_MAX, ZSTD_CHAINLOG_MAX, ZSTD_HASHLOG_MAX, ZSTD_SEARCHLOG_MAX, ZSTD_SEARCHLENGTH_MAX, ZSTD_TARGETLENGTH_MAX, ZSTD_btultra, FADT_MAX }; + +/* # of values parameters can take on */ +static const U32 rangetable[NUM_PARAMS] = + { WLOG_RANGE, CLOG_RANGE, HLOG_RANGE, SLOG_RANGE, SLEN_RANGE, TLEN_RANGE, STRT_RANGE, FADT_RANGE }; + +static const ZSTD_cParameter cctxSetParamTable[NUM_PARAMS] = + { ZSTD_p_windowLog, ZSTD_p_chainLog, ZSTD_p_hashLog, ZSTD_p_searchLog, ZSTD_p_minMatch, ZSTD_p_targetLength, ZSTD_p_compressionStrategy, ZSTD_p_forceAttachDict }; + +static const char* g_paramNames[NUM_PARAMS] = + { "windowLog", "chainLog", "hashLog","searchLog", "searchLength", "targetLength", "strategy", "forceAttachDict"}; + + +static const U32 tlen_table[TLEN_RANGE] = { 0, 1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 256, 512, 999 }; +/* maps value from 0 to rangetable[param] - 1 to valid paramvalue */ +static U32 rangeMap(varInds_t param, U32 ind) { + ind = MIN(ind, rangetable[param] - 1); + switch(param) { + case tlen_ind: + return tlen_table[ind]; + case fadt_ind: /* 0, 1, 2 -> -1, 0, 1 */ + return ind - 1; + case wlog_ind: /* using default: triggers -Wswitch-enum */ + case clog_ind: + case hlog_ind: + case slog_ind: + case slen_ind: + case strt_ind: + return mintable[param] + ind; + case NUM_PARAMS: + return (U32)-1; + } + return 0; /* should never happen, stop compiler warnings */ +} + +/* inverse of rangeMap */ +static U32 invRangeMap(varInds_t param, U32 value) { + value = MIN(MAX(mintable[param], value), maxtable[param]); + switch(param) { + case tlen_ind: /* bin search */ + { + int lo = 0; + int hi = TLEN_RANGE; + while(lo < hi) { + int mid = (lo + hi) / 2; + if(tlen_table[mid] < value) { + lo = mid + 1; + } if(tlen_table[mid] == value) { + return mid; + } else { + hi = mid; + } + } + return lo; + } + case fadt_ind: + return value + 1; + case wlog_ind: + case clog_ind: + case hlog_ind: + case slog_ind: + case slen_ind: + case strt_ind: + return value - mintable[param]; + case NUM_PARAMS: + return (U32)-1; + } + return 0; /* should never happen, stop compiler warnings */ +} + +typedef struct { + U32 vals[NUM_PARAMS]; +} paramValues_t; + +//TODO: unset -> 0? +static ZSTD_compressionParameters pvalsToCParams(paramValues_t p) { + ZSTD_compressionParameters c; + c.windowLog = p.vals[wlog_ind]; + c.chainLog = p.vals[clog_ind]; + c.hashLog = p.vals[hlog_ind]; + c.searchLog = p.vals[slog_ind]; + c.searchLength = p.vals[slen_ind]; + c.targetLength = p.vals[tlen_ind]; + c.strategy = p.vals[strt_ind]; + /* no forceAttachDict */ + return c; +} + +/* 0 = auto for fadt */ +static paramValues_t cParamsToPVals(ZSTD_compressionParameters c) { + paramValues_t p; + p.vals[wlog_ind] = c.windowLog; + p.vals[clog_ind] = c.chainLog; + p.vals[hlog_ind] = c.hashLog; + p.vals[slog_ind] = c.searchLog; + p.vals[slen_ind] = c.searchLength; + p.vals[tlen_ind] = c.targetLength; + p.vals[strt_ind] = c.strategy; + p.vals[fadt_ind] = 0; + return p; +} + +/* equivalent of ZSTD_adjustCParams for paramValues_t */ +static paramValues_t adjustParams(paramValues_t p, size_t maxBlockSize, size_t dictSize) { + U32 fval = p.vals[fadt_ind]; + p = cParamsToPVals(ZSTD_adjustCParams(pvalsToCParams(p), maxBlockSize, dictSize)); + p.vals[fadt_ind] = fval; + return p; +} + +/*-************************************ +* Benchmark Parameters/Global Variables **************************************/ typedef BYTE U8; -static double g_grillDuration_s = 99999; /* about 27 hours */ +static U32 g_timeLimit_s = 99999; /* about 27 hours */ static U32 g_nbIterations = NBLOOPS; static double g_compressibility = COMPRESSIBILITY_DEFAULT; static U32 g_blockSize = 0; static U32 g_rand = 1; static U32 g_singleRun = 0; +static U32 g_optimizer = 0; static U32 g_target = 0; static U32 g_noSeed = 0; -static ZSTD_compressionParameters g_params = { 0, 0, 0, 0, 0, 0, ZSTD_greedy }; +static paramValues_t g_params; /* Initialized at the beginning of main w/ emptyParams() function */ static UTIL_time_t g_time; /* to be used to compare solution finding speeds to compare to original */ + +typedef struct { + BMK_result_t result; + paramValues_t params; +} winnerInfo_t; + +typedef struct { + BMK_result_t result; + ZSTD_compressionParameters params; +} oldWinnerInfo_t; + +typedef struct { + U32 cSpeed; /* bytes / sec */ + U32 dSpeed; + U32 cMem; /* bytes */ +} constraint_t; + +typedef struct winner_ll_node winner_ll_node; +struct winner_ll_node { + winnerInfo_t res; + winner_ll_node* next; +}; + +static winner_ll_node* g_winners; /* linked list sorted ascending by cSize & cSpeed */ +static BMK_result_t g_lvltarget; +static int g_optmode = 0; + +static U32 g_speedMultiplier = 1; +static U32 g_ratioMultiplier = 5; + +/* g_mode? */ + +/* range 0 - 99, measure of how strict */ +#define DEFAULT_STRICTNESS 99999 +static U32 g_strictness = DEFAULT_STRICTNESS; + void BMK_SetNbIterations(int nbLoops) { g_nbIterations = nbLoops; DISPLAY("- %u iterations -\n", g_nbIterations); } +/* + * Additional Global Variables (Defined Above Use) + * g_stratName + * g_level_constraint + * g_alreadyTested + * g_maxTries + */ + /*-******************************************************* * Private functions *********************************************************/ /* accuracy in seconds only, span can be multiple years */ -static double BMK_timeSpan(time_t tStart) { return difftime(time(NULL), tStart); } +static U32 BMK_timeSpan(UTIL_time_t tStart) { return (U32)(UTIL_clockSpanMicro(tStart) / 1000000ULL); } static size_t BMK_findMaxMem(U64 requiredMem) { @@ -134,14 +304,14 @@ static size_t BMK_findMaxMem(U64 requiredMem) requiredMem = (((requiredMem >> 26) + 1) << 26); if (requiredMem > maxMemory) requiredMem = maxMemory; - requiredMem += 2*step; - while (!testmem) { - requiredMem -= step; + requiredMem += 2 * step; + while (!testmem && requiredMem > 0) { testmem = malloc ((size_t)requiredMem); + requiredMem -= step; } free (testmem); - return (size_t) (requiredMem - step); + return (size_t) requiredMem; } @@ -198,14 +368,9 @@ static void findClockGranularity(void) { DEBUGOUTPUT("Granularity: %llu\n", (unsigned long long)g_clockGranularity); } -typedef struct { - U32 cSpeed; /* bytes / sec */ - U32 dSpeed; - U32 cMem; /* bytes */ -} constraint_t; - +/* allows zeros */ #define CLAMPCHECK(val,min,max) { \ - if (val && (((val)<(min)) | ((val)>(max)))) { \ + if (((val)<(min)) | ((val)>(max))) { \ DISPLAY("INVALID PARAMETER CONSTRAINTS\n"); \ return 0; \ } } @@ -213,41 +378,58 @@ typedef struct { /* Like ZSTD_checkCParams() but allows 0's */ /* no check on targetLen? */ -static int cParamValid(ZSTD_compressionParameters paramTarget) { - CLAMPCHECK(paramTarget.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX); - CLAMPCHECK(paramTarget.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX); - CLAMPCHECK(paramTarget.searchLength, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX); - CLAMPCHECK(paramTarget.windowLog, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX); - CLAMPCHECK(paramTarget.chainLog, ZSTD_CHAINLOG_MIN, ZSTD_CHAINLOG_MAX); - if(paramTarget.targetLength > ZSTD_TARGETLENGTH_MAX) { - DISPLAY("INVALID PARAMETER CONSTRAINTS\n"); - return 0; - } - if(paramTarget.strategy > ZSTD_btultra) { - DISPLAY("INVALID PARAMETER CONSTRAINTS\n"); - return 0; +static int paramValid(paramValues_t paramTarget) { + U32 i; + for(i = 0; i < NUM_PARAMS; i++) { + CLAMPCHECK(paramTarget.vals[i], mintable[i], maxtable[i]); } + //TODO: Strategy could be valid at 0 before, is that right? return 1; } -static void cParamZeroMin(ZSTD_compressionParameters* paramTarget) { - paramTarget->windowLog = paramTarget->windowLog ? paramTarget->windowLog : ZSTD_WINDOWLOG_MIN; - paramTarget->searchLog = paramTarget->searchLog ? paramTarget->searchLog : ZSTD_SEARCHLOG_MIN; - paramTarget->chainLog = paramTarget->chainLog ? paramTarget->chainLog : ZSTD_CHAINLOG_MIN; - paramTarget->hashLog = paramTarget->hashLog ? paramTarget->hashLog : ZSTD_HASHLOG_MIN; - paramTarget->searchLength = paramTarget->searchLength ? paramTarget->searchLength : ZSTD_SEARCHLENGTH_MIN; - paramTarget->targetLength = paramTarget->targetLength ? paramTarget->targetLength : 0; +//TODO: doesn't affect strategy? +static paramValues_t cParamUnsetMin(paramValues_t paramTarget) { + varInds_t i; + for(i = 0; i < NUM_PARAMS; i++) { + if(paramTarget.vals[i] == PARAM_UNSET) { + paramTarget.vals[i] = mintable[i]; + } + } + return paramTarget; } -static void BMK_translateAdvancedParams(const ZSTD_compressionParameters params) -{ - DISPLAY("--zstd=windowLog=%u,chainLog=%u,hashLog=%u,searchLog=%u,searchLength=%u,targetLength=%u,strategy=%u \n", - params.windowLog, params.chainLog, params.hashLog, params.searchLog, params.searchLength, params.targetLength, (U32)(params.strategy)); +static void BMK_translateAdvancedParams(FILE* f, const paramValues_t params) { + U32 i; + fprintf(f,"--zstd="); + for(i = 0; i < NUM_PARAMS; i++) { + fprintf(f,"%s", g_paramNames[i]); + fprintf(f,"=%u", params.vals[i]); + if(i != NUM_PARAMS - 1) { + fprintf(f, ","); + } + } + fprintf(f, "\n"); +} + + +static const char* g_stratName[ZSTD_btultra+1] = { + "(none) ", "ZSTD_fast ", "ZSTD_dfast ", + "ZSTD_greedy ", "ZSTD_lazy ", "ZSTD_lazy2 ", + "ZSTD_btlazy2 ", "ZSTD_btopt ", "ZSTD_btultra "}; + +static void BMK_displayOneResult(FILE* f, winnerInfo_t res, size_t srcSize) { + res.params = cParamUnsetMin(res.params); + fprintf(f," {%3u,%3u,%3u,%3u,%3u,%3u,%3d, %s}, ", + res.params.vals[wlog_ind], res.params.vals[clog_ind], res.params.vals[hlog_ind], res.params.vals[slog_ind], res.params.vals[slen_ind], + res.params.vals[tlen_ind], (int)res.params.vals[fadt_ind], g_stratName[res.params.vals[strt_ind]]); + fprintf(f, + " /* R:%5.3f at %5.1f MB/s - %5.1f MB/s */\n", + (double)srcSize / res.result.cSize, (double)res.result.cSpeed / (1 MB), (double)res.result.dSpeed / (1 MB)); } /* checks results are feasible */ static int feasible(const BMK_result_t results, const constraint_t target) { - return (results.cSpeed >= target.cSpeed) && (results.dSpeed >= target.dSpeed) && (results.cMem <= target.cMem); + return (results.cSpeed >= target.cSpeed) && (results.dSpeed >= target.dSpeed) && (results.cMem <= target.cMem) && (!g_optmode || results.cSize <= g_lvltarget.cSize); } /* hill climbing value for part 1 */ @@ -267,24 +449,37 @@ static double resultScore(const BMK_result_t res, const size_t srcSize, const co ret = (MIN(1, cs) + MIN(1, ds) + MIN(1, cm))*r1 + rt * rtr + (MAX(0, log(cs))+ MAX(0, log(ds))+ MAX(0, log(cm))) * r2; + return ret; } +/* calculates normalized squared euclidean distance of result1 if it is in the first quadrant relative to lvlRes */ +static double resultDistLvl(const BMK_result_t result1, const BMK_result_t lvlRes) { + double normalizedCSpeedGain1 = (result1.cSpeed / lvlRes.cSpeed) - 1; + double normalizedRatioGain1 = ((double)lvlRes.cSize / result1.cSize) - 1; + if(normalizedRatioGain1 < 0 || normalizedCSpeedGain1 < 0) { + return 0.0; + } + return normalizedRatioGain1 * g_ratioMultiplier + normalizedCSpeedGain1 * g_speedMultiplier; +} + /* return true if r2 strictly better than r1 */ static int compareResultLT(const BMK_result_t result1, const BMK_result_t result2, const constraint_t target, size_t srcSize) { if(feasible(result1, target) && feasible(result2, target)) { - return (result1.cSize > result2.cSize) || (result1.cSize == result2.cSize && result2.cSpeed > result1.cSpeed) - || (result1.cSize == result2.cSize && result2.cSpeed == result1.cSpeed && result2.dSpeed > result1.dSpeed); + if(g_optmode) { + return resultDistLvl(result1, g_lvltarget) < resultDistLvl(result2, g_lvltarget); + } else { + return (result1.cSize > result2.cSize) || (result1.cSize == result2.cSize && result2.cSpeed > result1.cSpeed) + || (result1.cSize == result2.cSize && result2.cSpeed == result1.cSpeed && result2.dSpeed > result1.dSpeed); + } } return feasible(result2, target) || (!feasible(result1, target) && (resultScore(result1, srcSize, target) < resultScore(result2, srcSize, target))); - } -/* factor sort of arbitrary */ static constraint_t relaxTarget(constraint_t target) { target.cMem = (U32)-1; - target.cSpeed *= 0.9; - target.dSpeed *= 0.9; + target.cSpeed *= ((double)g_strictness) / 100; + target.dSpeed *= ((double)g_strictness) / 100; return target; } @@ -292,33 +487,16 @@ static constraint_t relaxTarget(constraint_t target) { * Bench functions *********************************************************/ -const char* g_stratName[ZSTD_btultra+1] = { - "(none) ", "ZSTD_fast ", "ZSTD_dfast ", - "ZSTD_greedy ", "ZSTD_lazy ", "ZSTD_lazy2 ", - "ZSTD_btlazy2 ", "ZSTD_btopt ", "ZSTD_btultra "}; - -/* benchParam but only takes in one input buffer. */ -static int -BMK_benchParam1(BMK_result_t* resultPtr, - const void* srcBuffer, size_t srcSize, - const ZSTD_compressionParameters cParams) { - - BMK_return_t res = BMK_benchMem(srcBuffer,srcSize, &srcSize, 1, BASE_CLEVEL, &cParams, NULL, 0, 0, "File"); - *resultPtr = res.result; - return res.error; -} - -typedef struct { - BMK_result_t result; - ZSTD_compressionParameters params; -} winnerInfo_t; - -static ZSTD_compressionParameters emptyParams(void) { - ZSTD_compressionParameters p = { 0, 0, 0, 0, 0, 0, (ZSTD_strategy)0 }; +static paramValues_t emptyParams(void) { + U32 i; + paramValues_t p; + for(i = 0; i < NUM_PARAMS; i++) { + p.vals[i] = PARAM_UNSET; + } return p; } -static winnerInfo_t initWinnerInfo(ZSTD_compressionParameters p) { +static winnerInfo_t initWinnerInfo(paramValues_t p) { winnerInfo_t w1; w1.result.cSpeed = 0.; w1.result.dSpeed = 0.; @@ -348,46 +526,25 @@ typedef struct { ZSTD_DCtx* dctx; } contexts_t; -static int -BMK_benchParam(BMK_result_t* resultPtr, - const buffers_t buf, const contexts_t ctx, - const ZSTD_compressionParameters cParams) { - BMK_return_t res = BMK_benchMem(buf.srcPtrs[0], buf.srcSize, buf.srcSizes, (unsigned)buf.nbBlocks, 0, &cParams, ctx.dictBuffer, ctx.dictSize, 0, "Files"); - *resultPtr = res.result; - return res.error; -} - /*-******************************************************* -* From Paramgrill +* From bench.c *********************************************************/ static void BMK_initCCtx(ZSTD_CCtx* ctx, const void* dictBuffer, const size_t dictBufferSize, const int cLevel, - const ZSTD_compressionParameters* comprParams, const BMK_advancedParams_t* adv) { + const paramValues_t* comprParams) { + varInds_t i; ZSTD_CCtx_reset(ctx); ZSTD_CCtx_resetParameters(ctx); - if (adv->nbWorkers==1) { - ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbWorkers, 0); - } else { - ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbWorkers, adv->nbWorkers); - } ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionLevel, cLevel); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_enableLongDistanceMatching, adv->ldmFlag); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmMinMatch, adv->ldmMinMatch); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmHashLog, adv->ldmHashLog); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmBucketSizeLog, adv->ldmBucketSizeLog); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmHashEveryLog, adv->ldmHashEveryLog); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_windowLog, comprParams->windowLog); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_hashLog, comprParams->hashLog); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_chainLog, comprParams->chainLog); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_searchLog, comprParams->searchLog); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_minMatch, comprParams->searchLength); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_targetLength, comprParams->targetLength); - ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionStrategy, comprParams->strategy); + + for(i = 0; i < NUM_PARAMS; i++) { + if(comprParams->vals[i] != PARAM_UNSET) + ZSTD_CCtx_setParameter(ctx, cctxSetParamTable[i], comprParams->vals[i]); + } ZSTD_CCtx_loadDictionary(ctx, dictBuffer, dictBufferSize); } - static void BMK_initDCtx(ZSTD_DCtx* dctx, const void* dictBuffer, const size_t dictBufferSize) { ZSTD_DCtx_reset(dctx); @@ -399,13 +556,12 @@ typedef struct { const void* dictBuffer; size_t dictBufferSize; int cLevel; - const ZSTD_compressionParameters* comprParams; - const BMK_advancedParams_t* adv; + const paramValues_t* comprParams; } BMK_initCCtxArgs; static size_t local_initCCtx(void* payload) { const BMK_initCCtxArgs* ag = (const BMK_initCCtxArgs*)payload; - BMK_initCCtx(ag->ctx, ag->dictBuffer, ag->dictBufferSize, ag->cLevel, ag->comprParams, ag->adv); + BMK_initCCtx(ag->ctx, ag->dictBuffer, ag->dictBufferSize, ag->cLevel, ag->comprParams); return 0; } @@ -479,9 +635,223 @@ static size_t local_defaultDecompress( } /*-******************************************************* -* From Paramgrill End +* From bench.c End *********************************************************/ +static void freeNonSrcBuffers(const buffers_t b) { + free(b.srcPtrs); + free(b.srcSizes); + + if(b.dstPtrs != NULL) { + free(b.dstPtrs[0]); + } + free(b.dstPtrs); + free(b.dstCapacities); + free(b.dstSizes); + + if(b.resPtrs != NULL) { + free(b.resPtrs[0]); + } + free(b.resPtrs); + free(b.resSizes); +} + +static void freeBuffers(const buffers_t b) { + if(b.srcPtrs != NULL) { + free(b.srcBuffer); + } + freeNonSrcBuffers(b); +} + +/* srcBuffer will be freed by freeBuffers now */ +static int createBuffersFromMemory(buffers_t* buff, void * srcBuffer, size_t nbFiles, + const size_t* fileSizes) +{ + size_t pos = 0, n, blockSize; + U32 maxNbBlocks, blockNb = 0; + buff->srcSize = 0; + for(n = 0; n < nbFiles; n++) { + buff->srcSize += fileSizes[n]; + } + + if(buff->srcSize == 0) { + DISPLAY("No data to bench\n"); + return 1; + } + + blockSize = g_blockSize ? g_blockSize : buff->srcSize; + maxNbBlocks = (U32) ((buff->srcSize + (blockSize-1)) / blockSize) + (U32)nbFiles; + + buff->srcPtrs = (const void**)calloc(maxNbBlocks, sizeof(void*)); + buff->srcSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + + buff->dstPtrs = (void**)calloc(maxNbBlocks, sizeof(void*)); + buff->dstCapacities = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + buff->dstSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + + buff->resPtrs = (void**)calloc(maxNbBlocks, sizeof(void*)); + buff->resSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); + + if(!buff->srcPtrs || !buff->srcSizes || !buff->dstPtrs || !buff->dstCapacities || !buff->dstSizes || !buff->resPtrs || !buff->resSizes) { + DISPLAY("alloc error\n"); + freeNonSrcBuffers(*buff); + return 1; + } + + buff->srcBuffer = srcBuffer; + buff->srcPtrs[0] = (const void*)buff->srcBuffer; + buff->dstPtrs[0] = malloc(ZSTD_compressBound(buff->srcSize) + (maxNbBlocks * 1024)); + buff->resPtrs[0] = malloc(buff->srcSize); + + if(!buff->dstPtrs[0] || !buff->resPtrs[0]) { + DISPLAY("alloc error\n"); + freeNonSrcBuffers(*buff); + return 1; + } + + for(n = 0; n < nbFiles; n++) { + size_t pos_end = pos + fileSizes[n]; + for(; pos < pos_end; blockNb++) { + buff->srcPtrs[blockNb] = (const void*)((char*)srcBuffer + pos); + buff->srcSizes[blockNb] = blockSize; + pos += blockSize; + } + + if(fileSizes[n] > 0) { buff->srcSizes[blockNb - 1] = ((fileSizes[n] - 1) % blockSize) + 1; } + pos = pos_end; + } + + buff->dstCapacities[0] = ZSTD_compressBound(buff->srcSizes[0]); + buff->dstSizes[0] = buff->dstCapacities[0]; + buff->resSizes[0] = buff->srcSizes[0]; + + for(n = 1; n < blockNb; n++) { + buff->dstPtrs[n] = ((char*)buff->dstPtrs[n-1]) + buff->dstCapacities[n-1]; + buff->resPtrs[n] = ((char*)buff->resPtrs[n-1]) + buff->resSizes[n-1]; + buff->dstCapacities[n] = ZSTD_compressBound(buff->srcSizes[n]); + buff->dstSizes[n] = buff->dstCapacities[n]; + buff->resSizes[n] = buff->srcSizes[n]; + } + + buff->nbBlocks = blockNb; + + return 0; +} + +/* allocates buffer's arguments. returns success / failuere */ +static int createBuffers(buffers_t* buff, const char* const * const fileNamesTable, + size_t nbFiles) { + size_t pos = 0; + size_t n; + size_t totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, (U32)nbFiles); + size_t benchedSize = MIN(BMK_findMaxMem(totalSizeToLoad * 3) / 3, totalSizeToLoad); + size_t* fileSizes = calloc(sizeof(size_t), nbFiles); + void* srcBuffer = NULL; + int ret = 0; + + if(!totalSizeToLoad || !benchedSize) { + ret = 1; + DISPLAY("Nothing to Bench\n"); + goto _cleanUp; + } + + srcBuffer = malloc(benchedSize); + + if(!fileSizes || !srcBuffer) { + ret = 1; + goto _cleanUp; + } + + for(n = 0; n < nbFiles; n++) { + FILE* f; + U64 fileSize = UTIL_getFileSize(fileNamesTable[n]); + if (UTIL_isDirectory(fileNamesTable[n])) { + DISPLAY("Ignoring %s directory... \n", fileNamesTable[n]); + continue; + } + if (fileSize == UTIL_FILESIZE_UNKNOWN) { + DISPLAY("Cannot evaluate size of %s, ignoring ... \n", fileNamesTable[n]); + continue; + } + f = fopen(fileNamesTable[n], "rb"); + if (f==NULL) { + DISPLAY("impossible to open file %s\n", fileNamesTable[n]); + fclose(f); + ret = 10; + goto _cleanUp; + } + + DISPLAY("Loading %s... \r", fileNamesTable[n]); + + if (fileSize + pos > benchedSize) fileSize = benchedSize - pos, nbFiles=n; /* buffer too small - stop after this file */ + { + char* buffer = (char*)(srcBuffer); + size_t const readSize = fread((buffer)+pos, 1, (size_t)fileSize, f); + fclose(f); + if (readSize != (size_t)fileSize) { /* should we accept partial read? */ + DISPLAY("could not read %s", fileNamesTable[n]); + ret = 1; + goto _cleanUp; + } + + fileSizes[n] = readSize; + pos += readSize; + } + } + + ret = createBuffersFromMemory(buff, srcBuffer, nbFiles, fileSizes); + +_cleanUp: + if(ret) { free(srcBuffer); } + free(fileSizes); + return ret; +} + +static void freeContexts(const contexts_t ctx) { + free(ctx.dictBuffer); + ZSTD_freeCCtx(ctx.cctx); + ZSTD_freeDCtx(ctx.dctx); +} + +static int createContexts(contexts_t* ctx, const char* dictFileName) { + FILE* f; + size_t readSize; + ctx->cctx = ZSTD_createCCtx(); + ctx->dctx = ZSTD_createDCtx(); + if(dictFileName == NULL) { + ctx->dictSize = 0; + ctx->dictBuffer = NULL; + return 0; + } + ctx->dictSize = UTIL_getFileSize(dictFileName); + ctx->dictBuffer = malloc(ctx->dictSize); + + f = fopen(dictFileName, "rb"); + + if(!f) { + DISPLAY("unable to open file\n"); + fclose(f); + freeContexts(*ctx); + return 1; + } + + if(ctx->dictSize > 64 MB || !(ctx->dictBuffer)) { + DISPLAY("dictionary too large\n"); + fclose(f); + freeContexts(*ctx); + return 1; + } + readSize = fread(ctx->dictBuffer, 1, ctx->dictSize, f); + if(readSize != ctx->dictSize) { + DISPLAY("unable to read file\n"); + fclose(f); + freeContexts(*ctx); + return 1; + } + fclose(f); + return 0; +} + /* Replicate functionality of benchMemAdvanced, but with pre-split src / dst buffers */ /* The purpose is so that sufficient information is returned so that a decompression call to benchMemInvertible is possible */ /* BMK_benchMemAdvanced(srcBuffer,srcSize, dstBuffer, dstSize, fileSizes, nbFiles, 0, &cParams, dictBuffer, dictSize, ctx, dctx, 0, "File", &adv); */ @@ -490,7 +860,7 @@ static size_t local_defaultDecompress( /* if in decodeOnly, then srcPtr's will be compressed blocks, and uncompressedBlocks will be written to dstPtrs? */ /* dictionary nullable, nothing else though. */ static BMK_return_t BMK_benchMemInvertible(const buffers_t buf, const contexts_t ctx, - const int cLevel, const ZSTD_compressionParameters* comprParams, + const int cLevel, const paramValues_t* comprParams, const BMK_mode_t mode, const BMK_loopMode_t loopMode, const unsigned nbSeconds) { U32 i; @@ -509,11 +879,6 @@ static BMK_return_t BMK_benchMemInvertible(const buffers_t buf, const contexts_t ZSTD_CCtx* cctx = ctx.cctx; ZSTD_DCtx* dctx = ctx.dctx; - BMK_advancedParams_t adv = BMK_initAdvancedParams(); - adv.mode = mode; - adv.loopMode = loopMode; - adv.nbSeconds = nbSeconds; - /* warmimg up memory */ /* can't do this if decode only */ for(i = 0; i < buf.nbBlocks; i++) { @@ -535,7 +900,6 @@ static BMK_return_t BMK_benchMemInvertible(const buffers_t buf, const contexts_t cctxprep.dictBufferSize = dictBufferSize; cctxprep.cLevel = cLevel; cctxprep.comprParams = comprParams; - cctxprep.adv = &adv; dctxprep.dctx = dctx; dctxprep.dictBuffer = dictBuffer; dctxprep.dictBufferSize = dictBufferSize; @@ -625,40 +989,168 @@ static BMK_return_t BMK_benchMemInvertible(const buffers_t buf, const contexts_t } } /* Bench */ - results.result.cMem = (1 << (comprParams->windowLog)) + ZSTD_sizeof_CCtx(cctx); + results.result.cMem = (1 << (comprParams->vals[wlog_ind])) + ZSTD_sizeof_CCtx(cctx); return results; } +static int BMK_benchParam(BMK_result_t* resultPtr, + buffers_t buf, contexts_t ctx, + const paramValues_t cParams) { + BMK_return_t res = BMK_benchMemInvertible(buf, ctx, BASE_CLEVEL, &cParams, BMK_both, BMK_timeMode, 3); + *resultPtr = res.result; + return res.error; +} -static void BMK_printWinner(FILE* f, const U32 cLevel, const BMK_result_t result, const ZSTD_compressionParameters params, const size_t srcSize) +/* comparison function: */ +/* strictly better, strictly worse, equal, speed-side adv, size-side adv */ +//Maybe use compress_only for benchmark first run? +#define WORSE_RESULT 0 +#define BETTER_RESULT 1 +#define ERROR_RESULT 2 + +#define SPEED_RESULT 4 +#define SIZE_RESULT 5 +/* maybe have epsilon-eq to limit table size? */ +static int speedSizeCompare(BMK_result_t r1, BMK_result_t r2) { + if(r1.cSpeed < r2.cSpeed) { + if(r1.cSize >= r2.cSize) { + return BETTER_RESULT; + } + return SPEED_RESULT; /* r2 is smaller but not faster. */ + } else { + if(r1.cSize <= r2.cSize) { + return WORSE_RESULT; + } + return SIZE_RESULT; /* r2 is faster but not smaller */ + } +} + +/* 0 for insertion, 1 for no insert */ +/* maintain invariant speedSizeCompare(n, n->next) = SPEED_RESULT */ +static int insertWinner(winnerInfo_t w, constraint_t targetConstraints) { + BMK_result_t r = w.result; + winner_ll_node* cur_node = g_winners; + /* first node to insert */ + if(!feasible(r, targetConstraints)) { + return 1; + } + + if(g_winners == NULL) { + winner_ll_node* first_node = malloc(sizeof(winner_ll_node)); + if(first_node == NULL) { + return 1; + } + first_node->next = NULL; + first_node->res = w; + g_winners = first_node; + return 0; + } + + while(cur_node->next != NULL) { + switch(speedSizeCompare(cur_node->res.result, r)) { + case WORSE_RESULT: + { + return 1; /* never insert if better */ + } + case BETTER_RESULT: + { + winner_ll_node* tmp; + cur_node->res = cur_node->next->res; + tmp = cur_node->next; + cur_node->next = cur_node->next->next; + free(tmp); + break; + } + case SIZE_RESULT: + { + cur_node = cur_node->next; + break; + } + case SPEED_RESULT: /* insert after first size result, then return */ + { + winner_ll_node* newnode = malloc(sizeof(winner_ll_node)); + if(newnode == NULL) { + return 1; + } + newnode->res = cur_node->res; + cur_node->res = w; + newnode->next = cur_node->next; + cur_node->next = newnode; + return 0; + } + } + + } + + assert(cur_node->next == NULL); + switch(speedSizeCompare(cur_node->res.result, r)) { + case WORSE_RESULT: + { + return 1; /* never insert if better */ + } + case BETTER_RESULT: + { + cur_node->res = w; + return 0; + } + case SIZE_RESULT: + { + winner_ll_node* newnode = malloc(sizeof(winner_ll_node)); + if(newnode == NULL) { + return 1; + } + newnode->res = w; + newnode->next = NULL; + cur_node->next = newnode; + return 0; + } + case SPEED_RESULT: /* insert before first size result, then return */ + { + winner_ll_node* newnode = malloc(sizeof(winner_ll_node)); + if(newnode == NULL) { + return 1; + } + newnode->res = cur_node->res; + cur_node->res = w; + newnode->next = cur_node->next; + cur_node->next = newnode; + return 0; + } + default: + return 1; + } +} + +/* Writes to f the results of a parameter benchmark */ +/* when used with --optimize, will only print results better than previously discovered */ +static void BMK_printWinner(FILE* f, const U32 cLevel, const BMK_result_t result, const paramValues_t params, const size_t srcSize) { char lvlstr[15] = "Custom Level"; - const U64 time = UTIL_clockSpanNano(g_time); - const U64 minutes = time / (60ULL * TIMELOOP_NANOSEC); + winnerInfo_t w; + w.params = params; + w.result = result; - DISPLAY("\r%79s\r", ""); - - fprintf(f," {%3u,%3u,%3u,%3u,%3u,%3u, %s }, ", - params.windowLog, params.chainLog, params.hashLog, params.searchLog, params.searchLength, - params.targetLength, g_stratName[(U32)(params.strategy)]); + fprintf(f, "\r%79s\r", ""); if(cLevel != CUSTOM_LEVEL) { snprintf(lvlstr, 15, " Level %2u ", cLevel); } - fprintf(f, - "/* %s */ /* R:%5.3f at %5.1f MB/s - %5.1f MB/s */", - lvlstr, (double)srcSize / result.cSize, (double)result.cSpeed / (1 << 20), (double)result.dSpeed / (1 << 20)); + fprintf(f, "/* %s */ ", lvlstr); + BMK_displayOneResult(f, w, srcSize); - if(TIMED) { fprintf(f, " - %1lu:%2lu:%05.2f", (unsigned long) minutes / 60,(unsigned long) minutes % 60, (double)(time - minutes * TIMELOOP_NANOSEC * 60ULL)/TIMELOOP_NANOSEC); } + if(TIMED) { + const U64 time = UTIL_clockSpanNano(g_time); + const U64 minutes = time / (60ULL * TIMELOOP_NANOSEC); + fprintf(f, " - %1lu:%2lu:%05.2f", (unsigned long) minutes / 60,(unsigned long) minutes % 60, (double)(time - minutes * TIMELOOP_NANOSEC * 60ULL)/TIMELOOP_NANOSEC); + } fprintf(f, "\n"); } -static void BMK_printWinnerOpt(FILE* f, const U32 cLevel, const BMK_result_t result, const ZSTD_compressionParameters params, const constraint_t targetConstraints, const size_t srcSize) +static void BMK_printWinnerOpt(FILE* f, const U32 cLevel, const BMK_result_t result, const paramValues_t params, const constraint_t targetConstraints, const size_t srcSize) { /* global winner used for constraints */ - static winnerInfo_t g_winner = { { 0, 0, (size_t)-1, (size_t)-1 } , { 0, 0, 0, 0, 0, 0, ZSTD_fast } }; - + static winnerInfo_t g_winner = { { (size_t)-1LL, 0, 0, (size_t)-1LL }, { { PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET, PARAM_UNSET } } }; if(DEBUG || compareResultLT(g_winner.result, result, targetConstraints, srcSize)) { if(DEBUG && compareResultLT(g_winner.result, result, targetConstraints, srcSize)) { DISPLAY("New Winner: \n"); @@ -667,14 +1159,44 @@ static void BMK_printWinnerOpt(FILE* f, const U32 cLevel, const BMK_result_t res BMK_printWinner(f, cLevel, result, params, srcSize); if(compareResultLT(g_winner.result, result, targetConstraints, srcSize)) { - BMK_translateAdvancedParams(params); + BMK_translateAdvancedParams(f, params); g_winner.result = result; g_winner.params = params; } } + + //prints out tradeoff table if using lvloptimize + if(g_optmode && g_optimizer) { + winnerInfo_t w; + winner_ll_node* n; + w.result = result; + w.params = params; + insertWinner(w, targetConstraints); + + if(!DEBUG) { fprintf(f, "\033c"); } + fprintf(f, "\n"); + + /* the table */ + fprintf(f, "================================\n"); + for(n = g_winners; n != NULL; n = n->next) { + fprintf(f, "\r%79s\r", ""); + BMK_displayOneResult(f, n->res, srcSize); + } + fprintf(f, "================================\n"); + fprintf(f, "Level Bounds: R: > %.3f AND C: < %.1f MB/s \n\n", + (double)srcSize / g_lvltarget.cSize, (double)g_lvltarget.cSpeed / (1 MB)); + + + fprintf(f, "Overall Winner: \n"); + BMK_displayOneResult(f, g_winner, srcSize); + BMK_translateAdvancedParams(f, g_winner.params); + + fprintf(f, "Latest BMK: \n");\ + BMK_displayOneResult(f, w, srcSize); + } } -static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, size_t srcSize) +static void BMK_printWinners2(FILE* f, const oldWinnerInfo_t* winners, size_t srcSize) { int cLevel; @@ -682,11 +1204,11 @@ static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, size_t srcSi fprintf(f, " /* W, C, H, S, L, T, strat */ \n"); for (cLevel=0; cLevel <= NB_LEVELS_TRACKED; cLevel++) - BMK_printWinner(f, cLevel, winners[cLevel].result, winners[cLevel].params, srcSize); + BMK_printWinner(f, cLevel, winners[cLevel].result, cParamsToPVals(winners[cLevel].params), srcSize); } -static void BMK_printWinners(FILE* f, const winnerInfo_t* winners, size_t srcSize) +static void BMK_printWinners(FILE* f, const oldWinnerInfo_t* winners, size_t srcSize) { fseek(f, 0, SEEK_SET); BMK_printWinners2(f, winners, srcSize); @@ -723,14 +1245,14 @@ static void BMK_init_level_constraints(int bytePerSec_level1) } } } -static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters params, - const void* srcBuffer, size_t srcSize) +static int BMK_seed(oldWinnerInfo_t* winners, const ZSTD_compressionParameters params, + buffers_t buf, contexts_t ctx) { BMK_result_t testResult; int better = 0; int cLevel; - BMK_benchParam1(&testResult, srcBuffer, srcSize, params); + BMK_benchParam(&testResult, buf, ctx, cParamsToPVals(params)); for (cLevel = 1; cLevel <= NB_LEVELS_TRACKED; cLevel++) { @@ -746,15 +1268,15 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para /* first solution for this cLevel */ winners[cLevel].result = testResult; winners[cLevel].params = params; - BMK_printWinner(stdout, cLevel, testResult, params, srcSize); + BMK_printWinner(stdout, cLevel, testResult, cParamsToPVals(params), buf.srcSize); better = 1; continue; } if ((double)testResult.cSize <= ((double)winners[cLevel].result.cSize * (1. + (0.02 / cLevel))) ) { /* Validate solution is "good enough" */ - double W_ratio = (double)srcSize / testResult.cSize; - double O_ratio = (double)srcSize / winners[cLevel].result.cSize; + double W_ratio = (double)buf.srcSize / testResult.cSize; + double O_ratio = (double)buf.srcSize / winners[cLevel].result.cSize; double W_ratioNote = log (W_ratio); double O_ratioNote = log (O_ratio); size_t W_DMemUsed = (1 << params.windowLog) + (16 KB); @@ -811,7 +1333,7 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para winners[cLevel].result = testResult; winners[cLevel].params = params; - BMK_printWinner(stdout, cLevel, testResult, params, srcSize); + BMK_printWinner(stdout, cLevel, testResult, cParamsToPVals(params), buf.srcSize); better = 1; } } @@ -828,24 +1350,26 @@ static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters para /* no point in windowLog < chainLog (no point 2x chainLog for bt) */ /* now with built in bounds-checking */ /* no longer does anything with sanitizeVarArray + clampcheck */ -static ZSTD_compressionParameters sanitizeParams(ZSTD_compressionParameters params) +static paramValues_t sanitizeParams(paramValues_t params) { - if (params.strategy == ZSTD_fast) - params.chainLog = 0, params.searchLog = 0; - if (params.strategy == ZSTD_dfast) - params.searchLog = 0; - if (params.strategy != ZSTD_btopt && params.strategy != ZSTD_btultra && params.strategy != ZSTD_fast) - params.targetLength = 0; + if (params.vals[strt_ind] == ZSTD_fast) + params.vals[clog_ind] = 0, params.vals[slog_ind] = 0; + if (params.vals[strt_ind] == ZSTD_dfast) + params.vals[slog_ind] = 0; + if (params.vals[strt_ind] != ZSTD_btopt && params.vals[strt_ind] != ZSTD_btultra && params.vals[strt_ind] != ZSTD_fast) + params.vals[tlen_ind] = 0; return params; } -/* new length */ +/* return: new length */ /* keep old array, will need if iter over strategy. */ +/* prunes useless params */ static int sanitizeVarArray(varInds_t* varNew, const int varLength, const varInds_t* varArray, const ZSTD_strategy strat) { int i, j = 0; for(i = 0; i < varLength; i++) { if( !((varArray[i] == clog_ind && strat == ZSTD_fast) + || (varArray[i] == slog_ind && strat == ZSTD_fast) || (varArray[i] == slog_ind && strat == ZSTD_dfast) || (varArray[i] == tlen_ind && strat != ZSTD_btopt && strat != ZSTD_btultra && strat != ZSTD_fast))) { varNew[j] = varArray[i]; @@ -857,75 +1381,31 @@ static int sanitizeVarArray(varInds_t* varNew, const int varLength, const varInd } /* res should be NUM_PARAMS size */ -/* constructs varArray from ZSTD_compressionParameters style parameter */ -static int variableParams(const ZSTD_compressionParameters paramConstraints, varInds_t* res) { +/* constructs varArray from paramValues_t style parameter */ +/* pass in using dict. */ +static int variableParams(const paramValues_t paramConstraints, varInds_t* res, const int usingDictionary) { + varInds_t i; int j = 0; - if(!paramConstraints.windowLog) { - res[j] = wlog_ind; - j++; - } - if(!paramConstraints.chainLog) { - res[j] = clog_ind; - j++; - } - if(!paramConstraints.hashLog) { - res[j] = hlog_ind; - j++; - } - if(!paramConstraints.searchLog) { - res[j] = slog_ind; - j++; - } - if(!paramConstraints.searchLength) { - res[j] = slen_ind; - j++; - } - if(!paramConstraints.targetLength) { - res[j] = tlen_ind; - j++; - } - return j; -} - -/* bin-search on tlen_table for correct index. */ -static int tlen_inv(U32 x) { - int lo = 0; - int hi = TLEN_RANGE; - while(lo < hi) { - int mid = (lo + hi) / 2; - if(tlen_table[mid] < x) { - lo = mid + 1; - } if(tlen_table[mid] == x) { - return mid; - } else { - hi = mid; + for(i = 0; i < NUM_PARAMS; i++) { + if(paramConstraints.vals[i] == PARAM_UNSET) { + if(i == fadt_ind && !usingDictionary) continue; /* don't use fadt if no dictionary */ + res[j] = i; j++; } } - return lo; + return j; } /* amt will probably always be \pm 1? */ /* slight change from old paramVariation, targetLength can only take on powers of 2 now (999 ~= 1024?) */ /* take max/min bounds into account as well? */ -static void paramVaryOnce(const varInds_t paramIndex, const int amt, ZSTD_compressionParameters* ptr) { - switch(paramIndex) - { - case wlog_ind: ptr->windowLog += amt; break; - case clog_ind: ptr->chainLog += amt; break; - case hlog_ind: ptr->hashLog += amt; break; - case slog_ind: ptr->searchLog += amt; break; - case slen_ind: ptr->searchLength += amt; break; - case tlen_ind: - ptr->targetLength = tlen_table[MAX(0, MIN(TLEN_RANGE - 1, tlen_inv(ptr->targetLength) + amt))]; - break; - default: break; - } +static void paramVaryOnce(const varInds_t paramIndex, const int amt, paramValues_t* ptr) { + ptr->vals[paramIndex] = rangeMap(paramIndex, invRangeMap(paramIndex, ptr->vals[paramIndex]) + amt); //TODO: bounds check. } /* varies ptr by nbChanges respecting varyParams*/ -static void paramVariation(ZSTD_compressionParameters* ptr, const varInds_t* varyParams, const int varyLen, const U32 nbChanges) +static void paramVariation(paramValues_t* ptr, const varInds_t* varyParams, const int varyLen, const U32 nbChanges) { - ZSTD_compressionParameters p; + paramValues_t p; U32 validated = 0; while (!validated) { U32 i; @@ -934,7 +1414,7 @@ static void paramVariation(ZSTD_compressionParameters* ptr, const varInds_t* var const U32 changeID = FUZ_rand(&g_rand) % (varyLen << 1); paramVaryOnce(varyParams[changeID >> 1], ((changeID & 1) << 1) - 1, &p); } - validated = !ZSTD_isError(ZSTD_checkCParams(p)); + validated = paramValid(p); } *ptr = p; } @@ -944,52 +1424,33 @@ static size_t memoTableLen(const varInds_t* varyParams, const int varyLen) { size_t arrayLen = 1; int i; for(i = 0; i < varyLen; i++) { - arrayLen *= rangetable[varyParams[i]]; + if(varyParams[i] != strt_ind) { + arrayLen *= rangetable[varyParams[i]]; + } } return arrayLen; } /* returns unique index in memotable of compression parameters */ -static unsigned memoTableInd(const ZSTD_compressionParameters* ptr, const varInds_t* varyParams, const int varyLen) { +static unsigned memoTableInd(const paramValues_t* ptr, const varInds_t* varyParams, const int varyLen) { int i; unsigned ind = 0; for(i = 0; i < varyLen; i++) { - switch(varyParams[i]) { - case wlog_ind: ind *= WLOG_RANGE; ind += ptr->windowLog - - ZSTD_WINDOWLOG_MIN ; break; - case clog_ind: ind *= CLOG_RANGE; ind += ptr->chainLog - - ZSTD_CHAINLOG_MIN ; break; - case hlog_ind: ind *= HLOG_RANGE; ind += ptr->hashLog - - ZSTD_HASHLOG_MIN ; break; - case slog_ind: ind *= SLOG_RANGE; ind += ptr->searchLog - - ZSTD_SEARCHLOG_MIN ; break; - case slen_ind: ind *= SLEN_RANGE; ind += ptr->searchLength - - ZSTD_SEARCHLENGTH_MIN; break; - case tlen_ind: ind *= TLEN_RANGE; ind += tlen_inv(ptr->targetLength) - - ZSTD_TARGETLENGTH_MIN; break; - } + varInds_t v = varyParams[i]; + if(v == strt_ind) continue; /* exclude strategy from memotable */ + ind *= rangetable[v]; ind += invRangeMap(v, ptr->vals[v]); } return ind; } /* inverse of above function (from index to parameters) */ -static void memoTableIndInv(ZSTD_compressionParameters* ptr, const varInds_t* varyParams, const int varyLen, size_t ind) { +static void memoTableIndInv(paramValues_t* ptr, const varInds_t* varyParams, const int varyLen, size_t ind) { int i; for(i = varyLen - 1; i >= 0; i--) { - switch(varyParams[i]) { - case wlog_ind: ptr->windowLog = ind % WLOG_RANGE + ZSTD_WINDOWLOG_MIN; - ind /= WLOG_RANGE; break; - case clog_ind: ptr->chainLog = ind % CLOG_RANGE + ZSTD_CHAINLOG_MIN; - ind /= CLOG_RANGE; break; - case hlog_ind: ptr->hashLog = ind % HLOG_RANGE + ZSTD_HASHLOG_MIN; - ind /= HLOG_RANGE; break; - case slog_ind: ptr->searchLog = ind % SLOG_RANGE + ZSTD_SEARCHLOG_MIN; - ind /= SLOG_RANGE; break; - case slen_ind: ptr->searchLength = ind % SLEN_RANGE + ZSTD_SEARCHLENGTH_MIN; - ind /= SLEN_RANGE; break; - case tlen_ind: ptr->targetLength = tlen_table[(ind % TLEN_RANGE)]; - ind /= TLEN_RANGE; break; - } + varInds_t v = varyParams[i]; + if(v == strt_ind) continue; + ptr->vals[v] = rangeMap(v, ind % rangetable[v]); + ind /= rangetable[v]; } } @@ -998,36 +1459,36 @@ static void memoTableIndInv(ZSTD_compressionParameters* ptr, const varInds_t* va * redundant / obviously non-optimal parameter configurations (e.g. wlog - 1 larger) * than srcSize, clog > wlog, ... */ -static void initMemoTable(U8* memoTable, ZSTD_compressionParameters paramConstraints, const constraint_t target, const varInds_t* varyParams, const int varyLen, const size_t srcSize) { +static void initMemoTable(U8* memoTable, paramValues_t paramConstraints, const constraint_t target, const varInds_t* varyParams, const int varyLen, const size_t srcSize) { size_t i; size_t arrayLen = memoTableLen(varyParams, varyLen); - int cwFixed = !paramConstraints.chainLog || !paramConstraints.windowLog; - int scFixed = !paramConstraints.searchLog || !paramConstraints.chainLog; - int whFixed = !paramConstraints.windowLog || !paramConstraints.hashLog; - int wFixed = !paramConstraints.windowLog; + int cwFixed = paramConstraints.vals[clog_ind] == PARAM_UNSET || paramConstraints.vals[wlog_ind] == PARAM_UNSET; + int scFixed = paramConstraints.vals[slog_ind] == PARAM_UNSET || paramConstraints.vals[clog_ind] == PARAM_UNSET; + int whFixed = paramConstraints.vals[wlog_ind] == PARAM_UNSET || paramConstraints.vals[hlog_ind] == PARAM_UNSET; + int wFixed = paramConstraints.vals[wlog_ind] == PARAM_UNSET; int j = 0; assert(memoTable != NULL); memset(memoTable, 0, arrayLen); - cParamZeroMin(¶mConstraints); + paramConstraints = cParamUnsetMin(paramConstraints); for(i = 0; i < arrayLen; i++) { memoTableIndInv(¶mConstraints, varyParams, varyLen, i); - if(ZSTD_estimateCStreamSize_usingCParams(paramConstraints) > (size_t)target.cMem) { + if(ZSTD_estimateCStreamSize_usingCParams(pvalsToCParams(paramConstraints)) > (size_t)target.cMem) { memoTable[i] = 255; j++; } - if(wFixed && (1ULL << (paramConstraints.windowLog - 1)) > srcSize) { + if(wFixed && (1ULL << (paramConstraints.vals[wlog_ind] - 1)) >= srcSize && paramConstraints.vals[wlog_ind] != mintable[wlog_ind]) { memoTable[i] = 255; } /* nil out parameter sets equivalent to others. */ - if(cwFixed/* at most least 1 param fixed. */) { - if(paramConstraints.strategy == ZSTD_btlazy2 || paramConstraints.strategy == ZSTD_btopt || paramConstraints.strategy == ZSTD_btultra) { - if(paramConstraints.chainLog > paramConstraints.windowLog + 1) { + if(cwFixed) { + if(paramConstraints.vals[strt_ind] == ZSTD_btlazy2 || paramConstraints.vals[strt_ind] == ZSTD_btopt || paramConstraints.vals[strt_ind] == ZSTD_btultra) { + if(paramConstraints.vals[clog_ind] > paramConstraints.vals[wlog_ind]+ 1) { if(memoTable[i] != 255) { j++; } memoTable[i] = 255; } } else { - if(paramConstraints.chainLog > paramConstraints.windowLog) { + if(paramConstraints.vals[clog_ind] > paramConstraints.vals[wlog_ind]) { if(memoTable[i] != 255) { j++; } memoTable[i] = 255; } @@ -1035,14 +1496,14 @@ static void initMemoTable(U8* memoTable, ZSTD_compressionParameters paramConstra } if(scFixed) { - if(paramConstraints.searchLog > paramConstraints.chainLog) { + if(paramConstraints.vals[slog_ind] > paramConstraints.vals[clog_ind]) { if(memoTable[i] != 255) { j++; } memoTable[i] = 255; } } if(whFixed) { - if(paramConstraints.hashLog > paramConstraints.windowLog + 1) { + if(paramConstraints.vals[hlog_ind] > paramConstraints.vals[wlog_ind] + 1) { if(memoTable[i] != 255) { j++; } memoTable[i] = 255; } @@ -1050,7 +1511,7 @@ static void initMemoTable(U8* memoTable, ZSTD_compressionParameters paramConstra } DEBUGOUTPUT("%d / %d Invalid\n", j, (int)i); if((int)i == j) { - DEBUGOUTPUT("!!!Strategy %d totally infeasible\n", (int)paramConstraints.strategy) + DEBUGOUTPUT("!!!Strategy %d totally infeasible\n", (int)paramConstraints.vals[strt_ind]); } } @@ -1066,9 +1527,9 @@ static void freeMemoTableArray(U8** mtAll) { /* inits memotables for all (including mallocs), all strategies */ /* takes unsanitized varyParams */ -static U8** createMemoTableArray(ZSTD_compressionParameters paramConstraints, constraint_t target, const varInds_t* varyParams, const int varyLen, const size_t srcSize) { +static U8** createMemoTableArray(paramValues_t paramConstraints, constraint_t target, const varInds_t* varyParams, const int varyLen, const size_t srcSize) { varInds_t varNew[NUM_PARAMS]; - U8** mtAll = calloc(sizeof(U8*),(ZSTD_btultra + 1)); + U8** mtAll = (U8**)calloc(sizeof(U8*),(ZSTD_btultra + 1)); int i; if(mtAll == NULL) { return NULL; @@ -1087,6 +1548,16 @@ static U8** createMemoTableArray(ZSTD_compressionParameters paramConstraints, co return mtAll; } +static paramValues_t overwriteParams(paramValues_t base, paramValues_t mask) { + U32 i; + for(i = 0; i < NUM_PARAMS; i++) { + if(mask.vals[i] != PARAM_UNSET) { + base.vals[i] = mask.vals[i]; + } + } + return base; +} + #define PARAMTABLELOG 25 #define PARAMTABLESIZE (1<> 3) & PARAMTABLEMASK] */ static BYTE* NB_TESTS_PLAYED(ZSTD_compressionParameters p) { - ZSTD_compressionParameters p2 = sanitizeParams(p); + ZSTD_compressionParameters p2 = pvalsToCParams(sanitizeParams(cParamsToPVals(p))); return &g_alreadyTested[(XXH64((void*)&p2, sizeof(p2), 0) >> 3) & PARAMTABLEMASK]; } -static void playAround(FILE* f, winnerInfo_t* winners, +static void playAround(FILE* f, oldWinnerInfo_t* winners, ZSTD_compressionParameters params, - const void* srcBuffer, size_t srcSize) + buffers_t buf, contexts_t ctx) { int nbVariations = 0; UTIL_time_t const clockStart = UTIL_getTime(); - const U32 unconstrained[NUM_PARAMS] = { 0, 1, 2, 3, 4, 5 }; + const U32 unconstrained[NUM_PARAMS] = { 0, 1, 2, 3, 4, 5, 6 }; /* no fadt */ while (UTIL_clockSpanMicro(clockStart) < g_maxVariationTime) { - ZSTD_compressionParameters p = params; + paramValues_t p = cParamsToPVals(params); + ZSTD_compressionParameters p2; BYTE* b; if (nbVariations++ > g_maxNbVariations) break; - paramVariation(&p, unconstrained, 7, 4); + paramVariation(&p, unconstrained, NUM_PARAMS, 4); + + p2 = pvalsToCParams(p); /* exclude faster if already played params */ - if (FUZ_rand(&g_rand) & ((1 << *NB_TESTS_PLAYED(p))-1)) + if (FUZ_rand(&g_rand) & ((1 << *NB_TESTS_PLAYED(p2))-1)) continue; /* test */ - b = NB_TESTS_PLAYED(p); + b = NB_TESTS_PLAYED(p2); (*b)++; - if (!BMK_seed(winners, p, srcBuffer, srcSize)) continue; + if (!BMK_seed(winners, p2, buf, ctx)) continue; /* improvement found => search more */ - BMK_printWinners(f, winners, srcSize); - playAround(f, winners, p, srcBuffer, srcSize); + BMK_printWinners(f, winners, buf.srcSize); + playAround(f, winners, p2, buf, ctx); } } @@ -1160,7 +1634,7 @@ static ZSTD_compressionParameters randomParams(void) } /* Sets pc to random unmeasured set of parameters */ -static void randomConstrainedParams(ZSTD_compressionParameters* pc, varInds_t* varArray, int varLen, U8* memoTable) +static void randomConstrainedParams(paramValues_t* pc, varInds_t* varArray, int varLen, U8* memoTable) { size_t tries = memoTableLen(varArray, varLen); const size_t maxSize = memoTableLen(varArray, varLen); @@ -1174,37 +1648,25 @@ static void randomConstrainedParams(ZSTD_compressionParameters* pc, varInds_t* v } static void BMK_selectRandomStart( - FILE* f, winnerInfo_t* winners, - const void* srcBuffer, size_t srcSize) + FILE* f, oldWinnerInfo_t* winners, + buffers_t buf, contexts_t ctx) { U32 const id = FUZ_rand(&g_rand) % (NB_LEVELS_TRACKED+1); if ((id==0) || (winners[id].params.windowLog==0)) { /* use some random entry */ - ZSTD_compressionParameters const p = ZSTD_adjustCParams(randomParams(), srcSize, 0); - playAround(f, winners, p, srcBuffer, srcSize); + ZSTD_compressionParameters const p = ZSTD_adjustCParams(randomParams(), buf.srcSize, 0); + playAround(f, winners, p, buf, ctx); } else { - playAround(f, winners, winners[id].params, srcBuffer, srcSize); + playAround(f, winners, winners[id].params, buf, ctx); } } - -static void BMK_benchOnce(const void* srcBuffer, size_t srcSize) -{ - BMK_result_t testResult; - g_params = ZSTD_adjustCParams(g_params, srcSize, 0); - BMK_benchParam1(&testResult, srcBuffer, srcSize, g_params); - DISPLAY("Compression Ratio: %.3f Compress Speed: %.1f MB/s Decompress Speed: %.1f MB/s\n", (double)srcSize / testResult.cSize, - (double)testResult.cSpeed / (1 MB), (double)testResult.dSpeed / (1 MB)); - return; -} - -static void BMK_benchFullTable(const void* srcBuffer, size_t srcSize) +static void BMK_benchFullTable(buffers_t buf, contexts_t ctx, const size_t maxBlockSize) { ZSTD_compressionParameters params; - winnerInfo_t winners[NB_LEVELS_TRACKED+1]; + oldWinnerInfo_t winners[NB_LEVELS_TRACKED+1]; const char* const rfName = "grillResults.txt"; FILE* const f = fopen(rfName, "w"); - const size_t blockSize = g_blockSize ? g_blockSize : srcSize; /* cut by block or not ? */ /* init */ assert(g_singleRun==0); @@ -1212,12 +1674,12 @@ static void BMK_benchFullTable(const void* srcBuffer, size_t srcSize) if (f==NULL) { DISPLAY("error opening %s \n", rfName); exit(1); } if (g_target) { - BMK_init_level_constraints(g_target*(1 MB)); + BMK_init_level_constraints(g_target * (1 MB)); } else { /* baseline config for level 1 */ - ZSTD_compressionParameters const l1params = ZSTD_getCParams(1, blockSize, 0); + ZSTD_compressionParameters const l1params = ZSTD_getCParams(1, maxBlockSize, ctx.dictSize); BMK_result_t testResult; - BMK_benchParam1(&testResult, srcBuffer, srcSize, l1params); + BMK_benchParam(&testResult, buf, ctx, cParamsToPVals(l1params)); BMK_init_level_constraints((int)((testResult.cSpeed * 31) / 32)); } @@ -1225,133 +1687,142 @@ static void BMK_benchFullTable(const void* srcBuffer, size_t srcSize) { const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel(); int i; for (i=0; i<=maxSeeds; i++) { - params = ZSTD_getCParams(i, blockSize, 0); - BMK_seed(winners, params, srcBuffer, srcSize); + params = ZSTD_getCParams(i, maxBlockSize, 0); + BMK_seed(winners, params, buf, ctx); } } - BMK_printWinners(f, winners, srcSize); + BMK_printWinners(f, winners, buf.srcSize); /* start tests */ - { const time_t grillStart = time(NULL); + { const UTIL_time_t grillStart = UTIL_getTime(); do { - BMK_selectRandomStart(f, winners, srcBuffer, srcSize); - } while (BMK_timeSpan(grillStart) < g_grillDuration_s); + BMK_selectRandomStart(f, winners, buf, ctx); + } while (BMK_timeSpan(grillStart) < g_timeLimit_s); } /* end summary */ - BMK_printWinners(f, winners, srcSize); + BMK_printWinners(f, winners, buf.srcSize); DISPLAY("grillParams operations completed \n"); /* clean up*/ fclose(f); } -static void BMK_benchMemInit(const void* srcBuffer, size_t srcSize) -{ - if (g_singleRun) - return BMK_benchOnce(srcBuffer, srcSize); - else - return BMK_benchFullTable(srcBuffer, srcSize); -} +static int benchOnce(buffers_t buf, contexts_t ctx) { + BMK_result_t testResult; + if(BMK_benchParam(&testResult, buf, ctx, g_params)) { + DISPLAY("Error during benchmarking\n"); + return 1; + } + + BMK_printWinner(stdout, CUSTOM_LEVEL, testResult, g_params, buf.srcSize); + + return 0; +} static int benchSample(void) { const char* const name = "Sample 10MB"; - size_t const benchedSize = (10 MB); + size_t const benchedSize = 10 MB; + U32 blockSize = g_blockSize ? g_blockSize : benchedSize; + void* srcBuffer = malloc(benchedSize); + int ret = 0; - void* origBuff = malloc(benchedSize); - if (!origBuff) { perror("not enough memory"); return 12; } + buffers_t buf; + contexts_t ctx; + + if(srcBuffer == NULL) { + DISPLAY("Out of Memory\n"); + return 2; + } - /* Fill buffer */ - RDG_genBuffer(origBuff, benchedSize, g_compressibility, 0.0, 0); + RDG_genBuffer(srcBuffer, benchedSize, g_compressibility, 0.0, 0); + + if(createBuffersFromMemory(&buf, srcBuffer, 1, &benchedSize)) { + DISPLAY("Buffer Creation Error\n"); + free(srcBuffer); + return 3; + } + + if(createContexts(&ctx, NULL)) { + DISPLAY("Context Creation Error\n"); + freeBuffers(buf); + return 1; + } /* bench */ DISPLAY("\r%79s\r", ""); DISPLAY("using %s %i%%: \n", name, (int)(g_compressibility*100)); - BMK_benchMemInit(origBuff, benchedSize); - free(origBuff); - return 0; + if(g_singleRun) { + ret = benchOnce(buf, ctx); + } else { + BMK_benchFullTable(buf, ctx, MIN(blockSize, benchedSize)); + } + + freeBuffers(buf); + freeContexts(ctx); + + return ret; } - /* benchFiles() : * note: while this function takes a table of filenames, * in practice, only the first filename will be used */ -int benchFiles(const char** fileNamesTable, int nbFiles) +int benchFiles(const char** fileNamesTable, int nbFiles, const char* dictFileName, int cLevel) { - int fileIdx=0; + buffers_t buf; + contexts_t ctx; + size_t maxBlockSize = 0, i; + int ret = 0; - /* Loop for each file */ - while (fileIdx inFileSize) benchedSize = (size_t)inFileSize; - if (benchedSize < inFileSize) - DISPLAY("Not enough memory for '%s' full size; testing %i MB only...\n", inFileName, (int)(benchedSize>>20)); - origBuff = malloc(benchedSize); - if (origBuff==NULL) { - DISPLAY("\nError: not enough memory!\n"); - fclose(inFile); - return 12; - } - - /* Fill input buffer */ - DISPLAY("Loading %s... \r", inFileName); - { size_t const readSize = fread(origBuff, 1, benchedSize, inFile); - fclose(inFile); - if(readSize != benchedSize) { - DISPLAY("\nError: problem reading file '%s' !! \n", inFileName); - free(origBuff); - return 13; - } } - - /* bench */ - DISPLAY("\r%79s\r", ""); - DISPLAY("using %s : \n", inFileName); - BMK_benchMemInit(origBuff, benchedSize); - - /* clean */ - free(origBuff); + if(createBuffers(&buf, fileNamesTable, nbFiles)) { + DISPLAY("unable to load files\n"); + return 1; } - return 0; + if(createContexts(&ctx, dictFileName)) { + DISPLAY("unable to load dictionary\n"); + freeBuffers(buf); + return 2; + } + + for(i = 0; i < buf.nbBlocks; i++) { + maxBlockSize = MAX(maxBlockSize, buf.srcSizes[i]); + } + + DISPLAY("\r%79s\r", ""); + if(nbFiles == 1) { + DISPLAY("using %s : \n", fileNamesTable[0]); + } else { + DISPLAY("using %d Files : \n", nbFiles); + } + + g_params = adjustParams(overwriteParams(cParamsToPVals(ZSTD_getCParams(cLevel, maxBlockSize, ctx.dictSize)), g_params), maxBlockSize, ctx.dictSize); + + if(g_singleRun) { + ret = benchOnce(buf, ctx); + } else { + BMK_benchFullTable(buf, ctx, maxBlockSize); + } + + freeBuffers(buf); + freeContexts(ctx); + return ret; } - - -#define WORSE_RESULT 0 -#define BETTER_RESULT 1 -#define ERROR_RESULT 2 - /* Benchmarking which stops when we are sufficiently sure the solution is infeasible / worse than the winner */ -#define VARIANCE 1.1 +#define VARIANCE 1.2 +#define HIGH_VARIANCE 100.0 static int allBench(BMK_result_t* resultPtr, const buffers_t buf, const contexts_t ctx, - const ZSTD_compressionParameters cParams, + const paramValues_t cParams, const constraint_t target, BMK_result_t* winnerResult, int feas) { BMK_return_t benchres; BMK_result_t resultMax; U64 loopDurationC = 0, loopDurationD = 0; - double uncertaintyConstantC, uncertaintyConstantD; + double uncertaintyConstantC = 3., uncertaintyConstantD = 3.; double winnerRS; /* initial benchmarking, gives exact ratio and memory, warms up future runs */ @@ -1369,22 +1840,16 @@ static int allBench(BMK_result_t* resultPtr, /* calculate uncertainty in compression / decompression runs */ if(benchres.result.cSpeed) { loopDurationC = ((buf.srcSize * TIMELOOP_NANOSEC) / benchres.result.cSpeed); - uncertaintyConstantC = ((loopDurationC + (double)(2 * g_clockGranularity))/loopDurationC) * VARIANCE; - } else { - loopDurationC = 0; - uncertaintyConstantC = 3; + uncertaintyConstantC = ((loopDurationC + (double)(2 * g_clockGranularity))/loopDurationC); } if(benchres.result.dSpeed) { loopDurationD = ((buf.srcSize * TIMELOOP_NANOSEC) / benchres.result.dSpeed); - uncertaintyConstantD = ((loopDurationD + (double)(2 * g_clockGranularity))/loopDurationD) * VARIANCE; - } else { - loopDurationD = 0; - uncertaintyConstantD = 3; + uncertaintyConstantD = ((loopDurationD + (double)(2 * g_clockGranularity))/loopDurationD); } /* anything with worse ratio in feas is definitely worse, discard */ - if(feas && benchres.result.cSize < winnerResult->cSize) { + if(feas && benchres.result.cSize < winnerResult->cSize && !g_optmode) { return WORSE_RESULT; } @@ -1408,12 +1873,12 @@ static int allBench(BMK_result_t* resultPtr, /* optimistic assumption of benchres.result */ resultMax = benchres.result; - resultMax.cSpeed *= uncertaintyConstantC; - resultMax.dSpeed *= uncertaintyConstantD; + resultMax.cSpeed *= uncertaintyConstantC * VARIANCE; + resultMax.dSpeed *= uncertaintyConstantD * VARIANCE; /* disregard infeasible results in feas mode */ /* disregard if resultMax < winner in infeas mode */ - if((feas && !feasible(resultMax, target)) || + if((feas && !feasible(resultMax, target)) || (!feas && (winnerRS > resultScore(resultMax, buf.srcSize, target)))) { return WORSE_RESULT; } @@ -1445,7 +1910,6 @@ static int allBench(BMK_result_t* resultPtr, } else { return WORSE_RESULT; } - } #define INFEASIBLE_THRESHOLD 200 @@ -1453,7 +1917,7 @@ static int allBench(BMK_result_t* resultPtr, /* Memoized benchmarking, won't benchmark anything which has already been benchmarked before. */ static int benchMemo(BMK_result_t* resultPtr, const buffers_t buf, const contexts_t ctx, - const ZSTD_compressionParameters cParams, + const paramValues_t cParams, const constraint_t target, BMK_result_t* winnerResult, U8* const memoTable, const varInds_t* varyParams, const int varyLen, const int feas) { @@ -1489,27 +1953,33 @@ static int benchMemo(BMK_result_t* resultPtr, * Phase 2 optimizes in accordance with what the original function sets out to maximize, with * all feasible solutions valued over all infeasible solutions. */ + +/* sanitize all params here. + * all generation after random should be sanitized. (maybe sanitize random) + */ static winnerInfo_t climbOnce(const constraint_t target, - const varInds_t* varArray, const int varLen, - U8* const memoTable, - const buffers_t buf, const contexts_t ctx, - const ZSTD_compressionParameters init) { + const varInds_t* varArray, const int varLen, ZSTD_strategy strat, + U8** memoTableArray, + buffers_t buf, contexts_t ctx, + const paramValues_t init) { /* * cparam - currently considered 'center' * candidate - params to benchmark/results * winner - best option found so far. */ - ZSTD_compressionParameters cparam = init; + paramValues_t cparam = init; winnerInfo_t candidateInfo, winnerInfo; int better = 1; int feas = 0; + varInds_t varNew[NUM_PARAMS]; + int varLenNew = sanitizeVarArray(varNew, varLen, varArray, strat); winnerInfo = initWinnerInfo(init); candidateInfo = winnerInfo; { winnerInfo_t bestFeasible1 = initWinnerInfo(cparam); - DISPLAY("Climb Part 1\n"); + DEBUGOUTPUT("Climb Part 1\n"); while(better) { int i, dist, offset; @@ -1521,17 +1991,22 @@ static winnerInfo_t climbOnce(const constraint_t target, /* all dist-1 candidates */ for(i = 0; i < varLen; i++) { for(offset = -1; offset <= 1; offset += 2) { + CHECKTIME(winnerInfo); candidateInfo.params = cparam; paramVaryOnce(varArray[i], offset, &candidateInfo.params); - candidateInfo.params = sanitizeParams(candidateInfo.params); - if(!ZSTD_isError(ZSTD_checkCParams(candidateInfo.params))) { - int res = benchMemo(&candidateInfo.result, - buf, ctx, - candidateInfo.params, target, &winnerInfo.result, memoTable, - varArray, varLen, feas); + + if(paramValid(candidateInfo.params)) { + int res; + if(strat != candidateInfo.params.vals[strt_ind]) { /* maybe only try strategy switching after exhausting non-switching solutions? */ + strat = candidateInfo.params.vals[strt_ind]; + varLenNew = sanitizeVarArray(varNew, varLen, varArray, strat); + } + res = benchMemo(&candidateInfo.result, buf, ctx, + sanitizeParams(candidateInfo.params), target, &winnerInfo.result, memoTableArray[strat], + varNew, varLenNew, feas); if(res == BETTER_RESULT) { /* synonymous with better when called w/ infeasibleBM */ winnerInfo = candidateInfo; - BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winnerInfo.result, winnerInfo.params, target, buf.srcSize); + BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winnerInfo.result, sanitizeParams(winnerInfo.params), target, buf.srcSize); better = 1; if(compareResultLT(bestFeasible1.result, winnerInfo.result, target, buf.srcSize)) { bestFeasible1 = winnerInfo; @@ -1546,22 +2021,29 @@ static winnerInfo_t climbOnce(const constraint_t target, } for(dist = 2; dist < varLen + 2; dist++) { /* varLen is # dimensions */ - for(i = 0; i < 2 * varLen + 2; i++) { + for(i = 0; i < (1 << varLen) / varLen + 2; i++) { int res; + CHECKTIME(winnerInfo); candidateInfo.params = cparam; /* param error checking already done here */ paramVariation(&candidateInfo.params, varArray, varLen, dist); - res = benchMemo(&candidateInfo.result, - buf, ctx, - candidateInfo.params, target, &winnerInfo.result, memoTable, - varArray, varLen, feas); + + if(strat != candidateInfo.params.vals[strt_ind]) { + strat = candidateInfo.params.vals[strt_ind]; + varLenNew = sanitizeVarArray(varNew, varLen, varArray, strat); + } + + res = benchMemo(&candidateInfo.result, buf, ctx, + sanitizeParams(candidateInfo.params), target, &winnerInfo.result, memoTableArray[strat], + varNew, varLenNew, feas); if(res == BETTER_RESULT) { /* synonymous with better in this case*/ winnerInfo = candidateInfo; - BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winnerInfo.result, winnerInfo.params, target, buf.srcSize); + BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winnerInfo.result, sanitizeParams(winnerInfo.params), target, buf.srcSize); better = 1; if(compareResultLT(bestFeasible1.result, winnerInfo.result, target, buf.srcSize)) { bestFeasible1 = winnerInfo; } + break; } } @@ -1576,7 +2058,7 @@ static winnerInfo_t climbOnce(const constraint_t target, feas = 1; better = 1; winnerInfo = bestFeasible1; /* note with change, bestFeasible may not necessarily be feasible, but if one has been benchmarked, it will be. */ - DISPLAY("Climb Part 2\n"); + DEBUGOUTPUT("Climb Part 2\n"); } } winnerInfo = bestFeasible1; @@ -1595,228 +2077,40 @@ static winnerInfo_t climbOnce(const constraint_t target, */ static winnerInfo_t optimizeFixedStrategy( const buffers_t buf, const contexts_t ctx, - const constraint_t target, ZSTD_compressionParameters paramTarget, + const constraint_t target, paramValues_t paramTarget, const ZSTD_strategy strat, const varInds_t* varArray, const int varLen, - U8* const memoTable, const int tries) { + U8** memoTableArray, const int tries) { int i = 0; varInds_t varNew[NUM_PARAMS]; int varLenNew = sanitizeVarArray(varNew, varLen, varArray, strat); - ZSTD_compressionParameters init; + + paramValues_t init; winnerInfo_t winnerInfo, candidateInfo; winnerInfo = initWinnerInfo(emptyParams()); /* so climb is given the right fixed strategy */ - paramTarget.strategy = strat; + paramTarget.vals[strt_ind] = strat; /* to pass ZSTD_checkCParams */ - cParamZeroMin(¶mTarget); + paramTarget = cParamUnsetMin(paramTarget); init = paramTarget; while(i < tries) { DEBUGOUTPUT("Restart\n"); - randomConstrainedParams(&init, varNew, varLenNew, memoTable); - candidateInfo = climbOnce(target, varNew, varLenNew, memoTable, buf, ctx, init); + randomConstrainedParams(&init, varNew, varLenNew, memoTableArray[strat]); + candidateInfo = climbOnce(target, varArray, varLen, strat, memoTableArray, buf, ctx, init); if(compareResultLT(winnerInfo.result, candidateInfo.result, target, buf.srcSize)) { winnerInfo = candidateInfo; BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winnerInfo.result, winnerInfo.params, target, buf.srcSize); i = 0; } + CHECKTIME(winnerInfo); i++; } return winnerInfo; } -static void freeBuffers(const buffers_t b) { - if(b.srcPtrs != NULL) { - free(b.srcBuffer); - } - free(b.srcPtrs); - free(b.srcSizes); - - if(b.dstPtrs != NULL) { - free(b.dstPtrs[0]); - } - free(b.dstPtrs); - free(b.dstCapacities); - free(b.dstSizes); - - if(b.resPtrs != NULL) { - free(b.resPtrs[0]); - } - free(b.resPtrs); -} - -/* allocates buffer's arguments. returns 0 = success / 1 = failuere */ -static int createBuffers(buffers_t* const buff, const char* const * const fileNamesTable, - const size_t nbFiles) -{ - size_t pos = 0; - size_t n; - U64 const totalSizeToLoad = UTIL_getTotalFileSize(fileNamesTable, (U32)nbFiles); - size_t benchedSize = MIN(BMK_findMaxMem(totalSizeToLoad * 3) / 3, totalSizeToLoad); - const size_t blockSize = g_blockSize ? g_blockSize : totalSizeToLoad; //(largest fileSize or total fileSize) - U32 const maxNbBlocks = (U32) ((totalSizeToLoad + (blockSize-1)) / blockSize) + (U32)nbFiles; - U32 blockNb = 0; - - memset(buff, 0, sizeof(buffers_t)); - - buff->srcPtrs = (const void**)calloc(maxNbBlocks, sizeof(void*)); - buff->srcSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); - - buff->dstPtrs = (void**)calloc(maxNbBlocks, sizeof(void*)); - buff->dstCapacities = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); - buff->dstSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); - - buff->resPtrs = (void**)calloc(maxNbBlocks, sizeof(void*)); - buff->resSizes = (size_t*)malloc(maxNbBlocks * sizeof(size_t)); - - if(!buff->srcPtrs || !buff->srcSizes || !buff->dstPtrs || !buff->dstCapacities || !buff->dstSizes || !buff->resPtrs || !buff->resSizes) { - DISPLAY("alloc error\n"); - freeBuffers(*buff); - return 1; - } - - buff->srcBuffer = malloc(benchedSize); - buff->srcPtrs[0] = (const void*)buff->srcBuffer; - buff->dstPtrs[0] = malloc(ZSTD_compressBound(benchedSize) + (maxNbBlocks * 1024)); - buff->resPtrs[0] = malloc(benchedSize); - - if(!buff->srcPtrs[0] || !buff->dstPtrs[0] || !buff->resPtrs[0]) { - DISPLAY("alloc error\n"); - freeBuffers(*buff); - return 1; - } - - for(n = 0; n < nbFiles; n++) { - FILE* f; - U64 fileSize = UTIL_getFileSize(fileNamesTable[n]); - if (UTIL_isDirectory(fileNamesTable[n])) { - DISPLAY("Ignoring %s directory... \n", fileNamesTable[n]); - continue; - } - if (fileSize == UTIL_FILESIZE_UNKNOWN) { - DISPLAY("Cannot evaluate size of %s, ignoring ... \n", fileNamesTable[n]); - continue; - } - f = fopen(fileNamesTable[n], "rb"); - if (f==NULL) { - DISPLAY("impossible to open file %s\n", fileNamesTable[n]); - freeBuffers(*buff); - fclose(f); - return 10; - } - - DISPLAY("Loading %s... \r", fileNamesTable[n]); - - if (fileSize + pos > benchedSize) fileSize = benchedSize - pos, n = nbFiles; /* buffer too small - stop after this file */ - { - char* buffer = (char*)(buff->srcBuffer); - size_t const readSize = fread(((buffer)+pos), 1, (size_t)fileSize, f); - size_t blocked = 0; - while(blocked < readSize) { - buff->srcPtrs[blockNb] = (const void*)((buffer) + (pos + blocked)); - buff->srcSizes[blockNb] = blockSize; - blocked += blockSize; - blockNb++; - } - if(readSize > 0) { buff->srcSizes[blockNb - 1] = ((readSize - 1) % blockSize) + 1; } - - if (readSize != (size_t)fileSize) { - DISPLAY("could not read %s", fileNamesTable[n]); - freeBuffers(*buff); - fclose(f); - return 1; - } - - pos += readSize; - - } - fclose(f); - } - - buff->dstCapacities[0] = ZSTD_compressBound(buff->srcSizes[0]); - buff->dstSizes[0] = buff->dstCapacities[0]; - buff->resSizes[0] = buff->srcSizes[0]; - - for(n = 1; n < blockNb; n++) { - buff->dstPtrs[n] = ((char*)buff->dstPtrs[n-1]) + buff->dstCapacities[n-1]; - buff->resPtrs[n] = ((char*)buff->resPtrs[n-1]) + buff->resSizes[n-1]; - buff->dstCapacities[n] = ZSTD_compressBound(buff->srcSizes[n]); - buff->dstSizes[n] = buff->dstCapacities[n]; - buff->resSizes[n] = buff->srcSizes[n]; - } - buff->srcSize = pos; - buff->nbBlocks = blockNb; - - if (pos == 0) { DISPLAY("\nno data to bench\n"); return 1; } - - return 0; -} - -static void freeContexts(const contexts_t ctx) { - free(ctx.dictBuffer); - ZSTD_freeCCtx(ctx.cctx); - ZSTD_freeDCtx(ctx.dctx); -} - -/* Creates struct holding contexts and dictionary buffers. returns 0 on success, 1 on failure. */ -static int createContexts(contexts_t* const ctx, const char* dictFileName) { - FILE* f; - size_t readSize; - U64 dictSize; - ctx->cctx = ZSTD_createCCtx(); - ctx->dctx = ZSTD_createDCtx(); - ctx->dictSize = 0; - ctx->dictBuffer = NULL; - - if(!ctx->cctx || !ctx->dctx) { - DISPLAY("context allocation error\n"); - freeContexts(*ctx); - return 1; - } - - if(dictFileName == NULL) { - return 0; - } - - dictSize = UTIL_getFileSize(dictFileName); - - if(dictSize == UTIL_FILESIZE_UNKNOWN) { - DISPLAY("Unable to get dictionary size\n"); - freeContexts(*ctx); - return 1; - } else { - ctx->dictSize = (size_t)dictSize; - } - - ctx->dictBuffer = malloc(ctx->dictSize); - - f = fopen(dictFileName, "rb"); - - if(!f) { - DISPLAY("unable to open file\n"); - fclose(f); - freeContexts(*ctx); - return 1; - } - - if(ctx->dictSize > 64 MB || !(ctx->dictBuffer)) { - DISPLAY("dictionary too large\n"); - fclose(f); - freeContexts(*ctx); - return 1; - } - readSize = fread(ctx->dictBuffer, 1, ctx->dictSize, f); - if(readSize != ctx->dictSize) { - DISPLAY("unable to read file\n"); - fclose(f); - freeContexts(*ctx); - return 1; - } - return 0; -} - /* goes best, best-1, best+1, best-2, ... */ /* return 0 if nothing remaining */ static int nextStrategy(const int currentStrategy, const int bestStrategy) { @@ -1847,18 +2141,8 @@ static int nextStrategy(const int currentStrategy, const int bestStrategy) { } } -static ZSTD_compressionParameters maskParams(ZSTD_compressionParameters base, ZSTD_compressionParameters mask) { - base.windowLog = mask.windowLog ? mask.windowLog : base.windowLog; - base.chainLog = mask.chainLog ? mask.chainLog : base.chainLog; - base.hashLog = mask.hashLog ? mask.hashLog : base.hashLog; - base.searchLog = mask.searchLog ? mask.searchLog : base.searchLog; - base.searchLength = mask.searchLength ? mask.searchLength : base.searchLength; - base.targetLength = mask.targetLength ? mask.targetLength : base.targetLength; - base.strategy = mask.strategy ? mask.strategy : base.strategy; - return base; -} +/* experiment with playing with this and decay value */ -#define MAX_TRIES 8 /* main fn called when using --optimize */ /* Does strategy selection by benchmarking default compression levels * then optimizes by strategy, starting with the best one and moving @@ -1871,24 +2155,27 @@ static ZSTD_compressionParameters maskParams(ZSTD_compressionParameters base, ZS * paramTarget - parameter constraints (i.e. restriction search space to where strategy = ZSTD_fast) * cLevel - compression level to exceed (all solutions must be > lvl in cSpeed + ratio) */ -static int optimizeForSize(const char* const * const fileNamesTable, const size_t nbFiles, const char* dictFileName, constraint_t target, ZSTD_compressionParameters paramTarget, int cLevel) + +static int g_maxTries = 3; +#define TRY_DECAY 1 + +static int optimizeForSize(const char* const * const fileNamesTable, const size_t nbFiles, const char* dictFileName, constraint_t target, paramValues_t paramTarget, int cLevelOpt, int cLevelRun) { varInds_t varArray [NUM_PARAMS]; int ret = 0; - const int varLen = variableParams(paramTarget, varArray); + const int varLen = variableParams(paramTarget, varArray, dictFileName != NULL); winnerInfo_t winner = initWinnerInfo(emptyParams()); U8** allMT = NULL; - size_t k; - size_t maxBlockSize = 0; + paramValues_t paramBase = cParamUnsetMin(paramTarget); + size_t k, maxBlockSize = 0; contexts_t ctx; buffers_t buf; + g_time = UTIL_getTime(); - /* Init */ - if(!cParamValid(paramTarget)) { + if(!paramValid(paramBase)) { return 1; } - /* load dictionary*/ if(createBuffers(&buf, fileNamesTable, nbFiles)) { DISPLAY("unable to load files\n"); return 1; @@ -1912,43 +2199,80 @@ static int optimizeForSize(const char* const * const fileNamesTable, const size_ } /* if strategy is fixed, only init that part of memotable */ - if(paramTarget.strategy) { + if(paramTarget.vals[strt_ind] != PARAM_UNSET) { varInds_t varNew[NUM_PARAMS]; - int varLenNew = sanitizeVarArray(varNew, varLen, varArray, paramTarget.strategy); - allMT = calloc(sizeof(U8), (ZSTD_btultra + 1)); + int varLenNew = sanitizeVarArray(varNew, varLen, varArray, paramTarget.vals[strt_ind]); + allMT = (U8**)calloc(sizeof(U8*), (ZSTD_btultra + 1)); if(allMT == NULL) { ret = 57; goto _cleanUp; } - allMT[paramTarget.strategy] = malloc(sizeof(U8) * memoTableLen(varNew, varLenNew)); + allMT[paramTarget.vals[strt_ind]] = malloc(sizeof(U8) * memoTableLen(varNew, varLenNew)); - if(allMT[paramTarget.strategy] == NULL) { + if(allMT[paramTarget.vals[strt_ind]] == NULL) { ret = 58; goto _cleanUp; } - initMemoTable(allMT[paramTarget.strategy], paramTarget, target, varNew, varLenNew, maxBlockSize); + initMemoTable(allMT[paramTarget.vals[strt_ind]], paramTarget, target, varNew, varLenNew, maxBlockSize); } else { allMT = createMemoTableArray(paramTarget, target, varArray, varLen, maxBlockSize); } - if(!allMT) { DISPLAY("MemoTable Init Error\n"); ret = 2; goto _cleanUp; } - - if(cLevel) { - winner.params = ZSTD_getCParams(cLevel, maxBlockSize, ctx.dictSize); + + /* default strictness = Maximum for */ + if(g_strictness == DEFAULT_STRICTNESS) { + if(g_optmode) { + g_strictness = 99; + } else { + g_strictness = 90; + } + } else { + if(0 >= g_strictness || g_strictness > 100) { + DISPLAY("Strictness Outside of Bounds\n"); + ret = 4; + goto _cleanUp; + } + } + + /* use level'ing mode instead of normal target mode */ + /* Should lvl be parameter-masked here? */ + if(g_optmode) { + winner.params = cParamsToPVals(ZSTD_getCParams(cLevelOpt, maxBlockSize, ctx.dictSize)); if(BMK_benchParam(&winner.result, buf, ctx, winner.params)) { ret = 3; goto _cleanUp; } + + g_lvltarget = winner.result; + g_lvltarget.cSpeed *= ((double)g_strictness) / 100; + g_lvltarget.dSpeed *= ((double)g_strictness) / 100; + g_lvltarget.cSize /= ((double)g_strictness) / 100; - target.cSpeed = (U32)winner.result.cSpeed; - BMK_printWinnerOpt(stdout, cLevel, winner.result, winner.params, target, buf.srcSize); + target.cSpeed = (U32)g_lvltarget.cSpeed; + target.dSpeed = (U32)g_lvltarget.dSpeed; //See if this is reasonable. + + BMK_printWinnerOpt(stdout, cLevelOpt, winner.result, winner.params, target, buf.srcSize); + } + + /* Don't want it to return anything worse than the best known result */ + if(g_singleRun) { + BMK_result_t res; + g_params = adjustParams(overwriteParams(cParamsToPVals(ZSTD_getCParams(cLevelRun, maxBlockSize, ctx.dictSize)), g_params), maxBlockSize, ctx.dictSize); + if(BMK_benchParam(&res, buf, ctx, g_params)) { + ret = 45; + goto _cleanUp; + } + if(compareResultLT(winner.result, res, relaxTarget(target), buf.srcSize)) { + winner.result = res; + winner.params = g_params; + } } /* bench */ @@ -1958,6 +2282,7 @@ static int optimizeForSize(const char* const * const fileNamesTable, const size_ } else { DISPLAY("optimizing for %lu Files", (unsigned long)nbFiles); } + if(target.cSpeed != 0) { DISPLAY(" - limit compression speed %u MB/s", target.cSpeed >> 20); } if(target.dSpeed != 0) { DISPLAY(" - limit decompression speed %u MB/s", target.dSpeed >> 20); } if(target.cMem != (U32)-1) { DISPLAY(" - limit memory %u MB", target.cMem >> 20); } @@ -1966,20 +2291,19 @@ static int optimizeForSize(const char* const * const fileNamesTable, const size_ findClockGranularity(); { - varInds_t varNew[NUM_PARAMS]; + paramValues_t CParams; /* find best solution from default params */ { /* strategy selection */ const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel(); DEBUGOUTPUT("Strategy Selection\n"); - if(paramTarget.strategy == 0) { /* no variable based constraints */ + if(paramTarget.vals[strt_ind] == PARAM_UNSET) { BMK_result_t candidate; int i; for (i=1; i<=maxSeeds; i++) { int ec; - ZSTD_compressionParameters CParams = ZSTD_getCParams(i, maxBlockSize, ctx.dictSize); - CParams = maskParams(CParams, paramTarget); + CParams = overwriteParams(cParamsToPVals(ZSTD_getCParams(i, maxBlockSize, ctx.dictSize)), paramTarget); ec = BMK_benchParam(&candidate, buf, ctx, CParams); BMK_printWinnerOpt(stdout, i, candidate, CParams, target, buf.srcSize); @@ -1987,43 +2311,55 @@ static int optimizeForSize(const char* const * const fileNamesTable, const size_ winner.result = candidate; winner.params = CParams; } + + CHECKTIMEGT(ret, 0, _cleanUp); /* if pass time limit, stop */ + /* if the current params are too slow, just stop. */ + if(target.cSpeed > candidate.cSpeed * 3 / 2) { break; } } + + BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winner.result, winner.params, target, buf.srcSize); + BMK_translateAdvancedParams(stdout, winner.params); } } - BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winner.result, winner.params, target, buf.srcSize); - BMK_translateAdvancedParams(winner.params); DEBUGOUTPUT("Real Opt\n"); /* start 'real' tests */ { - int bestStrategy = (int)winner.params.strategy; - if(paramTarget.strategy == 0) { - int st = (int)winner.params.strategy; - int tries = MAX_TRIES; + int bestStrategy = (int)winner.params.vals[strt_ind]; + if(paramTarget.vals[strt_ind] == PARAM_UNSET) { + int st = bestStrategy; + int tries = g_maxTries; { - int varLenNew = sanitizeVarArray(varNew, varLen, varArray, st); - winnerInfo_t w1 = climbOnce(target, varNew, varLenNew, allMT[st], + /* one iterations of hill climbing with the level-defined parameters. */ + winnerInfo_t w1 = climbOnce(target, varArray, varLen, st, allMT, buf, ctx, winner.params); if(compareResultLT(winner.result, w1.result, target, buf.srcSize)) { winner = w1; } + CHECKTIMEGT(ret, 0, _cleanUp); } - while(st && tries) { - winnerInfo_t wc = optimizeFixedStrategy(buf, ctx, target, paramTarget, - st, varArray, varLen, allMT[st], tries); - DEBUGOUTPUT("StratNum %d\n", st); + while(st && tries > 0) { + winnerInfo_t wc; + DEBUGOUTPUT("StrategySwitch: %s\n", g_stratName[st]); + + wc = optimizeFixedStrategy(buf, ctx, target, paramBase, + st, varArray, varLen, allMT, tries); + if(compareResultLT(winner.result, wc.result, target, buf.srcSize)) { winner = wc; + tries = g_maxTries; + bestStrategy = st; + } else { + st = nextStrategy(st, bestStrategy); + tries -= TRY_DECAY; } - - st = nextStrategy(st, bestStrategy); - tries--; + CHECKTIMEGT(ret, 0, _cleanUp); } } else { - winner = optimizeFixedStrategy(buf, ctx, target, paramTarget, paramTarget.strategy, - varArray, varLen, allMT[paramTarget.strategy], 10); + winner = optimizeFixedStrategy(buf, ctx, target, paramBase, paramTarget.vals[strt_ind], + varArray, varLen, allMT, g_maxTries); } } @@ -2036,7 +2372,7 @@ static int optimizeForSize(const char* const * const fileNamesTable, const size_ } /* end summary */ BMK_printWinnerOpt(stdout, CUSTOM_LEVEL, winner.result, winner.params, target, buf.srcSize); - BMK_translateAdvancedParams(winner.params); + BMK_translateAdvancedParams(stdout, winner.params); DISPLAY("grillParams size - optimizer completed \n"); } @@ -2060,7 +2396,9 @@ static void errorOut(const char* msg) static unsigned readU32FromChar(const char** stringPtr) { const char errorMsg[] = "error: numeric value too large"; + unsigned sign = 1; unsigned result = 0; + if(**stringPtr == '-') { sign = (unsigned)-1; (*stringPtr)++; } while ((**stringPtr >='0') && (**stringPtr <='9')) { unsigned const max = (((unsigned)(-1)) / 10) - 1; if (result > max) errorOut(errorMsg); @@ -2078,7 +2416,7 @@ static unsigned readU32FromChar(const char** stringPtr) if (**stringPtr=='i') (*stringPtr)++; if (**stringPtr=='B') (*stringPtr)++; } - return result; + return result * sign; } static int usage(const char* exename) @@ -2101,9 +2439,10 @@ static int usage_advanced(void) DISPLAY( " -S : Single run \n"); DISPLAY( " --zstd : Single run, parameter selection same as zstdcli \n"); DISPLAY( " -P# : generated sample compressibility (default : %.1f%%) \n", COMPRESSIBILITY_DEFAULT * 100); - DISPLAY( " -t# : Caps runtime of operation in seconds (default : %u seconds (%.1f hours)) \n", (U32)g_grillDuration_s, g_grillDuration_s / 3600); + DISPLAY( " -t# : Caps runtime of operation in seconds (default : %u seconds (%.1f hours)) \n", g_timeLimit_s, (double)g_timeLimit_s / 3600); DISPLAY( " -v : Prints Benchmarking output\n"); DISPLAY( " -D : Next argument dictionary file\n"); + DISPLAY( " -s : Seperate Files\n"); return 0; } @@ -2115,15 +2454,16 @@ static int badusage(const char* exename) } #define PARSE_SUB_ARGS(stringLong, stringShort, variable) { if (longCommandWArg(&argument, stringLong) || longCommandWArg(&argument, stringShort)) { variable = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; } } -#define PARSE_CPARAMS(variable) \ -{ \ - PARSE_SUB_ARGS("windowLog=", "wlog=", variable.windowLog); \ - PARSE_SUB_ARGS("chainLog=" , "clog=", variable.chainLog); \ - PARSE_SUB_ARGS("hashLog=", "hlog=", variable.hashLog); \ - PARSE_SUB_ARGS("searchLog=" , "slog=", variable.searchLog); \ - PARSE_SUB_ARGS("searchLength=", "slen=", variable.searchLength); \ - PARSE_SUB_ARGS("targetLength=" , "tlen=", variable.targetLength); \ - PARSE_SUB_ARGS("strategy=", "strat=", variable.strategy); \ +#define PARSE_CPARAMS(variable) \ +{ \ + PARSE_SUB_ARGS("windowLog=", "wlog=", variable.vals[wlog_ind]); \ + PARSE_SUB_ARGS("chainLog=" , "clog=", variable.vals[clog_ind]); \ + PARSE_SUB_ARGS("hashLog=", "hlog=", variable.vals[hlog_ind]); \ + PARSE_SUB_ARGS("searchLog=" , "slog=", variable.vals[slog_ind]); \ + PARSE_SUB_ARGS("searchLength=", "slen=", variable.vals[slen_ind]); \ + PARSE_SUB_ARGS("targetLength=" , "tlen=", variable.vals[tlen_ind]); \ + PARSE_SUB_ARGS("strategy=", "strat=", variable.vals[strt_ind]); \ + PARSE_SUB_ARGS("forceAttachDict=", "fad=" , variable.vals[fadt_ind]); \ } int main(int argc, const char** argv) @@ -2134,38 +2474,39 @@ int main(int argc, const char** argv) const char* exename=argv[0]; const char* input_filename = NULL; const char* dictFileName = NULL; - U32 optimizer = 0; U32 main_pause = 0; - int optimizerCLevel = 0; - - + int cLevelOpt = 0, cLevelRun = 0; + int seperateFiles = 0; constraint_t target = { 0, 0, (U32)-1 }; - ZSTD_compressionParameters paramTarget = { 0, 0, 0, 0, 0, 0, 0 }; + + paramValues_t paramTarget = emptyParams(); + g_params = emptyParams(); assert(argc>=1); /* for exename */ - g_time = UTIL_getTime(); - /* Welcome message */ DISPLAY(WELCOME_MESSAGE); for(i=1; i