added workSpaceTooLarge and workSpaceWasteful

also :
slightly increased speed of test fuzzer.16
dev
Yann Collet 2018-06-05 11:23:18 -07:00
parent bdb673666f
commit 3d523c741b
3 changed files with 62 additions and 29 deletions

View File

@ -695,7 +695,8 @@ size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams)
/** ZSTD_clampCParams() : /** ZSTD_clampCParams() :
* make CParam values within valid range. * make CParam values within valid range.
* @return : valid CParams */ * @return : valid CParams */
static ZSTD_compressionParameters ZSTD_clampCParams(ZSTD_compressionParameters cParams) static ZSTD_compressionParameters
ZSTD_clampCParams(ZSTD_compressionParameters cParams)
{ {
# define CLAMP(val,min,max) { \ # define CLAMP(val,min,max) { \
if (val<min) val=min; \ if (val<min) val=min; \
@ -706,8 +707,9 @@ static ZSTD_compressionParameters ZSTD_clampCParams(ZSTD_compressionParameters c
CLAMP(cParams.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX); CLAMP(cParams.hashLog, ZSTD_HASHLOG_MIN, ZSTD_HASHLOG_MAX);
CLAMP(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX); CLAMP(cParams.searchLog, ZSTD_SEARCHLOG_MIN, ZSTD_SEARCHLOG_MAX);
CLAMP(cParams.searchLength, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX); CLAMP(cParams.searchLength, ZSTD_SEARCHLENGTH_MIN, ZSTD_SEARCHLENGTH_MAX);
if ((U32)(cParams.targetLength) < ZSTD_TARGETLENGTH_MIN) cParams.targetLength = ZSTD_TARGETLENGTH_MIN; if ((U32)(cParams.targetLength) < ZSTD_TARGETLENGTH_MIN)
if ((U32)(cParams.strategy) > (U32)ZSTD_btultra) cParams.strategy = ZSTD_btultra; cParams.targetLength = ZSTD_TARGETLENGTH_MIN;
CLAMP(cParams.strategy, ZSTD_fast, ZSTD_btultra);
return cParams; return cParams;
} }
@ -723,8 +725,11 @@ static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat)
optimize `cPar` for a given input (`srcSize` and `dictSize`). optimize `cPar` for a given input (`srcSize` and `dictSize`).
mostly downsizing to reduce memory consumption and initialization latency. mostly downsizing to reduce memory consumption and initialization latency.
Both `srcSize` and `dictSize` are optional (use 0 if unknown). Both `srcSize` and `dictSize` are optional (use 0 if unknown).
Note : cPar is considered validated at this stage. Use ZSTD_checkCParams() to ensure that condition. */ Note : cPar is assumed validated. Use ZSTD_checkCParams() to ensure this condition. */
ZSTD_compressionParameters ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize) static ZSTD_compressionParameters
ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar,
unsigned long long srcSize,
size_t dictSize)
{ {
static const U64 minSrcSize = 513; /* (1<<9) + 1 */ static const U64 minSrcSize = 513; /* (1<<9) + 1 */
static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1); static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1);
@ -756,13 +761,18 @@ ZSTD_compressionParameters ZSTD_adjustCParams_internal(ZSTD_compressionParameter
return cPar; return cPar;
} }
ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressionParameters cPar, unsigned long long srcSize, size_t dictSize) ZSTD_compressionParameters
ZSTD_adjustCParams(ZSTD_compressionParameters cPar,
unsigned long long srcSize,
size_t dictSize)
{ {
cPar = ZSTD_clampCParams(cPar); cPar = ZSTD_clampCParams(cPar);
return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize); return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize);
} }
static size_t ZSTD_sizeof_matchState(ZSTD_compressionParameters const* cParams, const U32 forCCtx) static size_t
ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams,
const U32 forCCtx)
{ {
size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog); size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
size_t const hSize = ((size_t)1) << cParams->hashLog; size_t const hSize = ((size_t)1) << cParams->hashLog;
@ -848,12 +858,14 @@ size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams)
return ZSTD_estimateCStreamSize_usingCCtxParams(&params); return ZSTD_estimateCStreamSize_usingCCtxParams(&params);
} }
static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel) { static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel)
{
ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0); ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, 0, 0);
return ZSTD_estimateCStreamSize_usingCParams(cParams); return ZSTD_estimateCStreamSize_usingCParams(cParams);
} }
size_t ZSTD_estimateCStreamSize(int compressionLevel) { size_t ZSTD_estimateCStreamSize(int compressionLevel)
{
int level; int level;
size_t memBudget = 0; size_t memBudget = 0;
for (level=1; level<=compressionLevel; level++) { for (level=1; level<=compressionLevel; level++) {
@ -1042,10 +1054,18 @@ ZSTD_reset_matchState(ZSTD_matchState_t* ms,
return ptr; return ptr;
} }
#define ZSTD_WORKSPACETOOLARGE_FACTOR 3 /* define "workspace is too large" as ZSTD_WORKSPACETOOLARGE_FACTOR times larger than needed */
#define ZSTD_WORKSPACETOOLARGE_MAX 128 /* when workspace is continuously too large
* at least that number of times,
* context's memory usage is actually wasteful,
* because it's sized to handle a worst case scenario which rarely happens.
* In which case, resize it down to free some memory */
/*! ZSTD_resetCCtx_internal() : /*! ZSTD_resetCCtx_internal() :
note : `params` are assumed fully validated at this stage */ note : `params` are assumed fully validated at this stage */
static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
ZSTD_CCtx_params params, U64 pledgedSrcSize, ZSTD_CCtx_params params,
U64 pledgedSrcSize,
ZSTD_compResetPolicy_e const crp, ZSTD_compResetPolicy_e const crp,
ZSTD_buffered_policy_e const zbuff) ZSTD_buffered_policy_e const zbuff)
{ {
@ -1057,8 +1077,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
if (ZSTD_equivalentParams(zc->appliedParams, params, if (ZSTD_equivalentParams(zc->appliedParams, params,
zc->inBuffSize, zc->blockSize, zc->inBuffSize, zc->blockSize,
zbuff, pledgedSrcSize)) { zbuff, pledgedSrcSize)) {
DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%u)", DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%zu)",
zc->appliedParams.cParams.windowLog, (U32)zc->blockSize); zc->appliedParams.cParams.windowLog, zc->blockSize);
return ZSTD_continueCCtx(zc, params, pledgedSrcSize); return ZSTD_continueCCtx(zc, params, pledgedSrcSize);
} } } }
DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx"); DEBUGLOG(4, "ZSTD_equivalentParams()==0 -> reset CCtx");
@ -1069,8 +1089,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams); ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams);
assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog); assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);
assert(params.ldmParams.hashEveryLog < 32); assert(params.ldmParams.hashEveryLog < 32);
zc->ldmState.hashPower = zc->ldmState.hashPower = ZSTD_ldm_getHashPower(params.ldmParams.minMatchLength);
ZSTD_ldm_getHashPower(params.ldmParams.minMatchLength);
} }
{ size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize)); { size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize));
@ -1082,7 +1101,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0; size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0;
size_t const matchStateSize = ZSTD_sizeof_matchState(&params.cParams, /* forCCtx */ 1); size_t const matchStateSize = ZSTD_sizeof_matchState(&params.cParams, /* forCCtx */ 1);
size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize); size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize);
void* ptr; void* ptr; /* used to partition workSpace */
/* Check if workSpace is large enough, alloc a new one if needed */ /* Check if workSpace is large enough, alloc a new one if needed */
{ size_t const entropySpace = HUF_WORKSPACE_SIZE; { size_t const entropySpace = HUF_WORKSPACE_SIZE;
@ -1094,14 +1113,19 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
size_t const neededSpace = entropySpace + blockStateSpace + ldmSpace + size_t const neededSpace = entropySpace + blockStateSpace + ldmSpace +
ldmSeqSpace + matchStateSize + tokenSpace + ldmSeqSpace + matchStateSize + tokenSpace +
bufferSpace; bufferSpace;
DEBUGLOG(4, "Need %uKB workspace, including %uKB for match state, and %uKB for buffers",
(U32)(neededSpace>>10), (U32)(matchStateSize>>10), (U32)(bufferSpace>>10));
DEBUGLOG(4, "windowSize: %u - blockSize: %u", (U32)windowSize, (U32)blockSize);
if (zc->workSpaceSize < neededSpace) { /* too small : resize */ int const workSpaceTooSmall = zc->workSpaceSize < neededSpace;
DEBUGLOG(4, "Need to update workSpaceSize from %uK to %uK", int const workSpaceTooLarge = zc->workSpaceSize > ZSTD_WORKSPACETOOLARGE_FACTOR * neededSpace;
(unsigned)(zc->workSpaceSize>>10), int const workSpaceWasteful = 0 && workSpaceTooLarge && (zc->workSpaceTooLarge > ZSTD_WORKSPACETOOLARGE_MAX);
(unsigned)(neededSpace>>10)); DEBUGLOG(4, "Need %zuKB workspace, including %zuKB for match state, and %zuKB for buffers",
neededSpace>>10, matchStateSize>>10, bufferSpace>>10);
DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize);
zc->workSpaceTooLarge = workSpaceTooLarge ? zc->workSpaceTooLarge+1 : 0;
if (workSpaceTooSmall || workSpaceWasteful) {
DEBUGLOG(4, "Need to resize workSpaceSize from %zuKB to %zuKB",
zc->workSpaceSize >> 10,
neededSpace >> 10);
/* static cctx : no resize, error out */ /* static cctx : no resize, error out */
if (zc->staticSize) return ERROR(memory_allocation); if (zc->staticSize) return ERROR(memory_allocation);
@ -1110,9 +1134,12 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem); zc->workSpace = ZSTD_malloc(neededSpace, zc->customMem);
if (zc->workSpace == NULL) return ERROR(memory_allocation); if (zc->workSpace == NULL) return ERROR(memory_allocation);
zc->workSpaceSize = neededSpace; zc->workSpaceSize = neededSpace;
zc->workSpaceTooLarge = 0;
ptr = zc->workSpace; ptr = zc->workSpace;
/* Statically sized space. entropyWorkspace never moves (but prev/next block swap places) */ /* Statically sized space.
* entropyWorkspace never moves,
* though prev/next block swap places */
assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */ assert(((size_t)zc->workSpace & 3) == 0); /* ensure correct alignment */
assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_compressedBlockState_t)); assert(zc->workSpaceSize >= 2 * sizeof(ZSTD_compressedBlockState_t));
zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)zc->workSpace; zc->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)zc->workSpace;

