cffb6da339
Additional constraint checking Minor fixes more param parsing Add Memory Change paramVariation work on feasibility reformat bench Changed Paramgrill to use bench.c benchmarking customlevel macro Printing Flag Minor changes Explicit casting Makefile fix casting, type fix Printing Flag Minor Changes comments, helper fn's
1267 lines
49 KiB
C
1267 lines
49 KiB
C
/*
|
|
* Copyright (c) 2015-present, Yann Collet, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under both the BSD-style license (found in the
|
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
|
* in the COPYING file in the root directory of this source tree).
|
|
* You may select, at your option, one of the above-listed licenses.
|
|
*/
|
|
|
|
|
|
/*-************************************
|
|
* Dependencies
|
|
**************************************/
|
|
#include "util.h" /* Compiler options, UTIL_GetFileSize */
|
|
#include <stdlib.h> /* malloc */
|
|
#include <stdio.h> /* fprintf, fopen, ftello64 */
|
|
#include <string.h> /* strcmp */
|
|
#include <math.h> /* log */
|
|
#include <time.h>
|
|
#include <assert.h>
|
|
|
|
#include "mem.h"
|
|
#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_estimateCCtxSize */
|
|
#include "zstd.h"
|
|
#include "datagen.h"
|
|
#include "xxhash.h"
|
|
#include "util.h"
|
|
#include "bench.h"
|
|
|
|
|
|
/*-************************************
|
|
* Constants
|
|
**************************************/
|
|
#define PROGRAM_DESCRIPTION "ZSTD parameters tester"
|
|
#define AUTHOR "Yann Collet"
|
|
#define WELCOME_MESSAGE "*** %s %s %i-bits, by %s ***\n", PROGRAM_DESCRIPTION, ZSTD_VERSION_STRING, (int)(sizeof(void*)*8), AUTHOR
|
|
|
|
|
|
#define KB *(1<<10)
|
|
#define MB *(1<<20)
|
|
#define GB *(1ULL<<30)
|
|
|
|
#define NBLOOPS 2
|
|
#define TIMELOOP (2 * SEC_TO_MICRO)
|
|
#define NB_LEVELS_TRACKED 22 /* ensured being >= ZSTD_maxCLevel() in BMK_init_level_constraints() */
|
|
|
|
static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t)(1ULL << ((sizeof(size_t)*8)-31));
|
|
|
|
#define COMPRESSIBILITY_DEFAULT 0.50
|
|
|
|
static const U64 g_maxVariationTime = 60 * SEC_TO_MICRO;
|
|
static const int g_maxNbVariations = 64;
|
|
|
|
/*-************************************
|
|
* Macros
|
|
**************************************/
|
|
#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
|
|
|
|
#undef MIN
|
|
#undef MAX
|
|
#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
|
|
#define MAX(a,b) ( (a) > (b) ? (a) : (b) )
|
|
#define CUSTOM_LEVEL 99
|
|
|
|
/* indices for each of the variables */
|
|
#define WLOG_IND 0
|
|
#define CLOG_IND 1
|
|
#define HLOG_IND 2
|
|
#define SLOG_IND 3
|
|
#define SLEN_IND 4
|
|
#define TLEN_IND 5
|
|
#define STRT_IND 6
|
|
#define NUM_PARAMS 7
|
|
|
|
/*-************************************
|
|
* Benchmark Parameters
|
|
**************************************/
|
|
|
|
static double g_grillDuration_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_target = 0;
|
|
static U32 g_noSeed = 0;
|
|
static ZSTD_compressionParameters g_params = { 0, 0, 0, 0, 0, 0, ZSTD_greedy };
|
|
|
|
void BMK_SetNbIterations(int nbLoops)
|
|
{
|
|
g_nbIterations = nbLoops;
|
|
DISPLAY("- %u iterations -\n", g_nbIterations);
|
|
}
|
|
|
|
/*-*******************************************************
|
|
* Private functions
|
|
*********************************************************/
|
|
|
|
/* accuracy in seconds only, span can be multiple years */
|
|
static double BMK_timeSpan(time_t tStart) { return difftime(time(NULL), tStart); }
|
|
|
|
static size_t BMK_findMaxMem(U64 requiredMem)
|
|
{
|
|
size_t const step = 64 MB;
|
|
void* testmem = NULL;
|
|
|
|
requiredMem = (((requiredMem >> 26) + 1) << 26);
|
|
if (requiredMem > maxMemory) requiredMem = maxMemory;
|
|
|
|
requiredMem += 2*step;
|
|
while (!testmem) {
|
|
requiredMem -= step;
|
|
testmem = malloc ((size_t)requiredMem);
|
|
}
|
|
|
|
free (testmem);
|
|
return (size_t) (requiredMem - step);
|
|
}
|
|
|
|
|
|
static U32 FUZ_rotl32(U32 x, U32 r)
|
|
{
|
|
return ((x << r) | (x >> (32 - r)));
|
|
}
|
|
|
|
U32 FUZ_rand(U32* src)
|
|
{
|
|
const U32 prime1 = 2654435761U;
|
|
const U32 prime2 = 2246822519U;
|
|
U32 rand32 = *src;
|
|
rand32 *= prime1;
|
|
rand32 += prime2;
|
|
rand32 = FUZ_rotl32(rand32, 13);
|
|
*src = rand32;
|
|
return rand32 >> 5;
|
|
}
|
|
|
|
/** longCommandWArg() :
|
|
* check if *stringPtr is the same as longCommand.
|
|
* If yes, @return 1 and advances *stringPtr to the position which immediately follows longCommand.
|
|
* @return 0 and doesn't modify *stringPtr otherwise.
|
|
* from zstdcli.c
|
|
*/
|
|
static unsigned longCommandWArg(const char** stringPtr, const char* longCommand)
|
|
{
|
|
size_t const comSize = strlen(longCommand);
|
|
int const result = !strncmp(*stringPtr, longCommand, comSize);
|
|
if (result) *stringPtr += comSize;
|
|
return result;
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
U32 cSpeed; /* bytes / sec */
|
|
U32 dSpeed;
|
|
U32 Mem; /* bytes */
|
|
} constraint_t;
|
|
|
|
/*-*******************************************************
|
|
* Bench functions
|
|
*********************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
const char* srcPtr;
|
|
size_t srcSize;
|
|
char* cPtr;
|
|
size_t cRoom;
|
|
size_t cSize;
|
|
char* resPtr;
|
|
size_t resSize;
|
|
} blockParam_t;
|
|
|
|
|
|
const char* g_stratName[ZSTD_btultra+1] = {
|
|
"(none) ", "ZSTD_fast ", "ZSTD_dfast ",
|
|
"ZSTD_greedy ", "ZSTD_lazy ", "ZSTD_lazy2 ",
|
|
"ZSTD_btlazy2 ", "ZSTD_btopt ", "ZSTD_btultra "};
|
|
|
|
/* TODO: support additional parameters (more files, fileSizes) */
|
|
static size_t
|
|
BMK_benchParam(BMK_result_t* resultPtr,
|
|
const void* srcBuffer, size_t srcSize,
|
|
ZSTD_CCtx* ctx, ZSTD_DCtx* dctx,
|
|
const ZSTD_compressionParameters cParams) {
|
|
|
|
BMK_return_t res = BMK_benchMem(srcBuffer,srcSize, &srcSize, 1, 0, &cParams, NULL, 0, ctx, dctx, 0, "File");
|
|
*resultPtr = res.result;
|
|
return res.error;
|
|
}
|
|
|
|
static void BMK_printWinner(FILE* f, U32 cLevel, BMK_result_t result, ZSTD_compressionParameters params, size_t srcSize)
|
|
{
|
|
char lvlstr[15] = "Custom Level";
|
|
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)]);
|
|
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 */\n",
|
|
lvlstr, (double)srcSize / result.cSize, result.cSpeed / 1000000., result.dSpeed / 1000000.);
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
BMK_result_t result;
|
|
ZSTD_compressionParameters params;
|
|
} winnerInfo_t;
|
|
|
|
static void BMK_printWinners2(FILE* f, const winnerInfo_t* winners, size_t srcSize)
|
|
{
|
|
int cLevel;
|
|
|
|
fprintf(f, "\n /* Proposed configurations : */ \n");
|
|
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);
|
|
}
|
|
|
|
|
|
static void BMK_printWinners(FILE* f, const winnerInfo_t* winners, size_t srcSize)
|
|
{
|
|
fseek(f, 0, SEEK_SET);
|
|
BMK_printWinners2(f, winners, srcSize);
|
|
fflush(f);
|
|
BMK_printWinners2(stdout, winners, srcSize);
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
double cSpeed_min;
|
|
double dSpeed_min;
|
|
U32 windowLog_max;
|
|
ZSTD_strategy strategy_max;
|
|
} level_constraints_t;
|
|
|
|
static level_constraints_t g_level_constraint[NB_LEVELS_TRACKED+1];
|
|
|
|
static void BMK_init_level_constraints(int bytePerSec_level1)
|
|
{
|
|
assert(NB_LEVELS_TRACKED >= ZSTD_maxCLevel());
|
|
memset(g_level_constraint, 0, sizeof(g_level_constraint));
|
|
g_level_constraint[1].cSpeed_min = bytePerSec_level1;
|
|
g_level_constraint[1].dSpeed_min = 0.;
|
|
g_level_constraint[1].windowLog_max = 19;
|
|
g_level_constraint[1].strategy_max = ZSTD_fast;
|
|
|
|
/* establish speed objectives (relative to level 1) */
|
|
{ int l;
|
|
for (l=2; l<=NB_LEVELS_TRACKED; l++) {
|
|
g_level_constraint[l].cSpeed_min = (g_level_constraint[l-1].cSpeed_min * 49) / 64;
|
|
g_level_constraint[l].dSpeed_min = 0.;
|
|
g_level_constraint[l].windowLog_max = (l<20) ? 23 : l+5; /* only --ultra levels >= 20 can use windowlog > 23 */
|
|
g_level_constraint[l].strategy_max = (l<19) ? ZSTD_btopt : ZSTD_btultra; /* level 19 is allowed to use btultra */
|
|
} }
|
|
}
|
|
|
|
static int BMK_seed(winnerInfo_t* winners, const ZSTD_compressionParameters params,
|
|
const void* srcBuffer, size_t srcSize,
|
|
ZSTD_CCtx* ctx, ZSTD_DCtx* dctx)
|
|
{
|
|
BMK_result_t testResult;
|
|
int better = 0;
|
|
int cLevel;
|
|
|
|
BMK_benchParam(&testResult, srcBuffer, srcSize, ctx, dctx, params);
|
|
|
|
|
|
for (cLevel = 1; cLevel <= NB_LEVELS_TRACKED; cLevel++) {
|
|
if (testResult.cSpeed < g_level_constraint[cLevel].cSpeed_min)
|
|
continue; /* not fast enough for this level */
|
|
if (testResult.dSpeed < g_level_constraint[cLevel].dSpeed_min)
|
|
continue; /* not fast enough for this level */
|
|
if (params.windowLog > g_level_constraint[cLevel].windowLog_max)
|
|
continue; /* too much memory for this level */
|
|
if (params.strategy > g_level_constraint[cLevel].strategy_max)
|
|
continue; /* forbidden strategy for this level */
|
|
if (winners[cLevel].result.cSize==0) {
|
|
/* first solution for this cLevel */
|
|
winners[cLevel].result = testResult;
|
|
winners[cLevel].params = params;
|
|
BMK_printWinner(stdout, cLevel, testResult, params, 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_ratioNote = log (W_ratio);
|
|
double O_ratioNote = log (O_ratio);
|
|
size_t W_DMemUsed = (1 << params.windowLog) + (16 KB);
|
|
size_t O_DMemUsed = (1 << winners[cLevel].params.windowLog) + (16 KB);
|
|
double W_DMemUsed_note = W_ratioNote * ( 40 + 9*cLevel) - log((double)W_DMemUsed);
|
|
double O_DMemUsed_note = O_ratioNote * ( 40 + 9*cLevel) - log((double)O_DMemUsed);
|
|
|
|
size_t W_CMemUsed = (1 << params.windowLog) + ZSTD_estimateCCtxSize_usingCParams(params);
|
|
size_t O_CMemUsed = (1 << winners[cLevel].params.windowLog) + ZSTD_estimateCCtxSize_usingCParams(winners[cLevel].params);
|
|
double W_CMemUsed_note = W_ratioNote * ( 50 + 13*cLevel) - log((double)W_CMemUsed);
|
|
double O_CMemUsed_note = O_ratioNote * ( 50 + 13*cLevel) - log((double)O_CMemUsed);
|
|
|
|
double W_CSpeed_note = W_ratioNote * ( 30 + 10*cLevel) + log(testResult.cSpeed);
|
|
double O_CSpeed_note = O_ratioNote * ( 30 + 10*cLevel) + log(winners[cLevel].result.cSpeed);
|
|
|
|
double W_DSpeed_note = W_ratioNote * ( 20 + 2*cLevel) + log(testResult.dSpeed);
|
|
double O_DSpeed_note = O_ratioNote * ( 20 + 2*cLevel) + log(winners[cLevel].result.dSpeed);
|
|
|
|
if (W_DMemUsed_note < O_DMemUsed_note) {
|
|
/* uses too much Decompression memory for too little benefit */
|
|
if (W_ratio > O_ratio)
|
|
DISPLAY ("Decompression Memory : %5.3f @ %4.1f MB vs %5.3f @ %4.1f MB : not enough for level %i\n",
|
|
W_ratio, (double)(W_DMemUsed) / 1024 / 1024,
|
|
O_ratio, (double)(O_DMemUsed) / 1024 / 1024, cLevel);
|
|
continue;
|
|
}
|
|
if (W_CMemUsed_note < O_CMemUsed_note) {
|
|
/* uses too much memory for compression for too little benefit */
|
|
if (W_ratio > O_ratio)
|
|
DISPLAY ("Compression Memory : %5.3f @ %4.1f MB vs %5.3f @ %4.1f MB : not enough for level %i\n",
|
|
W_ratio, (double)(W_CMemUsed) / 1024 / 1024,
|
|
O_ratio, (double)(O_CMemUsed) / 1024 / 1024, cLevel);
|
|
continue;
|
|
}
|
|
if (W_CSpeed_note < O_CSpeed_note ) {
|
|
/* too large compression speed difference for the compression benefit */
|
|
if (W_ratio > O_ratio)
|
|
DISPLAY ("Compression Speed : %5.3f @ %4.1f MB/s vs %5.3f @ %4.1f MB/s : not enough for level %i\n",
|
|
W_ratio, testResult.cSpeed / 1000000,
|
|
O_ratio, winners[cLevel].result.cSpeed / 1000000., cLevel);
|
|
continue;
|
|
}
|
|
if (W_DSpeed_note < O_DSpeed_note ) {
|
|
/* too large decompression speed difference for the compression benefit */
|
|
if (W_ratio > O_ratio)
|
|
DISPLAY ("Decompression Speed : %5.3f @ %4.1f MB/s vs %5.3f @ %4.1f MB/s : not enough for level %i\n",
|
|
W_ratio, testResult.dSpeed / 1000000.,
|
|
O_ratio, winners[cLevel].result.dSpeed / 1000000., cLevel);
|
|
continue;
|
|
}
|
|
|
|
if (W_ratio < O_ratio)
|
|
DISPLAY("Solution %4.3f selected over %4.3f at level %i, due to better secondary statistics \n", W_ratio, O_ratio, cLevel);
|
|
|
|
winners[cLevel].result = testResult;
|
|
winners[cLevel].params = params;
|
|
BMK_printWinner(stdout, cLevel, testResult, params, srcSize);
|
|
|
|
better = 1;
|
|
} }
|
|
|
|
return better;
|
|
}
|
|
|
|
/* nullified useless params, to ensure count stats */
|
|
static ZSTD_compressionParameters* sanitizeParams(ZSTD_compressionParameters params)
|
|
{
|
|
g_params = params;
|
|
if (params.strategy == ZSTD_fast)
|
|
g_params.chainLog = 0, g_params.searchLog = 0;
|
|
if (params.strategy == ZSTD_dfast)
|
|
g_params.searchLog = 0;
|
|
if (params.strategy != ZSTD_btopt && params.strategy != ZSTD_btultra)
|
|
g_params.targetLength = 0;
|
|
return &g_params;
|
|
}
|
|
|
|
/* res should be NUM_PARAMS size */
|
|
static int variableParams(const ZSTD_compressionParameters paramConstraints, U32* res) {
|
|
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++;
|
|
}
|
|
if(!(U32)paramConstraints.strategy) {
|
|
res[j] = STRT_IND;
|
|
j++;
|
|
}
|
|
return j;
|
|
}
|
|
|
|
/* computes inverse of above array, returns same number, -1 = unused ind */
|
|
static int inverseVariableParams(const ZSTD_compressionParameters paramConstraints, U32* res) {
|
|
int j = 0;
|
|
if(!paramConstraints.windowLog) {
|
|
res[WLOG_IND] = j;
|
|
j++;
|
|
} else {
|
|
res[WLOG_IND] = -1;
|
|
}
|
|
if(!paramConstraints.chainLog) {
|
|
res[j] = CLOG_IND;
|
|
j++;
|
|
} else {
|
|
res[WLOG_IND] = -1;
|
|
}
|
|
if(!paramConstraints.hashLog) {
|
|
res[j] = HLOG_IND;
|
|
j++;
|
|
} else {
|
|
res[WLOG_IND] = -1;
|
|
}
|
|
if(!paramConstraints.searchLog) {
|
|
res[j] = SLOG_IND;
|
|
j++;
|
|
} else {
|
|
res[WLOG_IND] = -1;
|
|
}
|
|
if(!paramConstraints.searchLength) {
|
|
res[j] = SLEN_IND;
|
|
j++;
|
|
} else {
|
|
res[WLOG_IND] = -1;
|
|
}
|
|
if(!paramConstraints.targetLength) {
|
|
res[j] = TLEN_IND;
|
|
j++;
|
|
} else {
|
|
res[WLOG_IND] = -1;
|
|
}
|
|
if(!(U32)paramConstraints.strategy) {
|
|
res[j] = STRT_IND;
|
|
j++;
|
|
} else {
|
|
res[WLOG_IND] = -1;
|
|
}
|
|
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?) */
|
|
static void paramVaryOnce(U32 paramIndex, 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:
|
|
if(amt > 0) {
|
|
ptr->targetLength <<= amt;
|
|
} else {
|
|
ptr->targetLength >>= -amt;
|
|
}
|
|
break;
|
|
case STRT_IND: ptr->strategy += amt; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
//Don't fuzz fixed variables.
|
|
//turn pcs to pcs array with macro for params.
|
|
//pass in variation array from variableParams
|
|
static void paramVariation(ZSTD_compressionParameters* ptr, const U32* varyParams, const int varyLen)
|
|
{
|
|
ZSTD_compressionParameters p;
|
|
U32 validated = 0;
|
|
while (!validated) {
|
|
U32 nbChanges = (FUZ_rand(&g_rand) & 3) + 1;
|
|
p = *ptr;
|
|
for ( ; nbChanges ; nbChanges--) {
|
|
const U32 changeID = FUZ_rand(&g_rand) % (2 * varyLen);
|
|
paramVaryOnce(varyParams[changeID >> 1], ((changeID & 1) << 1) - 1, &p);
|
|
}
|
|
validated = !ZSTD_isError(ZSTD_checkCParams(p));
|
|
|
|
//Make sure memory is at least close to feasible?
|
|
//ZSTD_estimateCCtxSize thing.
|
|
}
|
|
*ptr = p;
|
|
}
|
|
|
|
|
|
#define PARAMTABLELOG 25
|
|
#define PARAMTABLESIZE (1<<PARAMTABLELOG)
|
|
#define PARAMTABLEMASK (PARAMTABLESIZE-1)
|
|
static BYTE g_alreadyTested[PARAMTABLESIZE] = {0}; /* init to zero */
|
|
|
|
#define NB_TESTS_PLAYED(p) \
|
|
g_alreadyTested[(XXH64(sanitizeParams(p), sizeof(p), 0) >> 3) & PARAMTABLEMASK]
|
|
|
|
|
|
static void playAround(FILE* f, winnerInfo_t* winners,
|
|
ZSTD_compressionParameters params,
|
|
const void* srcBuffer, size_t srcSize,
|
|
ZSTD_CCtx* ctx, ZSTD_DCtx* dctx)
|
|
{
|
|
int nbVariations = 0;
|
|
UTIL_time_t const clockStart = UTIL_getTime();
|
|
const U32 unconstrained[NUM_PARAMS] = { 0, 1, 2, 3, 4, 5, 6 };
|
|
|
|
|
|
while (UTIL_clockSpanMicro(clockStart) < g_maxVariationTime) {
|
|
ZSTD_compressionParameters p = params;
|
|
|
|
if (nbVariations++ > g_maxNbVariations) break;
|
|
paramVariation(&p, unconstrained, 7);
|
|
|
|
/* exclude faster if already played params */
|
|
if (FUZ_rand(&g_rand) & ((1 << NB_TESTS_PLAYED(p))-1))
|
|
continue;
|
|
|
|
/* test */
|
|
NB_TESTS_PLAYED(p)++;
|
|
if (!BMK_seed(winners, p, srcBuffer, srcSize, ctx, dctx)) continue;
|
|
|
|
/* improvement found => search more */
|
|
BMK_printWinners(f, winners, srcSize);
|
|
playAround(f, winners, p, srcBuffer, srcSize, ctx, dctx);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static ZSTD_compressionParameters randomParams(void)
|
|
{
|
|
ZSTD_compressionParameters p;
|
|
U32 validated = 0;
|
|
while (!validated) {
|
|
/* totally random entry */
|
|
p.chainLog = (FUZ_rand(&g_rand) % (ZSTD_CHAINLOG_MAX+1 - ZSTD_CHAINLOG_MIN)) + ZSTD_CHAINLOG_MIN;
|
|
p.hashLog = (FUZ_rand(&g_rand) % (ZSTD_HASHLOG_MAX+1 - ZSTD_HASHLOG_MIN)) + ZSTD_HASHLOG_MIN;
|
|
p.searchLog = (FUZ_rand(&g_rand) % (ZSTD_SEARCHLOG_MAX+1 - ZSTD_SEARCHLOG_MIN)) + ZSTD_SEARCHLOG_MIN;
|
|
p.windowLog = (FUZ_rand(&g_rand) % (ZSTD_WINDOWLOG_MAX+1 - ZSTD_WINDOWLOG_MIN)) + ZSTD_WINDOWLOG_MIN;
|
|
p.searchLength=(FUZ_rand(&g_rand) % (ZSTD_SEARCHLENGTH_MAX+1 - ZSTD_SEARCHLENGTH_MIN)) + ZSTD_SEARCHLENGTH_MIN;
|
|
p.targetLength=(FUZ_rand(&g_rand) % (512));
|
|
p.strategy = (ZSTD_strategy) (FUZ_rand(&g_rand) % (ZSTD_btultra +1));
|
|
validated = !ZSTD_isError(ZSTD_checkCParams(p));
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static ZSTD_compressionParameters randomConstrainedParams(ZSTD_compressionParameters pc)
|
|
{
|
|
ZSTD_compressionParameters p;
|
|
U32 validated = 0;
|
|
while (!validated) {
|
|
/* totally random entry */
|
|
if(!pc.chainLog) p.chainLog = (FUZ_rand(&g_rand) % (ZSTD_CHAINLOG_MAX+1 - ZSTD_CHAINLOG_MIN)) + ZSTD_CHAINLOG_MIN;
|
|
if(!pc.chainLog) p.hashLog = (FUZ_rand(&g_rand) % (ZSTD_HASHLOG_MAX+1 - ZSTD_HASHLOG_MIN)) + ZSTD_HASHLOG_MIN;
|
|
if(!pc.chainLog) p.searchLog = (FUZ_rand(&g_rand) % (ZSTD_SEARCHLOG_MAX+1 - ZSTD_SEARCHLOG_MIN)) + ZSTD_SEARCHLOG_MIN;
|
|
if(!pc.chainLog) p.windowLog = (FUZ_rand(&g_rand) % (ZSTD_WINDOWLOG_MAX+1 - ZSTD_WINDOWLOG_MIN)) + ZSTD_WINDOWLOG_MIN;
|
|
if(!pc.chainLog) p.searchLength=(FUZ_rand(&g_rand) % (ZSTD_SEARCHLENGTH_MAX+1 - ZSTD_SEARCHLENGTH_MIN)) + ZSTD_SEARCHLENGTH_MIN;
|
|
if(!pc.chainLog) p.targetLength=(FUZ_rand(&g_rand) % (512)) + 1; //ZSTD_TARGETLENGTH_MIN; //change to 2^[0,10?]
|
|
if(!pc.chainLog) p.strategy = (ZSTD_strategy) (FUZ_rand(&g_rand) % (ZSTD_btultra +1));
|
|
validated = !ZSTD_isError(ZSTD_checkCParams(p));
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static void BMK_selectRandomStart(
|
|
FILE* f, winnerInfo_t* winners,
|
|
const void* srcBuffer, size_t srcSize,
|
|
ZSTD_CCtx* ctx, ZSTD_DCtx* dctx)
|
|
{
|
|
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, ctx, dctx);
|
|
} else {
|
|
playAround(f, winners, winners[id].params, srcBuffer, srcSize, ctx, dctx);
|
|
}
|
|
}
|
|
|
|
|
|
static void BMK_benchOnce(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, const void* srcBuffer, size_t srcSize)
|
|
{
|
|
BMK_result_t testResult;
|
|
g_params = ZSTD_adjustCParams(g_params, srcSize, 0);
|
|
BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, dctx, g_params);
|
|
DISPLAY("Compression Ratio: %.3f Compress Speed: %.1f MB/s Decompress Speed: %.1f MB/s\n", (double)srcSize / testResult.cSize,
|
|
testResult.cSpeed / 1000000, testResult.dSpeed / 1000000);
|
|
return;
|
|
}
|
|
|
|
static void BMK_benchFullTable(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, const void* srcBuffer, size_t srcSize)
|
|
{
|
|
ZSTD_compressionParameters params;
|
|
winnerInfo_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);
|
|
memset(winners, 0, sizeof(winners));
|
|
if (f==NULL) { DISPLAY("error opening %s \n", rfName); exit(1); }
|
|
|
|
if (g_target) {
|
|
BMK_init_level_constraints(g_target*1000000);
|
|
} else {
|
|
/* baseline config for level 1 */
|
|
ZSTD_compressionParameters const l1params = ZSTD_getCParams(1, blockSize, 0);
|
|
BMK_result_t testResult;
|
|
BMK_benchParam(&testResult, srcBuffer, srcSize, cctx, dctx, l1params);
|
|
BMK_init_level_constraints((int)((testResult.cSpeed * 31) / 32));
|
|
}
|
|
|
|
/* populate initial solution */
|
|
{ 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, cctx, dctx);
|
|
} }
|
|
BMK_printWinners(f, winners, srcSize);
|
|
|
|
/* start tests */
|
|
{ const time_t grillStart = time(NULL);
|
|
do {
|
|
BMK_selectRandomStart(f, winners, srcBuffer, srcSize, cctx, dctx);
|
|
} while (BMK_timeSpan(grillStart) < g_grillDuration_s);
|
|
}
|
|
|
|
/* end summary */
|
|
BMK_printWinners(f, winners, srcSize);
|
|
DISPLAY("grillParams operations completed \n");
|
|
|
|
/* clean up*/
|
|
fclose(f);
|
|
}
|
|
|
|
static void BMK_benchMem_usingCCtx(ZSTD_CCtx* const cctx, ZSTD_DCtx* const dctx, const void* srcBuffer, size_t srcSize)
|
|
{
|
|
if (g_singleRun)
|
|
return BMK_benchOnce(cctx, dctx, srcBuffer, srcSize);
|
|
else
|
|
return BMK_benchFullTable(cctx, dctx, srcBuffer, srcSize);
|
|
}
|
|
|
|
static void BMK_benchMemCCtxInit(const void* srcBuffer, size_t srcSize)
|
|
{
|
|
ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
|
ZSTD_DCtx* const dctx = ZSTD_createDCtx();
|
|
if (cctx==NULL || dctx==NULL) { DISPLAY("Context Creation failed \n"); exit(1); }
|
|
BMK_benchMem_usingCCtx(cctx, dctx, srcBuffer, srcSize);
|
|
ZSTD_freeCCtx(cctx);
|
|
}
|
|
|
|
|
|
static int benchSample(void)
|
|
{
|
|
const char* const name = "Sample 10MB";
|
|
size_t const benchedSize = 10000000;
|
|
|
|
void* origBuff = malloc(benchedSize);
|
|
if (!origBuff) { perror("not enough memory"); return 12; }
|
|
|
|
/* Fill buffer */
|
|
RDG_genBuffer(origBuff, benchedSize, g_compressibility, 0.0, 0);
|
|
|
|
/* bench */
|
|
DISPLAY("\r%79s\r", "");
|
|
DISPLAY("using %s %i%%: \n", name, (int)(g_compressibility*100));
|
|
BMK_benchMemCCtxInit(origBuff, benchedSize);
|
|
|
|
free(origBuff);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* 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 fileIdx=0;
|
|
|
|
/* Loop for each file */
|
|
while (fileIdx<nbFiles) {
|
|
const char* const inFileName = fileNamesTable[fileIdx++];
|
|
FILE* const inFile = fopen( inFileName, "rb" );
|
|
U64 const inFileSize = UTIL_getFileSize(inFileName);
|
|
size_t benchedSize;
|
|
void* origBuff;
|
|
|
|
/* Check file existence */
|
|
if (inFile==NULL) {
|
|
DISPLAY( "Pb opening %s\n", inFileName);
|
|
return 11;
|
|
}
|
|
if (inFileSize == UTIL_FILESIZE_UNKNOWN) {
|
|
DISPLAY("Pb evaluating size of %s \n", inFileName);
|
|
fclose(inFile);
|
|
return 11;
|
|
}
|
|
|
|
/* Memory allocation */
|
|
benchedSize = BMK_findMaxMem(inFileSize*3) / 3;
|
|
if ((U64)benchedSize > 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_benchMemCCtxInit(origBuff, benchedSize);
|
|
|
|
/* clean */
|
|
free(origBuff);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void BMK_translateAdvancedParams(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));
|
|
}
|
|
|
|
//Results currently don't capture memory usage or anything.
|
|
//parameter feasibility is not checked, should just be restricted from use.
|
|
static int feasible(BMK_result_t results, constraint_t target) {
|
|
return (results.cSpeed >= target.cSpeed) && (results.dSpeed >= target.dSpeed) && (results.cMem <= target.Mem || !target.Mem);
|
|
}
|
|
|
|
/* returns 1 if result2 is strictly 'better' than result1 */
|
|
static int objective_lt(BMK_result_t result1, BMK_result_t result2) {
|
|
return (result1.cSize > result2.cSize) || (result1.cSize == result2.cSize && result2.cSpeed > result1.cSpeed);
|
|
}
|
|
|
|
/* res gives array dimensions, should be size NUM_PARAMS */
|
|
static size_t computeStateSize(const ZSTD_compressionParameters paramConstraints, U32* res) {
|
|
int ind = 0;
|
|
size_t base = 1;
|
|
if(!paramConstraints.windowLog) { res[ind] = ZSTD_WINDOWLOG_MAX - ZSTD_WINDOWLOG_MIN + 1; base *= res[ind]; ind++; }
|
|
if(!paramConstraints.chainLog) { res[ind] = ZSTD_CHAINLOG_MAX - ZSTD_CHAINLOG_MIN + 1; base *= res[ind]; ind++; }
|
|
if(!paramConstraints.hashLog) { res[ind] = ZSTD_HASHLOG_MAX - ZSTD_HASHLOG_MIN + 1; base *= res[ind]; ind++; }
|
|
if(!paramConstraints.searchLog) { res[ind] = ZSTD_SEARCHLOG_MAX - ZSTD_SEARCHLOG_MIN + 1; base *= res[ind]; ind++; }
|
|
if(!paramConstraints.searchLength) { res[ind] = ZSTD_SEARCHLENGTH_MAX - ZSTD_SEARCHLENGTH_MIN + 1; base *= res[ind]; ind++; }
|
|
if(!paramConstraints.targetLength) { res[ind] = 11; base *= res[ind]; ind++; } //restricting from 2^[0,10], no such macros
|
|
if(!(U32)paramConstraints.strategy) { res[ind] = 8; base *= 8; } //not strictly true, maybe would want to case on this.
|
|
|
|
return base;
|
|
}
|
|
|
|
|
|
static unsigned calcViolation(BMK_result_t results, constraint_t target) {
|
|
int diffcSpeed = MAX(target.cSpeed - results.cSpeed, 0);
|
|
int diffdSpeed = MAX(target.dSpeed - results.dSpeed, 0);
|
|
int diffcMem = MAX(results.cMem - target.Mem, 0);
|
|
return diffcSpeed + diffdSpeed + diffcMem;
|
|
}
|
|
|
|
/* finds some set of parameters which fulfills req's
|
|
* Prioritize highest / try to locally minimize sum?
|
|
* Is it ever useful to go out of the param constraints? ?
|
|
* random / perturb when revisit?
|
|
* momentum?
|
|
*/
|
|
static ZSTD_compressionParameters findFeasible(constraint_t target, ZSTD_compressionParameters paramTarget) {
|
|
unsigned violation;
|
|
|
|
ZSTD_compressionParameters winner = randomConstrainedParams(paramTarget);
|
|
//just use g_alreadyTested and xxhash?
|
|
BYTE* memotable;
|
|
do {
|
|
//prioritize memory
|
|
if(diffcMem >= diffcSpeed && diffcMem >= diffdSpeed) {
|
|
|
|
//prioritize compression Speed
|
|
} else if (diffcSpeeed >= diffdSpeed && diffcSpeed >= diffcMem) {
|
|
|
|
//prioritize decompressionSpeed
|
|
} else {
|
|
|
|
}
|
|
violation = calcViolation(result, target);
|
|
} while(objective);
|
|
if(validate) {
|
|
DISPLAY("Feasible Point Found\n");
|
|
return winner;
|
|
} else {
|
|
DISPLAY("No solution found\n");
|
|
ZSTD_compressionParameters ret = { 0, 0, 0, 0, 0, 0, 0 };
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* optimizeForSize():
|
|
* targetSpeed : expressed in B/s */
|
|
/* if state space is small (from paramTarget) */
|
|
int optimizeForSize(const char* inFileName, constraint_t target, ZSTD_compressionParameters paramTarget)
|
|
{
|
|
FILE* const inFile = fopen( inFileName, "rb" );
|
|
U64 const inFileSize = UTIL_getFileSize(inFileName);
|
|
size_t benchedSize = BMK_findMaxMem(inFileSize*3) / 3;
|
|
void* origBuff;
|
|
U32 paramVarArray [NUM_PARAMS];
|
|
int paramCount = variableParams(paramTarget, paramVarArray);
|
|
/* Init */
|
|
if (inFile==NULL) { DISPLAY( "Pb opening %s\n", inFileName); return 11; }
|
|
if (inFileSize == UTIL_FILESIZE_UNKNOWN) {
|
|
DISPLAY("Pb evaluatin size of %s \n", inFileName);
|
|
fclose(inFile);
|
|
return 11;
|
|
}
|
|
|
|
/* Memory allocation & restrictions */
|
|
if ((U64)benchedSize > inFileSize) benchedSize = (size_t)inFileSize;
|
|
if (benchedSize < inFileSize) {
|
|
DISPLAY("Not enough memory for '%s' \n", inFileName);
|
|
fclose(inFile);
|
|
return 11;
|
|
}
|
|
|
|
/* Alloc */
|
|
origBuff = malloc(benchedSize);
|
|
if(!origBuff) {
|
|
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("optimizing for %s", inFileName);
|
|
if(target.cSpeed != 0) { DISPLAY(" - limit compression speed %u MB/s", target.cSpeed / 1000000); }
|
|
if(target.dSpeed != 0) { DISPLAY(" - limit decompression speed %u MB/s", target.dSpeed / 1000000); }
|
|
if(target.Mem != 0) { DISPLAY(" - limit memory %u MB", target.Mem / 1000000); }
|
|
DISPLAY("\n");
|
|
{ ZSTD_CCtx* const ctx = ZSTD_createCCtx();
|
|
ZSTD_DCtx* const dctx = ZSTD_createDCtx();
|
|
winnerInfo_t winner;
|
|
BMK_result_t candidate;
|
|
const size_t blockSize = g_blockSize ? g_blockSize : benchedSize;
|
|
|
|
/* init */
|
|
if (ctx==NULL) { DISPLAY("\n ZSTD_createCCtx error \n"); free(origBuff); return 14;}
|
|
memset(&winner, 0, sizeof(winner));
|
|
winner.result.cSize = (size_t)(-1);
|
|
|
|
/* find best solution from default params */
|
|
//Can't do this iteration normally w/ cparameter constraints
|
|
{ const int maxSeeds = g_noSeed ? 1 : ZSTD_maxCLevel();
|
|
int i;
|
|
for (i=1; i<=maxSeeds; i++) {
|
|
ZSTD_compressionParameters const CParams = ZSTD_getCParams(i, blockSize, 0);
|
|
BMK_benchParam(&candidate, origBuff, benchedSize, ctx, dctx, CParams);
|
|
if (!feasible(candidate, target) ) {
|
|
break;
|
|
}
|
|
if (feasible(candidate,target) && objective_lt(winner.result, candidate))
|
|
{
|
|
winner.params = CParams;
|
|
winner.result = candidate;
|
|
BMK_printWinner(stdout, i, winner.result, winner.params, benchedSize);
|
|
} }
|
|
}
|
|
BMK_printWinner(stdout, CUSTOM_LEVEL, winner.result, winner.params, benchedSize);
|
|
|
|
BMK_translateAdvancedParams(winner.params);
|
|
|
|
/* start tests */
|
|
{ time_t const grillStart = time(NULL);
|
|
do {
|
|
ZSTD_compressionParameters params = winner.params;
|
|
paramVariation(¶ms, paramVarArray, paramCount);
|
|
if ((FUZ_rand(&g_rand) & 31) == 3) params = randomParams(); /* totally random config to improve search space */
|
|
params = ZSTD_adjustCParams(params, blockSize, 0);
|
|
|
|
/* exclude faster if already played set of params */
|
|
if (FUZ_rand(&g_rand) & ((1 << NB_TESTS_PLAYED(params))-1)) continue;
|
|
|
|
/* test */
|
|
NB_TESTS_PLAYED(params)++;
|
|
BMK_benchParam(&candidate, origBuff, benchedSize, ctx, dctx, params);
|
|
|
|
/* improvement found => new winner */
|
|
if (feasible(candidate,target) && objective_lt(winner.result, candidate))
|
|
{
|
|
winner.params = params;
|
|
winner.result = candidate;
|
|
BMK_printWinner(stdout, CUSTOM_LEVEL, winner.result, winner.params, benchedSize);
|
|
BMK_translateAdvancedParams(winner.params);
|
|
}
|
|
} while (BMK_timeSpan(grillStart) < g_grillDuration_s);
|
|
}
|
|
|
|
/* no solution found */
|
|
if(winner.result.cSize == (size_t)-1) {
|
|
DISPLAY("No feasible solution found\n");
|
|
return 1;
|
|
}
|
|
/* end summary */
|
|
BMK_printWinner(stdout, CUSTOM_LEVEL, winner.result, winner.params, benchedSize);
|
|
BMK_translateAdvancedParams(winner.params);
|
|
DISPLAY("grillParams size - optimizer completed \n");
|
|
|
|
/* clean up*/
|
|
ZSTD_freeCCtx(ctx);
|
|
ZSTD_freeDCtx(dctx);
|
|
}
|
|
|
|
free(origBuff);
|
|
return 0;
|
|
}
|
|
|
|
static void errorOut(const char* msg)
|
|
{
|
|
DISPLAY("%s \n", msg); exit(1);
|
|
}
|
|
|
|
/*! readU32FromChar() :
|
|
* @return : unsigned integer value read from input in `char` format.
|
|
* allows and interprets K, KB, KiB, M, MB and MiB suffix.
|
|
* Will also modify `*stringPtr`, advancing it to position where it stopped reading.
|
|
* Note : function will exit() program if digit sequence overflows */
|
|
static unsigned readU32FromChar(const char** stringPtr)
|
|
{
|
|
const char errorMsg[] = "error: numeric value too large";
|
|
unsigned result = 0;
|
|
while ((**stringPtr >='0') && (**stringPtr <='9')) {
|
|
unsigned const max = (((unsigned)(-1)) / 10) - 1;
|
|
if (result > max) errorOut(errorMsg);
|
|
result *= 10, result += **stringPtr - '0', (*stringPtr)++ ;
|
|
}
|
|
if ((**stringPtr=='K') || (**stringPtr=='M')) {
|
|
unsigned const maxK = ((unsigned)(-1)) >> 10;
|
|
if (result > maxK) errorOut(errorMsg);
|
|
result <<= 10;
|
|
if (**stringPtr=='M') {
|
|
if (result > maxK) errorOut(errorMsg);
|
|
result <<= 10;
|
|
}
|
|
(*stringPtr)++; /* skip `K` or `M` */
|
|
if (**stringPtr=='i') (*stringPtr)++;
|
|
if (**stringPtr=='B') (*stringPtr)++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int usage(const char* exename)
|
|
{
|
|
DISPLAY( "Usage :\n");
|
|
DISPLAY( " %s [arg] file\n", exename);
|
|
DISPLAY( "Arguments :\n");
|
|
DISPLAY( " file : path to the file used as reference (if none, generates a compressible sample)\n");
|
|
DISPLAY( " -H/-h : Help (this text + advanced options)\n");
|
|
return 0;
|
|
}
|
|
|
|
static int usage_advanced(void)
|
|
{
|
|
DISPLAY( "\nAdvanced options :\n");
|
|
DISPLAY( " -T# : set level 1 speed objective \n");
|
|
DISPLAY( " -B# : cut input into blocks of size # (default : single block) \n");
|
|
DISPLAY( " -i# : iteration loops [1-9](default : %i) \n", NBLOOPS);
|
|
DISPLAY( " -O# : find Optimized parameters for # MB/s compression speed (default : 0) \n");
|
|
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( " -v : Prints Benchmarking output\n");
|
|
return 0;
|
|
}
|
|
|
|
static int badusage(const char* exename)
|
|
{
|
|
DISPLAY("Wrong parameters\n");
|
|
usage(exename);
|
|
return 1;
|
|
}
|
|
|
|
int main(int argc, const char** argv)
|
|
{
|
|
int i,
|
|
filenamesStart=0,
|
|
result;
|
|
const char* exename=argv[0];
|
|
const char* input_filename=0;
|
|
U32 optimizer = 0;
|
|
U32 main_pause = 0;
|
|
|
|
constraint_t target = { 0 , 0, 0 }; //0 for anything unset
|
|
ZSTD_compressionParameters paramTarget = { 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
assert(argc>=1); /* for exename */
|
|
|
|
/* Welcome message */
|
|
DISPLAY(WELCOME_MESSAGE);
|
|
|
|
for(i=1; i<argc; i++) {
|
|
const char* argument = argv[i];
|
|
assert(argument != NULL);
|
|
|
|
if(!strcmp(argument,"--no-seed")) { g_noSeed = 1; continue; }
|
|
|
|
if (longCommandWArg(&argument, "--optimize=")) {
|
|
optimizer = 1;
|
|
for ( ; ;) {
|
|
if (longCommandWArg(&argument, "windowLog=") || longCommandWArg(&argument, "wlog=")) { paramTarget.windowLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "chainLog=") || longCommandWArg(&argument, "clog=")) { paramTarget.chainLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "hashLog=") || longCommandWArg(&argument, "hlog=")) { paramTarget.hashLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "searchLog=") || longCommandWArg(&argument, "slog=")) { paramTarget.searchLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "searchLength=") || longCommandWArg(&argument, "slen=")) { paramTarget.searchLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "targetLength=") || longCommandWArg(&argument, "tlen=")) { paramTarget.targetLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "strategy=") || longCommandWArg(&argument, "strat=")) { paramTarget.strategy = (ZSTD_strategy)(readU32FromChar(&argument)); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "compressionSpeed=") || longCommandWArg(&argument, "cSpeed=")) { target.cSpeed = readU32FromChar(&argument) * 1000000; if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "decompressionSpeed=") || longCommandWArg(&argument, "dSpeed=")) { target.dSpeed = readU32FromChar(&argument) * 1000000; if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "compressionMemory=") || longCommandWArg(&argument, "cMem=")) { target.Mem = readU32FromChar(&argument) * 1000000; if (argument[0]==',') { argument++; continue; } else break; }
|
|
/* in MB or MB/s */
|
|
DISPLAY("invalid optimization parameter \n");
|
|
return 1;
|
|
}
|
|
|
|
if (argument[0] != 0) {
|
|
DISPLAY("invalid --optimize= format\n");
|
|
return 1; /* check the end of string */
|
|
}
|
|
continue;
|
|
} else if (longCommandWArg(&argument, "--zstd=")) {
|
|
/* Decode command (note : aggregated commands are allowed) */
|
|
g_singleRun = 1;
|
|
g_params = ZSTD_getCParams(2, g_blockSize, 0);
|
|
for ( ; ;) {
|
|
if (longCommandWArg(&argument, "windowLog=") || longCommandWArg(&argument, "wlog=")) { g_params.windowLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "chainLog=") || longCommandWArg(&argument, "clog=")) { g_params.chainLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "hashLog=") || longCommandWArg(&argument, "hlog=")) { g_params.hashLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "searchLog=") || longCommandWArg(&argument, "slog=")) { g_params.searchLog = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "searchLength=") || longCommandWArg(&argument, "slen=")) { g_params.searchLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "targetLength=") || longCommandWArg(&argument, "tlen=")) { g_params.targetLength = readU32FromChar(&argument); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "strategy=") || longCommandWArg(&argument, "strat=")) { g_params.strategy = (ZSTD_strategy)(readU32FromChar(&argument)); if (argument[0]==',') { argument++; continue; } else break; }
|
|
if (longCommandWArg(&argument, "level=") || longCommandWArg(&argument, "lvl=")) { g_params = ZSTD_getCParams(readU32FromChar(&argument), g_blockSize, 0); if (argument[0]==',') { argument++; continue; } else break; }
|
|
DISPLAY("invalid compression parameter \n");
|
|
return 1;
|
|
}
|
|
|
|
if (argument[0] != 0) {
|
|
DISPLAY("invalid --zstd= format\n");
|
|
return 1; /* check the end of string */
|
|
}
|
|
continue;
|
|
/* if not return, success */
|
|
} else if (argument[0]=='-') {
|
|
argument++;
|
|
|
|
while (argument[0]!=0) {
|
|
|
|
switch(argument[0])
|
|
{
|
|
/* Display help on usage */
|
|
case 'h' :
|
|
case 'H': usage(exename); usage_advanced(); return 0;
|
|
|
|
/* Pause at the end (hidden option) */
|
|
case 'p': main_pause = 1; argument++; break;
|
|
/* Modify Nb Iterations */
|
|
|
|
case 'i':
|
|
argument++;
|
|
g_nbIterations = readU32FromChar(&argument);
|
|
break;
|
|
|
|
/* Sample compressibility (when no file provided) */
|
|
case 'P':
|
|
argument++;
|
|
{ U32 const proba32 = readU32FromChar(&argument);
|
|
g_compressibility = (double)proba32 / 100.;
|
|
}
|
|
break;
|
|
|
|
case 'O':
|
|
argument++;
|
|
optimizer = 1;
|
|
for ( ; ; ) {
|
|
switch(*argument)
|
|
{
|
|
/* Inputs in MB or MB/s */
|
|
case 'C':
|
|
argument++;
|
|
target.cSpeed = readU32FromChar(&argument) * 1000000;
|
|
continue;
|
|
case 'D':
|
|
argument++;
|
|
target.dSpeed = readU32FromChar(&argument) * 1000000;
|
|
continue;
|
|
case 'M':
|
|
argument++;
|
|
target.Mem = readU32FromChar(&argument) * 1000000;
|
|
continue;
|
|
case 'w':
|
|
argument++;
|
|
paramTarget.windowLog = readU32FromChar(&argument);
|
|
continue;
|
|
case 'c':
|
|
argument++;
|
|
paramTarget.chainLog = readU32FromChar(&argument);
|
|
continue;
|
|
case 'h':
|
|
argument++;
|
|
paramTarget.hashLog = readU32FromChar(&argument);
|
|
continue;
|
|
case 's':
|
|
argument++;
|
|
paramTarget.searchLog = readU32FromChar(&argument);
|
|
continue;
|
|
case 'l': /* search length */
|
|
argument++;
|
|
paramTarget.searchLength = readU32FromChar(&argument);
|
|
continue;
|
|
case 't': /* target length */
|
|
argument++;
|
|
paramTarget.targetLength = readU32FromChar(&argument);
|
|
continue;
|
|
case 'S': /* strategy */
|
|
argument++;
|
|
paramTarget.strategy = (ZSTD_strategy)readU32FromChar(&argument);
|
|
continue;
|
|
default : ;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* Run Single conf */
|
|
case 'S':
|
|
g_singleRun = 1;
|
|
argument++;
|
|
g_params = ZSTD_getCParams(2, g_blockSize, 0);
|
|
for ( ; ; ) {
|
|
switch(*argument)
|
|
{
|
|
case 'w':
|
|
argument++;
|
|
g_params.windowLog = readU32FromChar(&argument);
|
|
continue;
|
|
case 'c':
|
|
argument++;
|
|
g_params.chainLog = readU32FromChar(&argument);
|
|
continue;
|
|
case 'h':
|
|
argument++;
|
|
g_params.hashLog = readU32FromChar(&argument);
|
|
continue;
|
|
case 's':
|
|
argument++;
|
|
g_params.searchLog = readU32FromChar(&argument);
|
|
continue;
|
|
case 'l': /* search length */
|
|
argument++;
|
|
g_params.searchLength = readU32FromChar(&argument);
|
|
continue;
|
|
case 't': /* target length */
|
|
argument++;
|
|
g_params.targetLength = readU32FromChar(&argument);
|
|
continue;
|
|
case 'S': /* strategy */
|
|
argument++;
|
|
g_params.strategy = (ZSTD_strategy)readU32FromChar(&argument);
|
|
continue;
|
|
case 'L':
|
|
{ int const cLevel = readU32FromChar(&argument);
|
|
g_params = ZSTD_getCParams(cLevel, g_blockSize, 0);
|
|
continue;
|
|
}
|
|
default : ;
|
|
}
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
/* target level1 speed objective, in MB/s */
|
|
case 'T':
|
|
argument++;
|
|
g_target = readU32FromChar(&argument);
|
|
break;
|
|
|
|
/* cut input into blocks */
|
|
case 'B':
|
|
argument++;
|
|
g_blockSize = readU32FromChar(&argument);
|
|
DISPLAY("using %u KB block size \n", g_blockSize>>10);
|
|
break;
|
|
|
|
/* caps runtime (in seconds) */
|
|
case 't':
|
|
argument++;
|
|
g_grillDuration_s = (double)readU32FromChar(&argument);
|
|
break;
|
|
|
|
/* Unknown command */
|
|
default : return badusage(exename);
|
|
}
|
|
}
|
|
continue;
|
|
} /* if (argument[0]=='-') */
|
|
|
|
/* first provided filename is input */
|
|
if (!input_filename) { input_filename=argument; filenamesStart=i; continue; }
|
|
}
|
|
|
|
if (filenamesStart==0) {
|
|
if (optimizer) {
|
|
DISPLAY("Optimizer Expects File\n");
|
|
return 1;
|
|
} else {
|
|
result = benchSample();
|
|
}
|
|
} else {
|
|
if (optimizer) {
|
|
result = optimizeForSize(input_filename, target, paramTarget);
|
|
} else {
|
|
result = benchFiles(argv+filenamesStart, argc-filenamesStart);
|
|
} }
|
|
|
|
if (main_pause) { int unused; printf("press enter...\n"); unused = getchar(); (void)unused; }
|
|
|
|
return result;
|
|
}
|