View File

@ -27,6 +27,7 @@
extern "C" { extern "C" {
#endif #endif
/*-************************************* /*-*************************************
* Constants * Constants
***************************************/ ***************************************/
@ -37,7 +38,8 @@ extern "C" {
It's not a big deal though : candidate will just be sorted again. It's not a big deal though : candidate will just be sorted again.
Additionnally, candidate position 1 will be lost. Additionnally, candidate position 1 will be lost.
But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss. But candidate 1 cannot hide a large tree of candidates, so it's a minimal loss.
The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy */ The benefit is that ZSTD_DUBT_UNSORTED_MARK cannot be misdhandled after table re-use with a different strategy
Constant required by ZSTD_compressBlock_btlazy2() and ZSTD_reduceTable_internal() */
/*-************************************* /*-*************************************
@ -204,6 +206,8 @@ struct ZSTD_CCtx_s {
ZSTD_CCtx_params requestedParams; ZSTD_CCtx_params requestedParams;
ZSTD_CCtx_params appliedParams; ZSTD_CCtx_params appliedParams;
U32 dictID; U32 dictID;
int workSpaceTooLarge;
void* workSpace; void* workSpace;
size_t workSpaceSize; size_t workSpaceSize;
size_t blockSize; size_t blockSize;

View File

@ -450,14 +450,16 @@ static int basicUnitTests(U32 seed, double compressibility)
} }
DISPLAYLEVEL(3, "OK \n"); DISPLAYLEVEL(3, "OK \n");
DISPLAYLEVEL(3, "test%3d : large window log smaller data : ", testNb++); DISPLAYLEVEL(3, "test%3d : overflow protection with large windowLog : ", testNb++);
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx(); { ZSTD_CCtx* const cctx = ZSTD_createCCtx();
ZSTD_parameters params = ZSTD_getParams(1, ZSTD_CONTENTSIZE_UNKNOWN, 0); ZSTD_parameters params = ZSTD_getParams(-9, ZSTD_CONTENTSIZE_UNKNOWN, 0);
size_t const nbCompressions = (1U << 31) / CNBuffSize + 1; size_t const nbCompressions = ((1U << 31) / CNBuffSize) + 1; /* ensure U32 overflow protection is triggered */
size_t i; size_t cnb;
assert(cctx != NULL);
params.fParams.contentSizeFlag = 0; params.fParams.contentSizeFlag = 0;
params.cParams.windowLog = ZSTD_WINDOWLOG_MAX; params.cParams.windowLog = ZSTD_WINDOWLOG_MAX;
for (i = 0; i < nbCompressions; ++i) { for (cnb = 0; cnb < nbCompressions; ++cnb) {
DISPLAYLEVEL(6, "run %zu / %zu \n", cnb, nbCompressions);
CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN) ); /* re-use same parameters */ CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN) ); /* re-use same parameters */
CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize) ); CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, compressedBufferSize, CNBuffer, CNBuffSize) );
} }