2016-04-22 10:43:18 +00:00
|
|
|
/* ******************************************************************
|
2020-03-26 22:19:05 +00:00
|
|
|
* Huffman encoder, part of New Generation Entropy library
|
|
|
|
* Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
|
|
|
|
*
|
|
|
|
* You can contact the author at :
|
|
|
|
* - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
|
|
|
* - Public forum : https://groups.google.com/forum/#!forum/lz4c
|
|
|
|
*
|
|
|
|
* 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.
|
2016-04-22 10:43:18 +00:00
|
|
|
****************************************************************** */
|
|
|
|
|
|
|
|
/* **************************************************************
|
|
|
|
* Compiler specifics
|
|
|
|
****************************************************************/
|
|
|
|
#ifdef _MSC_VER /* Visual Studio */
|
|
|
|
# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* **************************************************************
|
|
|
|
* Includes
|
|
|
|
****************************************************************/
|
|
|
|
#include <string.h> /* memcpy, memset */
|
|
|
|
#include <stdio.h> /* printf (debug) */
|
2020-05-01 20:07:57 +00:00
|
|
|
#include "../common/compiler.h"
|
|
|
|
#include "../common/bitstream.h"
|
2018-06-13 23:49:31 +00:00
|
|
|
#include "hist.h"
|
2016-06-04 22:58:01 +00:00
|
|
|
#define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */
|
2020-05-01 20:07:57 +00:00
|
|
|
#include "../common/fse.h" /* header compression */
|
2016-06-04 22:42:28 +00:00
|
|
|
#define HUF_STATIC_LINKING_ONLY
|
2020-05-01 20:07:57 +00:00
|
|
|
#include "../common/huf.h"
|
|
|
|
#include "../common/error_private.h"
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* **************************************************************
|
|
|
|
* Error Management
|
|
|
|
****************************************************************/
|
2017-08-15 18:23:28 +00:00
|
|
|
#define HUF_isError ERR_isError
|
2018-06-13 18:59:26 +00:00
|
|
|
#define HUF_STATIC_ASSERT(c) DEBUG_STATIC_ASSERT(c) /* use only *after* variable declarations */
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
|
2016-05-20 12:36:36 +00:00
|
|
|
/* **************************************************************
|
|
|
|
* Utils
|
|
|
|
****************************************************************/
|
|
|
|
unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
|
|
|
|
{
|
|
|
|
return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
/* *******************************************************
|
|
|
|
* HUF : Huffman block compression
|
|
|
|
*********************************************************/
|
2016-12-02 00:13:35 +00:00
|
|
|
/* HUF_compressWeights() :
|
|
|
|
* Same as FSE_compress(), but dedicated to huff0's weights compression.
|
|
|
|
* The use case needs much less stack memory.
|
|
|
|
* Note : all elements within weightTable are supposed to be <= HUF_TABLELOG_MAX.
|
|
|
|
*/
|
|
|
|
#define MAX_FSE_TABLELOG_FOR_HUFF_HEADER 6
|
2018-09-27 22:13:43 +00:00
|
|
|
static size_t HUF_compressWeights (void* dst, size_t dstSize, const void* weightTable, size_t wtSize)
|
2016-12-02 00:13:35 +00:00
|
|
|
{
|
|
|
|
BYTE* const ostart = (BYTE*) dst;
|
|
|
|
BYTE* op = ostart;
|
|
|
|
BYTE* const oend = ostart + dstSize;
|
|
|
|
|
fix confusion between unsigned <-> U32
as suggested in #1441.
generally U32 and unsigned are the same thing,
except when they are not ...
case : 32-bit compilation for MIPS (uint32_t == unsigned long)
A vast majority of transformation consists in transforming U32 into unsigned.
In rare cases, it's the other way around (typically for internal code, such as seeds).
Among a few issues this patches solves :
- some parameters were declared with type `unsigned` in *.h,
but with type `U32` in their implementation *.c .
- some parameters have type unsigned*,
but the caller user a pointer to U32 instead.
These fixes are useful.
However, the bulk of changes is about %u formating,
which requires unsigned type,
but generally receives U32 values instead,
often just for brevity (U32 is shorter than unsigned).
These changes are generally minor, or even annoying.
As a consequence, the amount of code changed is larger than I would expect for such a patch.
Testing is also a pain :
it requires manually modifying `mem.h`,
in order to lie about `U32`
and force it to be an `unsigned long` typically.
On a 64-bit system, this will break the equivalence unsigned == U32.
Unfortunately, it will also break a few static_assert(), controlling structure sizes.
So it also requires modifying `debug.h` to make `static_assert()` a noop.
And then reverting these changes.
So it's inconvenient, and as a consequence,
this property is currently not checked during CI tests.
Therefore, these problems can emerge again in the future.
I wonder if it is worth ensuring proper distinction of U32 != unsigned in CI tests.
It's another restriction for coding, adding more frustration during merge tests,
since most platforms don't need this distinction (hence contributor will not see it),
and while this can matter in theory, the number of platforms impacted seems minimal.
Thoughts ?
2018-12-22 00:19:44 +00:00
|
|
|
unsigned maxSymbolValue = HUF_TABLELOG_MAX;
|
2016-12-02 00:13:35 +00:00
|
|
|
U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER;
|
|
|
|
|
|
|
|
FSE_CTable CTable[FSE_CTABLE_SIZE_U32(MAX_FSE_TABLELOG_FOR_HUFF_HEADER, HUF_TABLELOG_MAX)];
|
|
|
|
BYTE scratchBuffer[1<<MAX_FSE_TABLELOG_FOR_HUFF_HEADER];
|
|
|
|
|
fix confusion between unsigned <-> U32
as suggested in #1441.
generally U32 and unsigned are the same thing,
except when they are not ...
case : 32-bit compilation for MIPS (uint32_t == unsigned long)
A vast majority of transformation consists in transforming U32 into unsigned.
In rare cases, it's the other way around (typically for internal code, such as seeds).
Among a few issues this patches solves :
- some parameters were declared with type `unsigned` in *.h,
but with type `U32` in their implementation *.c .
- some parameters have type unsigned*,
but the caller user a pointer to U32 instead.
These fixes are useful.
However, the bulk of changes is about %u formating,
which requires unsigned type,
but generally receives U32 values instead,
often just for brevity (U32 is shorter than unsigned).
These changes are generally minor, or even annoying.
As a consequence, the amount of code changed is larger than I would expect for such a patch.
Testing is also a pain :
it requires manually modifying `mem.h`,
in order to lie about `U32`
and force it to be an `unsigned long` typically.
On a 64-bit system, this will break the equivalence unsigned == U32.
Unfortunately, it will also break a few static_assert(), controlling structure sizes.
So it also requires modifying `debug.h` to make `static_assert()` a noop.
And then reverting these changes.
So it's inconvenient, and as a consequence,
this property is currently not checked during CI tests.
Therefore, these problems can emerge again in the future.
I wonder if it is worth ensuring proper distinction of U32 != unsigned in CI tests.
It's another restriction for coding, adding more frustration during merge tests,
since most platforms don't need this distinction (hence contributor will not see it),
and while this can matter in theory, the number of platforms impacted seems minimal.
Thoughts ?
2018-12-22 00:19:44 +00:00
|
|
|
unsigned count[HUF_TABLELOG_MAX+1];
|
2016-12-02 00:13:35 +00:00
|
|
|
S16 norm[HUF_TABLELOG_MAX+1];
|
|
|
|
|
|
|
|
/* init conditions */
|
|
|
|
if (wtSize <= 1) return 0; /* Not compressible */
|
|
|
|
|
|
|
|
/* Scan input and build symbol stats */
|
2018-06-13 23:49:31 +00:00
|
|
|
{ unsigned const maxCount = HIST_count_simple(count, &maxSymbolValue, weightTable, wtSize); /* never fails */
|
2016-12-02 00:13:35 +00:00
|
|
|
if (maxCount == wtSize) return 1; /* only a single symbol in src : rle */
|
2018-06-13 23:49:31 +00:00
|
|
|
if (maxCount == 1) return 0; /* each symbol present maximum once => not compressible */
|
2016-12-02 00:13:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue);
|
|
|
|
CHECK_F( FSE_normalizeCount(norm, tableLog, count, wtSize, maxSymbolValue) );
|
|
|
|
|
|
|
|
/* Write table description header */
|
2020-05-04 21:43:09 +00:00
|
|
|
{ CHECK_V_F(hSize, FSE_writeNCount(op, (size_t)(oend-op), norm, maxSymbolValue, tableLog) );
|
2016-12-02 00:13:35 +00:00
|
|
|
op += hSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compress */
|
|
|
|
CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, sizeof(scratchBuffer)) );
|
2020-05-04 21:43:09 +00:00
|
|
|
{ CHECK_V_F(cSize, FSE_compress_usingCTable(op, (size_t)(oend - op), weightTable, wtSize, CTable) );
|
2016-12-02 00:13:35 +00:00
|
|
|
if (cSize == 0) return 0; /* not enough space for compressed data */
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
2020-05-04 21:43:09 +00:00
|
|
|
return (size_t)(op-ostart);
|
2016-12-02 00:13:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
struct HUF_CElt_s {
|
|
|
|
U16 val;
|
|
|
|
BYTE nbBits;
|
2016-08-29 16:03:12 +00:00
|
|
|
}; /* typedef'd to HUF_CElt within "huf.h" */
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/*! HUF_writeCTable() :
|
2017-02-20 20:08:59 +00:00
|
|
|
`CTable` : Huffman tree to save, using huf representation.
|
2016-04-22 10:43:18 +00:00
|
|
|
@return : size of saved CTable */
|
|
|
|
size_t HUF_writeCTable (void* dst, size_t maxDstSize,
|
fix confusion between unsigned <-> U32
as suggested in #1441.
generally U32 and unsigned are the same thing,
except when they are not ...
case : 32-bit compilation for MIPS (uint32_t == unsigned long)
A vast majority of transformation consists in transforming U32 into unsigned.
In rare cases, it's the other way around (typically for internal code, such as seeds).
Among a few issues this patches solves :
- some parameters were declared with type `unsigned` in *.h,
but with type `U32` in their implementation *.c .
- some parameters have type unsigned*,
but the caller user a pointer to U32 instead.
These fixes are useful.
However, the bulk of changes is about %u formating,
which requires unsigned type,
but generally receives U32 values instead,
often just for brevity (U32 is shorter than unsigned).
These changes are generally minor, or even annoying.
As a consequence, the amount of code changed is larger than I would expect for such a patch.
Testing is also a pain :
it requires manually modifying `mem.h`,
in order to lie about `U32`
and force it to be an `unsigned long` typically.
On a 64-bit system, this will break the equivalence unsigned == U32.
Unfortunately, it will also break a few static_assert(), controlling structure sizes.
So it also requires modifying `debug.h` to make `static_assert()` a noop.
And then reverting these changes.
So it's inconvenient, and as a consequence,
this property is currently not checked during CI tests.
Therefore, these problems can emerge again in the future.
I wonder if it is worth ensuring proper distinction of U32 != unsigned in CI tests.
It's another restriction for coding, adding more frustration during merge tests,
since most platforms don't need this distinction (hence contributor will not see it),
and while this can matter in theory, the number of platforms impacted seems minimal.
Thoughts ?
2018-12-22 00:19:44 +00:00
|
|
|
const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog)
|
2016-04-22 10:43:18 +00:00
|
|
|
{
|
2016-11-30 23:52:20 +00:00
|
|
|
BYTE bitsToWeight[HUF_TABLELOG_MAX + 1]; /* precomputed conversion table */
|
2016-07-24 13:35:59 +00:00
|
|
|
BYTE huffWeight[HUF_SYMBOLVALUE_MAX];
|
2016-04-22 10:43:18 +00:00
|
|
|
BYTE* op = (BYTE*)dst;
|
2016-07-24 13:35:59 +00:00
|
|
|
U32 n;
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* check conditions */
|
2016-11-30 23:52:20 +00:00
|
|
|
if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* convert to weight */
|
|
|
|
bitsToWeight[0] = 0;
|
2016-07-24 13:35:59 +00:00
|
|
|
for (n=1; n<huffLog+1; n++)
|
2016-04-22 10:43:18 +00:00
|
|
|
bitsToWeight[n] = (BYTE)(huffLog + 1 - n);
|
|
|
|
for (n=0; n<maxSymbolValue; n++)
|
|
|
|
huffWeight[n] = bitsToWeight[CTable[n].nbBits];
|
|
|
|
|
2016-11-30 23:52:20 +00:00
|
|
|
/* attempt weights compression by FSE */
|
2016-12-02 00:24:04 +00:00
|
|
|
{ CHECK_V_F(hSize, HUF_compressWeights(op+1, maxDstSize-1, huffWeight, maxSymbolValue) );
|
2016-12-02 00:13:35 +00:00
|
|
|
if ((hSize>1) & (hSize < maxSymbolValue/2)) { /* FSE compressed */
|
|
|
|
op[0] = (BYTE)hSize;
|
|
|
|
return hSize+1;
|
2016-11-30 23:52:20 +00:00
|
|
|
} }
|
2016-07-24 13:35:59 +00:00
|
|
|
|
2016-12-02 00:13:35 +00:00
|
|
|
/* write raw values as 4-bits (max : 15) */
|
2016-11-30 23:52:20 +00:00
|
|
|
if (maxSymbolValue > (256-128)) return ERROR(GENERIC); /* should not happen : likely means source cannot be compressed */
|
2016-07-24 13:35:59 +00:00
|
|
|
if (((maxSymbolValue+1)/2) + 1 > maxDstSize) return ERROR(dstSize_tooSmall); /* not enough space within dst buffer */
|
|
|
|
op[0] = (BYTE)(128 /*special case*/ + (maxSymbolValue-1));
|
2016-12-02 00:13:35 +00:00
|
|
|
huffWeight[maxSymbolValue] = 0; /* to be sure it doesn't cause msan issue in final combination */
|
2016-07-24 13:35:59 +00:00
|
|
|
for (n=0; n<maxSymbolValue; n+=2)
|
|
|
|
op[(n/2)+1] = (BYTE)((huffWeight[n] << 4) + huffWeight[n+1]);
|
|
|
|
return ((maxSymbolValue+1)/2) + 1;
|
|
|
|
}
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
|
2019-11-26 20:24:19 +00:00
|
|
|
size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* hasZeroWeights)
|
2016-04-22 10:43:18 +00:00
|
|
|
{
|
2016-12-02 00:13:35 +00:00
|
|
|
BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1]; /* init not required, even though some static analyzer may complain */
|
2016-05-20 12:36:36 +00:00
|
|
|
U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1]; /* large enough for values from 0 to 16 */
|
2016-04-22 10:43:18 +00:00
|
|
|
U32 tableLog = 0;
|
|
|
|
U32 nbSymbols = 0;
|
|
|
|
|
|
|
|
/* get symbol weights */
|
2016-12-02 00:24:04 +00:00
|
|
|
CHECK_V_F(readSize, HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX+1, rankVal, &nbSymbols, &tableLog, src, srcSize));
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* check result */
|
2016-05-20 12:36:36 +00:00
|
|
|
if (tableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
|
2017-10-03 20:22:13 +00:00
|
|
|
if (nbSymbols > *maxSymbolValuePtr+1) return ERROR(maxSymbolValue_tooSmall);
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* Prepare base value per rank */
|
|
|
|
{ U32 n, nextRankStart = 0;
|
|
|
|
for (n=1; n<=tableLog; n++) {
|
|
|
|
U32 current = nextRankStart;
|
|
|
|
nextRankStart += (rankVal[n] << (n-1));
|
|
|
|
rankVal[n] = current;
|
|
|
|
} }
|
|
|
|
|
|
|
|
/* fill nbBits */
|
2019-11-26 20:24:19 +00:00
|
|
|
*hasZeroWeights = 0;
|
2016-07-24 12:26:11 +00:00
|
|
|
{ U32 n; for (n=0; n<nbSymbols; n++) {
|
|
|
|
const U32 w = huffWeight[n];
|
2019-11-26 20:24:19 +00:00
|
|
|
*hasZeroWeights |= (w == 0);
|
|
|
|
CTable[n].nbBits = (BYTE)(tableLog + 1 - w) & -(w != 0);
|
2016-07-24 12:26:11 +00:00
|
|
|
} }
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* fill val */
|
2016-10-18 18:27:52 +00:00
|
|
|
{ U16 nbPerRank[HUF_TABLELOG_MAX+2] = {0}; /* support w=0=>n=tableLog+1 */
|
|
|
|
U16 valPerRank[HUF_TABLELOG_MAX+2] = {0};
|
2016-04-22 10:43:18 +00:00
|
|
|
{ U32 n; for (n=0; n<nbSymbols; n++) nbPerRank[CTable[n].nbBits]++; }
|
|
|
|
/* determine stating value per rank */
|
2016-10-18 18:27:52 +00:00
|
|
|
valPerRank[tableLog+1] = 0; /* for w==0 */
|
2016-04-22 10:43:18 +00:00
|
|
|
{ U16 min = 0;
|
2016-10-18 18:27:52 +00:00
|
|
|
U32 n; for (n=tableLog; n>0; n--) { /* start at n=tablelog <-> w=1 */
|
|
|
|
valPerRank[n] = min; /* get starting value within each rank */
|
2016-04-22 10:43:18 +00:00
|
|
|
min += nbPerRank[n];
|
|
|
|
min >>= 1;
|
|
|
|
} }
|
|
|
|
/* assign value within rank, symbol order */
|
2017-10-03 20:22:13 +00:00
|
|
|
{ U32 n; for (n=0; n<nbSymbols; n++) CTable[n].val = valPerRank[CTable[n].nbBits]++; }
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|
|
|
|
|
2017-10-03 20:22:13 +00:00
|
|
|
*maxSymbolValuePtr = nbSymbols - 1;
|
2016-04-22 10:43:18 +00:00
|
|
|
return readSize;
|
|
|
|
}
|
|
|
|
|
2018-05-08 22:37:06 +00:00
|
|
|
U32 HUF_getNbBits(const void* symbolTable, U32 symbolValue)
|
|
|
|
{
|
|
|
|
const HUF_CElt* table = (const HUF_CElt*)symbolTable;
|
|
|
|
assert(symbolValue <= HUF_SYMBOLVALUE_MAX);
|
|
|
|
return table[symbolValue].nbBits;
|
|
|
|
}
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
|
2016-12-02 00:13:35 +00:00
|
|
|
typedef struct nodeElt_s {
|
|
|
|
U32 count;
|
|
|
|
U16 parent;
|
|
|
|
BYTE byte;
|
|
|
|
BYTE nbBits;
|
|
|
|
} nodeElt;
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
|
|
|
|
{
|
|
|
|
const U32 largestBits = huffNode[lastNonNull].nbBits;
|
|
|
|
if (largestBits <= maxNbBits) return largestBits; /* early exit : no elt > maxNbBits */
|
|
|
|
|
|
|
|
/* there are several too large elements (at least >= 2) */
|
|
|
|
{ int totalCost = 0;
|
|
|
|
const U32 baseCost = 1 << (largestBits - maxNbBits);
|
2020-05-04 21:43:09 +00:00
|
|
|
int n = (int)lastNonNull;
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
while (huffNode[n].nbBits > maxNbBits) {
|
|
|
|
totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits));
|
|
|
|
huffNode[n].nbBits = (BYTE)maxNbBits;
|
|
|
|
n --;
|
|
|
|
} /* n stops at huffNode[n].nbBits <= maxNbBits */
|
|
|
|
while (huffNode[n].nbBits == maxNbBits) n--; /* n end at index of smallest symbol using < maxNbBits */
|
|
|
|
|
|
|
|
/* renorm totalCost */
|
|
|
|
totalCost >>= (largestBits - maxNbBits); /* note : totalCost is necessarily a multiple of baseCost */
|
|
|
|
|
|
|
|
/* repay normalized cost */
|
|
|
|
{ U32 const noSymbol = 0xF0F0F0F0;
|
2016-07-13 15:38:39 +00:00
|
|
|
U32 rankLast[HUF_TABLELOG_MAX+2];
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* Get pos of last (smallest) symbol per rank */
|
|
|
|
memset(rankLast, 0xF0, sizeof(rankLast));
|
|
|
|
{ U32 currentNbBits = maxNbBits;
|
2020-05-04 21:43:09 +00:00
|
|
|
int pos;
|
2016-04-22 10:43:18 +00:00
|
|
|
for (pos=n ; pos >= 0; pos--) {
|
|
|
|
if (huffNode[pos].nbBits >= currentNbBits) continue;
|
|
|
|
currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */
|
2020-05-04 21:43:09 +00:00
|
|
|
rankLast[maxNbBits-currentNbBits] = (U32)pos;
|
2016-04-22 10:43:18 +00:00
|
|
|
} }
|
|
|
|
|
|
|
|
while (totalCost > 0) {
|
2020-05-04 21:43:09 +00:00
|
|
|
U32 nBitsToDecrease = BIT_highbit32((U32)totalCost) + 1;
|
2016-04-22 10:43:18 +00:00
|
|
|
for ( ; nBitsToDecrease > 1; nBitsToDecrease--) {
|
2020-05-04 21:43:09 +00:00
|
|
|
U32 const highPos = rankLast[nBitsToDecrease];
|
|
|
|
U32 const lowPos = rankLast[nBitsToDecrease-1];
|
2016-04-22 10:43:18 +00:00
|
|
|
if (highPos == noSymbol) continue;
|
|
|
|
if (lowPos == noSymbol) break;
|
|
|
|
{ U32 const highTotal = huffNode[highPos].count;
|
|
|
|
U32 const lowTotal = 2 * huffNode[lowPos].count;
|
|
|
|
if (highTotal <= lowTotal) break;
|
|
|
|
} }
|
|
|
|
/* only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !) */
|
2017-05-24 20:50:10 +00:00
|
|
|
/* HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary */
|
|
|
|
while ((nBitsToDecrease<=HUF_TABLELOG_MAX) && (rankLast[nBitsToDecrease] == noSymbol))
|
2016-04-22 10:43:18 +00:00
|
|
|
nBitsToDecrease ++;
|
|
|
|
totalCost -= 1 << (nBitsToDecrease-1);
|
|
|
|
if (rankLast[nBitsToDecrease-1] == noSymbol)
|
|
|
|
rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease]; /* this rank is no longer empty */
|
|
|
|
huffNode[rankLast[nBitsToDecrease]].nbBits ++;
|
|
|
|
if (rankLast[nBitsToDecrease] == 0) /* special case, reached largest symbol */
|
|
|
|
rankLast[nBitsToDecrease] = noSymbol;
|
|
|
|
else {
|
|
|
|
rankLast[nBitsToDecrease]--;
|
|
|
|
if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease)
|
|
|
|
rankLast[nBitsToDecrease] = noSymbol; /* this rank is now empty */
|
|
|
|
} } /* while (totalCost > 0) */
|
|
|
|
|
|
|
|
while (totalCost < 0) { /* Sometimes, cost correction overshoot */
|
|
|
|
if (rankLast[1] == noSymbol) { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */
|
|
|
|
while (huffNode[n].nbBits == maxNbBits) n--;
|
|
|
|
huffNode[n+1].nbBits--;
|
2020-05-04 21:43:09 +00:00
|
|
|
assert(n >= 0);
|
|
|
|
rankLast[1] = (U32)(n+1);
|
2016-04-22 10:43:18 +00:00
|
|
|
totalCost++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
huffNode[ rankLast[1] + 1 ].nbBits--;
|
|
|
|
rankLast[1]++;
|
|
|
|
totalCost ++;
|
|
|
|
} } } /* there are several too large elements (at least >= 2) */
|
|
|
|
|
|
|
|
return maxNbBits;
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
U32 base;
|
|
|
|
U32 current;
|
|
|
|
} rankPos;
|
|
|
|
|
2020-02-27 16:32:44 +00:00
|
|
|
typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32];
|
|
|
|
|
2020-04-01 05:46:49 +00:00
|
|
|
#define RANK_POSITION_TABLE_SIZE 32
|
|
|
|
|
2020-02-27 16:32:44 +00:00
|
|
|
typedef struct {
|
|
|
|
huffNodeTable huffNodeTbl;
|
2020-04-01 05:46:49 +00:00
|
|
|
rankPos rankPosition[RANK_POSITION_TABLE_SIZE];
|
2020-02-27 16:32:44 +00:00
|
|
|
} HUF_buildCTable_wksp_tables;
|
|
|
|
|
|
|
|
static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue, rankPos* rankPosition)
|
2016-04-22 10:43:18 +00:00
|
|
|
{
|
|
|
|
U32 n;
|
|
|
|
|
2020-04-01 05:46:49 +00:00
|
|
|
memset(rankPosition, 0, sizeof(*rankPosition) * RANK_POSITION_TABLE_SIZE);
|
2016-04-22 10:43:18 +00:00
|
|
|
for (n=0; n<=maxSymbolValue; n++) {
|
|
|
|
U32 r = BIT_highbit32(count[n] + 1);
|
2020-02-27 16:32:44 +00:00
|
|
|
rankPosition[r].base ++;
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|
2020-02-27 16:32:44 +00:00
|
|
|
for (n=30; n>0; n--) rankPosition[n-1].base += rankPosition[n].base;
|
|
|
|
for (n=0; n<32; n++) rankPosition[n].current = rankPosition[n].base;
|
2016-04-22 10:43:18 +00:00
|
|
|
for (n=0; n<=maxSymbolValue; n++) {
|
|
|
|
U32 const c = count[n];
|
|
|
|
U32 const r = BIT_highbit32(c+1) + 1;
|
2020-02-27 16:32:44 +00:00
|
|
|
U32 pos = rankPosition[r].current++;
|
|
|
|
while ((pos > rankPosition[r].base) && (c > huffNode[pos-1].count)) {
|
2018-02-22 23:44:26 +00:00
|
|
|
huffNode[pos] = huffNode[pos-1];
|
|
|
|
pos--;
|
|
|
|
}
|
2016-04-22 10:43:18 +00:00
|
|
|
huffNode[pos].count = c;
|
|
|
|
huffNode[pos].byte = (BYTE)n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-02 01:47:30 +00:00
|
|
|
/** HUF_buildCTable_wksp() :
|
|
|
|
* Same as HUF_buildCTable(), but using externally allocated scratch buffer.
|
2020-04-01 05:46:49 +00:00
|
|
|
* `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as sizeof(HUF_buildCTable_wksp_tables).
|
2016-12-02 00:13:35 +00:00
|
|
|
*/
|
2016-05-20 12:36:36 +00:00
|
|
|
#define STARTNODE (HUF_SYMBOLVALUE_MAX+1)
|
2020-02-27 16:32:44 +00:00
|
|
|
|
fix confusion between unsigned <-> U32
as suggested in #1441.
generally U32 and unsigned are the same thing,
except when they are not ...
case : 32-bit compilation for MIPS (uint32_t == unsigned long)
A vast majority of transformation consists in transforming U32 into unsigned.
In rare cases, it's the other way around (typically for internal code, such as seeds).
Among a few issues this patches solves :
- some parameters were declared with type `unsigned` in *.h,
but with type `U32` in their implementation *.c .
- some parameters have type unsigned*,
but the caller user a pointer to U32 instead.
These fixes are useful.
However, the bulk of changes is about %u formating,
which requires unsigned type,
but generally receives U32 values instead,
often just for brevity (U32 is shorter than unsigned).
These changes are generally minor, or even annoying.
As a consequence, the amount of code changed is larger than I would expect for such a patch.
Testing is also a pain :
it requires manually modifying `mem.h`,
in order to lie about `U32`
and force it to be an `unsigned long` typically.
On a 64-bit system, this will break the equivalence unsigned == U32.
Unfortunately, it will also break a few static_assert(), controlling structure sizes.
So it also requires modifying `debug.h` to make `static_assert()` a noop.
And then reverting these changes.
So it's inconvenient, and as a consequence,
this property is currently not checked during CI tests.
Therefore, these problems can emerge again in the future.
I wonder if it is worth ensuring proper distinction of U32 != unsigned in CI tests.
It's another restriction for coding, adding more frustration during merge tests,
since most platforms don't need this distinction (hence contributor will not see it),
and while this can matter in theory, the number of platforms impacted seems minimal.
Thoughts ?
2018-12-22 00:19:44 +00:00
|
|
|
size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize)
|
2016-04-22 10:43:18 +00:00
|
|
|
{
|
2020-02-27 16:32:44 +00:00
|
|
|
HUF_buildCTable_wksp_tables* const wksp_tables = (HUF_buildCTable_wksp_tables*)workSpace;
|
|
|
|
nodeElt* const huffNode0 = wksp_tables->huffNodeTbl;
|
2016-12-02 01:47:30 +00:00
|
|
|
nodeElt* const huffNode = huffNode0+1;
|
2020-05-04 21:43:09 +00:00
|
|
|
int nonNullRank;
|
2016-04-22 10:43:18 +00:00
|
|
|
int lowS, lowN;
|
2020-05-04 21:43:09 +00:00
|
|
|
int nodeNb = STARTNODE;
|
|
|
|
int n, nodeRoot;
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* safety checks */
|
2018-02-26 22:52:23 +00:00
|
|
|
if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */
|
2020-02-27 16:32:44 +00:00
|
|
|
if (wkspSize < sizeof(HUF_buildCTable_wksp_tables))
|
|
|
|
return ERROR(workSpace_tooSmall);
|
2016-05-20 12:36:36 +00:00
|
|
|
if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;
|
2020-02-27 16:32:44 +00:00
|
|
|
if (maxSymbolValue > HUF_SYMBOLVALUE_MAX)
|
|
|
|
return ERROR(maxSymbolValue_tooLarge);
|
2016-12-02 01:47:30 +00:00
|
|
|
memset(huffNode0, 0, sizeof(huffNodeTable));
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* sort, decreasing order */
|
2020-02-27 16:32:44 +00:00
|
|
|
HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition);
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* init for parents */
|
2020-05-04 21:43:09 +00:00
|
|
|
nonNullRank = (int)maxSymbolValue;
|
2016-04-22 10:43:18 +00:00
|
|
|
while(huffNode[nonNullRank].count == 0) nonNullRank--;
|
|
|
|
lowS = nonNullRank; nodeRoot = nodeNb + lowS - 1; lowN = nodeNb;
|
|
|
|
huffNode[nodeNb].count = huffNode[lowS].count + huffNode[lowS-1].count;
|
2020-05-04 21:43:09 +00:00
|
|
|
huffNode[lowS].parent = huffNode[lowS-1].parent = (U16)nodeNb;
|
2016-04-22 10:43:18 +00:00
|
|
|
nodeNb++; lowS-=2;
|
|
|
|
for (n=nodeNb; n<=nodeRoot; n++) huffNode[n].count = (U32)(1U<<30);
|
2016-12-02 01:47:30 +00:00
|
|
|
huffNode0[0].count = (U32)(1U<<31); /* fake entry, strong barrier */
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* create parents */
|
|
|
|
while (nodeNb <= nodeRoot) {
|
2020-05-04 21:43:09 +00:00
|
|
|
int const n1 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;
|
|
|
|
int const n2 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;
|
2016-04-22 10:43:18 +00:00
|
|
|
huffNode[nodeNb].count = huffNode[n1].count + huffNode[n2].count;
|
2020-05-04 21:43:09 +00:00
|
|
|
huffNode[n1].parent = huffNode[n2].parent = (U16)nodeNb;
|
2016-04-22 10:43:18 +00:00
|
|
|
nodeNb++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* distribute weights (unlimited tree height) */
|
|
|
|
huffNode[nodeRoot].nbBits = 0;
|
|
|
|
for (n=nodeRoot-1; n>=STARTNODE; n--)
|
|
|
|
huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1;
|
|
|
|
for (n=0; n<=nonNullRank; n++)
|
|
|
|
huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1;
|
|
|
|
|
|
|
|
/* enforce maxTableLog */
|
2020-05-04 21:43:09 +00:00
|
|
|
maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits);
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* fill result into tree (val, nbBits) */
|
2016-05-20 12:36:36 +00:00
|
|
|
{ U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0};
|
|
|
|
U16 valPerRank[HUF_TABLELOG_MAX+1] = {0};
|
2020-05-04 21:43:09 +00:00
|
|
|
int const alphabetSize = (int)(maxSymbolValue + 1);
|
2016-05-20 12:36:36 +00:00
|
|
|
if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */
|
2016-04-22 10:43:18 +00:00
|
|
|
for (n=0; n<=nonNullRank; n++)
|
|
|
|
nbPerRank[huffNode[n].nbBits]++;
|
|
|
|
/* determine stating value per rank */
|
|
|
|
{ U16 min = 0;
|
2020-05-04 21:43:09 +00:00
|
|
|
for (n=(int)maxNbBits; n>0; n--) {
|
2016-04-22 10:43:18 +00:00
|
|
|
valPerRank[n] = min; /* get starting value within each rank */
|
|
|
|
min += nbPerRank[n];
|
|
|
|
min >>= 1;
|
|
|
|
} }
|
2020-05-04 21:43:09 +00:00
|
|
|
for (n=0; n<alphabetSize; n++)
|
2016-04-22 10:43:18 +00:00
|
|
|
tree[huffNode[n].byte].nbBits = huffNode[n].nbBits; /* push nbBits per symbol, symbol order */
|
2020-05-04 21:43:09 +00:00
|
|
|
for (n=0; n<alphabetSize; n++)
|
2016-04-22 10:43:18 +00:00
|
|
|
tree[n].val = valPerRank[tree[n].nbBits]++; /* assign value within rank, symbol order */
|
|
|
|
}
|
|
|
|
|
|
|
|
return maxNbBits;
|
|
|
|
}
|
|
|
|
|
2016-12-02 01:47:30 +00:00
|
|
|
/** HUF_buildCTable() :
|
2018-01-11 19:16:32 +00:00
|
|
|
* @return : maxNbBits
|
2016-12-02 01:47:30 +00:00
|
|
|
* Note : count is used before tree is written, so they can safely overlap
|
|
|
|
*/
|
fix confusion between unsigned <-> U32
as suggested in #1441.
generally U32 and unsigned are the same thing,
except when they are not ...
case : 32-bit compilation for MIPS (uint32_t == unsigned long)
A vast majority of transformation consists in transforming U32 into unsigned.
In rare cases, it's the other way around (typically for internal code, such as seeds).
Among a few issues this patches solves :
- some parameters were declared with type `unsigned` in *.h,
but with type `U32` in their implementation *.c .
- some parameters have type unsigned*,
but the caller user a pointer to U32 instead.
These fixes are useful.
However, the bulk of changes is about %u formating,
which requires unsigned type,
but generally receives U32 values instead,
often just for brevity (U32 is shorter than unsigned).
These changes are generally minor, or even annoying.
As a consequence, the amount of code changed is larger than I would expect for such a patch.
Testing is also a pain :
it requires manually modifying `mem.h`,
in order to lie about `U32`
and force it to be an `unsigned long` typically.
On a 64-bit system, this will break the equivalence unsigned == U32.
Unfortunately, it will also break a few static_assert(), controlling structure sizes.
So it also requires modifying `debug.h` to make `static_assert()` a noop.
And then reverting these changes.
So it's inconvenient, and as a consequence,
this property is currently not checked during CI tests.
Therefore, these problems can emerge again in the future.
I wonder if it is worth ensuring proper distinction of U32 != unsigned in CI tests.
It's another restriction for coding, adding more frustration during merge tests,
since most platforms don't need this distinction (hence contributor will not see it),
and while this can matter in theory, the number of platforms impacted seems minimal.
Thoughts ?
2018-12-22 00:19:44 +00:00
|
|
|
size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits)
|
2016-12-02 01:47:30 +00:00
|
|
|
{
|
2020-04-01 05:46:49 +00:00
|
|
|
HUF_buildCTable_wksp_tables workspace;
|
|
|
|
return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, &workspace, sizeof(workspace));
|
2016-12-02 01:47:30 +00:00
|
|
|
}
|
|
|
|
|
2019-11-05 20:51:25 +00:00
|
|
|
size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue)
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
{
|
|
|
|
size_t nbBits = 0;
|
|
|
|
int s;
|
|
|
|
for (s = 0; s <= (int)maxSymbolValue; ++s) {
|
|
|
|
nbBits += CTable[s].nbBits * count[s];
|
|
|
|
}
|
|
|
|
return nbBits >> 3;
|
|
|
|
}
|
|
|
|
|
2020-05-01 23:11:47 +00:00
|
|
|
int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) {
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
int bad = 0;
|
|
|
|
int s;
|
|
|
|
for (s = 0; s <= (int)maxSymbolValue; ++s) {
|
|
|
|
bad |= (count[s] != 0) & (CTable[s].nbBits == 0);
|
|
|
|
}
|
|
|
|
return !bad;
|
|
|
|
}
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
size_t HUF_compressBound(size_t size) { return HUF_COMPRESSBOUND(size); }
|
|
|
|
|
2018-03-13 20:44:10 +00:00
|
|
|
FORCE_INLINE_TEMPLATE void
|
|
|
|
HUF_encodeSymbol(BIT_CStream_t* bitCPtr, U32 symbol, const HUF_CElt* CTable)
|
|
|
|
{
|
|
|
|
BIT_addBitsFast(bitCPtr, CTable[symbol].val, CTable[symbol].nbBits);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define HUF_FLUSHBITS(s) BIT_flushBits(s)
|
|
|
|
|
|
|
|
#define HUF_FLUSHBITS_1(stream) \
|
|
|
|
if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*2+7) HUF_FLUSHBITS(stream)
|
2016-04-22 10:43:18 +00:00
|
|
|
|
2018-03-13 20:44:10 +00:00
|
|
|
#define HUF_FLUSHBITS_2(stream) \
|
|
|
|
if (sizeof((stream)->bitContainer)*8 < HUF_TABLELOG_MAX*4+7) HUF_FLUSHBITS(stream)
|
|
|
|
|
|
|
|
FORCE_INLINE_TEMPLATE size_t
|
|
|
|
HUF_compress1X_usingCTable_internal_body(void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
const HUF_CElt* CTable)
|
|
|
|
{
|
|
|
|
const BYTE* ip = (const BYTE*) src;
|
|
|
|
BYTE* const ostart = (BYTE*)dst;
|
|
|
|
BYTE* const oend = ostart + dstSize;
|
|
|
|
BYTE* op = ostart;
|
|
|
|
size_t n;
|
|
|
|
BIT_CStream_t bitC;
|
|
|
|
|
|
|
|
/* init */
|
|
|
|
if (dstSize < 8) return 0; /* not enough space to compress */
|
2020-05-04 21:43:09 +00:00
|
|
|
{ size_t const initErr = BIT_initCStream(&bitC, op, (size_t)(oend-op));
|
2018-03-13 20:44:10 +00:00
|
|
|
if (HUF_isError(initErr)) return 0; }
|
|
|
|
|
|
|
|
n = srcSize & ~3; /* join to mod 4 */
|
|
|
|
switch (srcSize & 3)
|
|
|
|
{
|
|
|
|
case 3 : HUF_encodeSymbol(&bitC, ip[n+ 2], CTable);
|
|
|
|
HUF_FLUSHBITS_2(&bitC);
|
|
|
|
/* fall-through */
|
|
|
|
case 2 : HUF_encodeSymbol(&bitC, ip[n+ 1], CTable);
|
|
|
|
HUF_FLUSHBITS_1(&bitC);
|
|
|
|
/* fall-through */
|
|
|
|
case 1 : HUF_encodeSymbol(&bitC, ip[n+ 0], CTable);
|
|
|
|
HUF_FLUSHBITS(&bitC);
|
|
|
|
/* fall-through */
|
|
|
|
case 0 : /* fall-through */
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; n>0; n-=4) { /* note : n&3==0 at this stage */
|
|
|
|
HUF_encodeSymbol(&bitC, ip[n- 1], CTable);
|
|
|
|
HUF_FLUSHBITS_1(&bitC);
|
|
|
|
HUF_encodeSymbol(&bitC, ip[n- 2], CTable);
|
|
|
|
HUF_FLUSHBITS_2(&bitC);
|
|
|
|
HUF_encodeSymbol(&bitC, ip[n- 3], CTable);
|
|
|
|
HUF_FLUSHBITS_1(&bitC);
|
|
|
|
HUF_encodeSymbol(&bitC, ip[n- 4], CTable);
|
|
|
|
HUF_FLUSHBITS(&bitC);
|
|
|
|
}
|
|
|
|
|
|
|
|
return BIT_closeCStream(&bitC);
|
|
|
|
}
|
2016-04-22 10:43:18 +00:00
|
|
|
|
2018-02-15 03:20:32 +00:00
|
|
|
#if DYNAMIC_BMI2
|
2016-04-22 10:43:18 +00:00
|
|
|
|
2018-03-13 20:44:10 +00:00
|
|
|
static TARGET_ATTRIBUTE("bmi2") size_t
|
|
|
|
HUF_compress1X_usingCTable_internal_bmi2(void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
const HUF_CElt* CTable)
|
|
|
|
{
|
|
|
|
return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
|
|
|
|
}
|
2018-02-15 03:20:32 +00:00
|
|
|
|
2018-03-13 20:44:10 +00:00
|
|
|
static size_t
|
|
|
|
HUF_compress1X_usingCTable_internal_default(void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
const HUF_CElt* CTable)
|
|
|
|
{
|
|
|
|
return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
|
|
|
|
}
|
2018-02-15 03:20:32 +00:00
|
|
|
|
2018-03-13 20:44:10 +00:00
|
|
|
static size_t
|
|
|
|
HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
const HUF_CElt* CTable, const int bmi2)
|
2016-04-22 10:43:18 +00:00
|
|
|
{
|
2018-02-15 03:20:32 +00:00
|
|
|
if (bmi2) {
|
|
|
|
return HUF_compress1X_usingCTable_internal_bmi2(dst, dstSize, src, srcSize, CTable);
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|
2018-02-15 03:20:32 +00:00
|
|
|
return HUF_compress1X_usingCTable_internal_default(dst, dstSize, src, srcSize, CTable);
|
|
|
|
}
|
2016-04-22 10:43:18 +00:00
|
|
|
|
2018-03-13 20:44:10 +00:00
|
|
|
#else
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
HUF_compress1X_usingCTable_internal(void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
const HUF_CElt* CTable, const int bmi2)
|
2018-02-15 03:20:32 +00:00
|
|
|
{
|
|
|
|
(void)bmi2;
|
2018-03-13 20:44:10 +00:00
|
|
|
return HUF_compress1X_usingCTable_internal_body(dst, dstSize, src, srcSize, CTable);
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 20:44:10 +00:00
|
|
|
#endif
|
|
|
|
|
2018-02-15 03:20:32 +00:00
|
|
|
size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
|
|
|
|
{
|
|
|
|
return HUF_compress1X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
|
|
|
|
}
|
2016-04-22 10:43:18 +00:00
|
|
|
|
2018-03-13 20:44:10 +00:00
|
|
|
|
|
|
|
static size_t
|
|
|
|
HUF_compress4X_usingCTable_internal(void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
const HUF_CElt* CTable, int bmi2)
|
|
|
|
{
|
|
|
|
size_t const segmentSize = (srcSize+3)/4; /* first 3 segments */
|
|
|
|
const BYTE* ip = (const BYTE*) src;
|
|
|
|
const BYTE* const iend = ip + srcSize;
|
|
|
|
BYTE* const ostart = (BYTE*) dst;
|
|
|
|
BYTE* const oend = ostart + dstSize;
|
|
|
|
BYTE* op = ostart;
|
|
|
|
|
|
|
|
if (dstSize < 6 + 1 + 1 + 1 + 8) return 0; /* minimum space to compress successfully */
|
|
|
|
if (srcSize < 12) return 0; /* no saving possible : too small input */
|
|
|
|
op += 6; /* jumpTable */
|
|
|
|
|
2020-05-05 17:16:59 +00:00
|
|
|
assert(op <= oend);
|
|
|
|
{ CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) );
|
2018-03-13 20:44:10 +00:00
|
|
|
if (cSize==0) return 0;
|
|
|
|
assert(cSize <= 65535);
|
|
|
|
MEM_writeLE16(ostart, (U16)cSize);
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip += segmentSize;
|
2020-05-05 17:16:59 +00:00
|
|
|
assert(op <= oend);
|
|
|
|
{ CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) );
|
2018-03-13 20:44:10 +00:00
|
|
|
if (cSize==0) return 0;
|
|
|
|
assert(cSize <= 65535);
|
|
|
|
MEM_writeLE16(ostart+2, (U16)cSize);
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip += segmentSize;
|
2020-05-05 17:16:59 +00:00
|
|
|
assert(op <= oend);
|
|
|
|
{ CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, segmentSize, CTable, bmi2) );
|
2018-03-13 20:44:10 +00:00
|
|
|
if (cSize==0) return 0;
|
|
|
|
assert(cSize <= 65535);
|
|
|
|
MEM_writeLE16(ostart+4, (U16)cSize);
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip += segmentSize;
|
2020-05-05 17:16:59 +00:00
|
|
|
assert(op <= oend);
|
|
|
|
assert(ip <= iend);
|
|
|
|
{ CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, (size_t)(oend-op), ip, (size_t)(iend-ip), CTable, bmi2) );
|
2018-03-13 20:44:10 +00:00
|
|
|
if (cSize==0) return 0;
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
2020-05-05 17:16:59 +00:00
|
|
|
return (size_t)(op-ostart);
|
2018-03-13 20:44:10 +00:00
|
|
|
}
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable)
|
|
|
|
{
|
2018-02-15 03:20:32 +00:00
|
|
|
return HUF_compress4X_usingCTable_internal(dst, dstSize, src, srcSize, CTable, /* bmi2 */ 0);
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|
|
|
|
|
2018-10-26 20:21:37 +00:00
|
|
|
typedef enum { HUF_singleStream, HUF_fourStreams } HUF_nbStreams_e;
|
2018-03-13 20:44:10 +00:00
|
|
|
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
static size_t HUF_compressCTable_internal(
|
|
|
|
BYTE* const ostart, BYTE* op, BYTE* const oend,
|
|
|
|
const void* src, size_t srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
HUF_nbStreams_e nbStreams, const HUF_CElt* CTable, const int bmi2)
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
{
|
2018-10-26 20:21:37 +00:00
|
|
|
size_t const cSize = (nbStreams==HUF_singleStream) ?
|
2020-05-05 17:16:59 +00:00
|
|
|
HUF_compress1X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2) :
|
|
|
|
HUF_compress4X_usingCTable_internal(op, (size_t)(oend - op), src, srcSize, CTable, bmi2);
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
if (HUF_isError(cSize)) { return cSize; }
|
|
|
|
if (cSize==0) { return 0; } /* uncompressible */
|
|
|
|
op += cSize;
|
|
|
|
/* check compressibility */
|
2020-05-05 17:16:59 +00:00
|
|
|
assert(op >= ostart);
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
if ((size_t)(op-ostart) >= srcSize-1) { return 0; }
|
2020-05-05 17:16:59 +00:00
|
|
|
return (size_t)(op-ostart);
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
}
|
|
|
|
|
2018-02-26 22:52:23 +00:00
|
|
|
typedef struct {
|
fix confusion between unsigned <-> U32
as suggested in #1441.
generally U32 and unsigned are the same thing,
except when they are not ...
case : 32-bit compilation for MIPS (uint32_t == unsigned long)
A vast majority of transformation consists in transforming U32 into unsigned.
In rare cases, it's the other way around (typically for internal code, such as seeds).
Among a few issues this patches solves :
- some parameters were declared with type `unsigned` in *.h,
but with type `U32` in their implementation *.c .
- some parameters have type unsigned*,
but the caller user a pointer to U32 instead.
These fixes are useful.
However, the bulk of changes is about %u formating,
which requires unsigned type,
but generally receives U32 values instead,
often just for brevity (U32 is shorter than unsigned).
These changes are generally minor, or even annoying.
As a consequence, the amount of code changed is larger than I would expect for such a patch.
Testing is also a pain :
it requires manually modifying `mem.h`,
in order to lie about `U32`
and force it to be an `unsigned long` typically.
On a 64-bit system, this will break the equivalence unsigned == U32.
Unfortunately, it will also break a few static_assert(), controlling structure sizes.
So it also requires modifying `debug.h` to make `static_assert()` a noop.
And then reverting these changes.
So it's inconvenient, and as a consequence,
this property is currently not checked during CI tests.
Therefore, these problems can emerge again in the future.
I wonder if it is worth ensuring proper distinction of U32 != unsigned in CI tests.
It's another restriction for coding, adding more frustration during merge tests,
since most platforms don't need this distinction (hence contributor will not see it),
and while this can matter in theory, the number of platforms impacted seems minimal.
Thoughts ?
2018-12-22 00:19:44 +00:00
|
|
|
unsigned count[HUF_SYMBOLVALUE_MAX + 1];
|
2018-02-26 22:52:23 +00:00
|
|
|
HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1];
|
2020-04-01 05:46:49 +00:00
|
|
|
HUF_buildCTable_wksp_tables buildCTable_wksp;
|
2018-02-26 22:52:23 +00:00
|
|
|
} HUF_compress_tables_t;
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
|
2018-02-26 22:52:23 +00:00
|
|
|
/* HUF_compress_internal() :
|
|
|
|
* `workSpace` must a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
|
2018-10-26 20:21:37 +00:00
|
|
|
static size_t
|
|
|
|
HUF_compress_internal (void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
unsigned maxSymbolValue, unsigned huffLog,
|
|
|
|
HUF_nbStreams_e nbStreams,
|
|
|
|
void* workSpace, size_t wkspSize,
|
|
|
|
HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat,
|
|
|
|
const int bmi2)
|
2016-04-22 10:43:18 +00:00
|
|
|
{
|
2018-02-26 22:52:23 +00:00
|
|
|
HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace;
|
2016-04-22 10:43:18 +00:00
|
|
|
BYTE* const ostart = (BYTE*)dst;
|
|
|
|
BYTE* const oend = ostart + dstSize;
|
|
|
|
BYTE* op = ostart;
|
|
|
|
|
2020-04-01 05:46:49 +00:00
|
|
|
HUF_STATIC_ASSERT(sizeof(*table) <= HUF_WORKSPACE_SIZE);
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
/* checks & inits */
|
2018-02-26 22:52:23 +00:00
|
|
|
if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC); /* must be aligned on 4-bytes boundaries */
|
2018-10-26 20:21:37 +00:00
|
|
|
if (wkspSize < HUF_WORKSPACE_SIZE) return ERROR(workSpace_tooSmall);
|
2018-02-26 22:52:23 +00:00
|
|
|
if (!srcSize) return 0; /* Uncompressed */
|
|
|
|
if (!dstSize) return 0; /* cannot fit anything within dst budget */
|
2016-05-20 12:36:36 +00:00
|
|
|
if (srcSize > HUF_BLOCKSIZE_MAX) return ERROR(srcSize_wrong); /* current block size limit */
|
|
|
|
if (huffLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
|
2018-02-26 22:52:23 +00:00
|
|
|
if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);
|
2016-05-20 12:36:36 +00:00
|
|
|
if (!maxSymbolValue) maxSymbolValue = HUF_SYMBOLVALUE_MAX;
|
|
|
|
if (!huffLog) huffLog = HUF_TABLELOG_DEFAULT;
|
2016-04-22 10:43:18 +00:00
|
|
|
|
2018-02-26 22:52:23 +00:00
|
|
|
/* Heuristic : If old table is valid, use it for small inputs */
|
2017-03-03 20:30:24 +00:00
|
|
|
if (preferRepeat && repeat && *repeat == HUF_repeat_valid) {
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compressCTable_internal(ostart, op, oend,
|
|
|
|
src, srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
nbStreams, oldHufTable, bmi2);
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
}
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
/* Scan input and build symbol stats */
|
2018-10-26 20:21:37 +00:00
|
|
|
{ CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, workSpace, wkspSize) );
|
2016-08-30 13:51:00 +00:00
|
|
|
if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */
|
2018-06-22 01:32:38 +00:00
|
|
|
if (largest <= (srcSize >> 7)+4) return 0; /* heuristic : probably not compressible enough */
|
2016-05-20 12:36:36 +00:00
|
|
|
}
|
2016-04-22 10:43:18 +00:00
|
|
|
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
/* Check validity of previous table */
|
2018-02-26 22:52:23 +00:00
|
|
|
if ( repeat
|
|
|
|
&& *repeat == HUF_repeat_check
|
|
|
|
&& !HUF_validateCTable(oldHufTable, table->count, maxSymbolValue)) {
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
*repeat = HUF_repeat_none;
|
|
|
|
}
|
|
|
|
/* Heuristic : use existing table for small inputs */
|
2017-03-03 20:30:24 +00:00
|
|
|
if (preferRepeat && repeat && *repeat != HUF_repeat_none) {
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compressCTable_internal(ostart, op, oend,
|
|
|
|
src, srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
nbStreams, oldHufTable, bmi2);
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
}
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
/* Build Huffman Tree */
|
2016-05-20 12:36:36 +00:00
|
|
|
huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
|
fix confusion between unsigned <-> U32
as suggested in #1441.
generally U32 and unsigned are the same thing,
except when they are not ...
case : 32-bit compilation for MIPS (uint32_t == unsigned long)
A vast majority of transformation consists in transforming U32 into unsigned.
In rare cases, it's the other way around (typically for internal code, such as seeds).
Among a few issues this patches solves :
- some parameters were declared with type `unsigned` in *.h,
but with type `U32` in their implementation *.c .
- some parameters have type unsigned*,
but the caller user a pointer to U32 instead.
These fixes are useful.
However, the bulk of changes is about %u formating,
which requires unsigned type,
but generally receives U32 values instead,
often just for brevity (U32 is shorter than unsigned).
These changes are generally minor, or even annoying.
As a consequence, the amount of code changed is larger than I would expect for such a patch.
Testing is also a pain :
it requires manually modifying `mem.h`,
in order to lie about `U32`
and force it to be an `unsigned long` typically.
On a 64-bit system, this will break the equivalence unsigned == U32.
Unfortunately, it will also break a few static_assert(), controlling structure sizes.
So it also requires modifying `debug.h` to make `static_assert()` a noop.
And then reverting these changes.
So it's inconvenient, and as a consequence,
this property is currently not checked during CI tests.
Therefore, these problems can emerge again in the future.
I wonder if it is worth ensuring proper distinction of U32 != unsigned in CI tests.
It's another restriction for coding, adding more frustration during merge tests,
since most platforms don't need this distinction (hence contributor will not see it),
and while this can matter in theory, the number of platforms impacted seems minimal.
Thoughts ?
2018-12-22 00:19:44 +00:00
|
|
|
{ size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count,
|
|
|
|
maxSymbolValue, huffLog,
|
2020-04-01 05:46:49 +00:00
|
|
|
&table->buildCTable_wksp, sizeof(table->buildCTable_wksp));
|
fix confusion between unsigned <-> U32
as suggested in #1441.
generally U32 and unsigned are the same thing,
except when they are not ...
case : 32-bit compilation for MIPS (uint32_t == unsigned long)
A vast majority of transformation consists in transforming U32 into unsigned.
In rare cases, it's the other way around (typically for internal code, such as seeds).
Among a few issues this patches solves :
- some parameters were declared with type `unsigned` in *.h,
but with type `U32` in their implementation *.c .
- some parameters have type unsigned*,
but the caller user a pointer to U32 instead.
These fixes are useful.
However, the bulk of changes is about %u formating,
which requires unsigned type,
but generally receives U32 values instead,
often just for brevity (U32 is shorter than unsigned).
These changes are generally minor, or even annoying.
As a consequence, the amount of code changed is larger than I would expect for such a patch.
Testing is also a pain :
it requires manually modifying `mem.h`,
in order to lie about `U32`
and force it to be an `unsigned long` typically.
On a 64-bit system, this will break the equivalence unsigned == U32.
Unfortunately, it will also break a few static_assert(), controlling structure sizes.
So it also requires modifying `debug.h` to make `static_assert()` a noop.
And then reverting these changes.
So it's inconvenient, and as a consequence,
this property is currently not checked during CI tests.
Therefore, these problems can emerge again in the future.
I wonder if it is worth ensuring proper distinction of U32 != unsigned in CI tests.
It's another restriction for coding, adding more frustration during merge tests,
since most platforms don't need this distinction (hence contributor will not see it),
and while this can matter in theory, the number of platforms impacted seems minimal.
Thoughts ?
2018-12-22 00:19:44 +00:00
|
|
|
CHECK_F(maxBits);
|
2016-05-20 12:36:36 +00:00
|
|
|
huffLog = (U32)maxBits;
|
2018-02-26 22:52:23 +00:00
|
|
|
/* Zero unused symbols in CTable, so we can check it for validity */
|
|
|
|
memset(table->CTable + (maxSymbolValue + 1), 0,
|
|
|
|
sizeof(table->CTable) - ((maxSymbolValue + 1) * sizeof(HUF_CElt)));
|
2016-05-20 12:36:36 +00:00
|
|
|
}
|
2016-04-22 10:43:18 +00:00
|
|
|
|
|
|
|
/* Write table description header */
|
2018-02-26 22:52:23 +00:00
|
|
|
{ CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, table->CTable, maxSymbolValue, huffLog) );
|
|
|
|
/* Check if using previous huffman table is beneficial */
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
if (repeat && *repeat != HUF_repeat_none) {
|
2018-02-26 22:52:23 +00:00
|
|
|
size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, table->count, maxSymbolValue);
|
|
|
|
size_t const newSize = HUF_estimateCompressedSize(table->CTable, table->count, maxSymbolValue);
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
if (oldSize <= hSize + newSize || hSize + 12 >= srcSize) {
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compressCTable_internal(ostart, op, oend,
|
|
|
|
src, srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
nbStreams, oldHufTable, bmi2);
|
2018-02-26 22:52:23 +00:00
|
|
|
} }
|
|
|
|
|
|
|
|
/* Use the new huffman table */
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
if (hSize + 12ul >= srcSize) { return 0; }
|
2016-05-20 12:36:36 +00:00
|
|
|
op += hSize;
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
if (repeat) { *repeat = HUF_repeat_none; }
|
2018-02-26 22:52:23 +00:00
|
|
|
if (oldHufTable)
|
|
|
|
memcpy(oldHufTable, table->CTable, sizeof(table->CTable)); /* Save new table */
|
2016-05-20 12:36:36 +00:00
|
|
|
}
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compressCTable_internal(ostart, op, oend,
|
|
|
|
src, srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
nbStreams, table->CTable, bmi2);
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-02 00:13:35 +00:00
|
|
|
size_t HUF_compress1X_wksp (void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
2016-12-02 01:47:30 +00:00
|
|
|
unsigned maxSymbolValue, unsigned huffLog,
|
|
|
|
void* workSpace, size_t wkspSize)
|
2016-12-02 00:13:35 +00:00
|
|
|
{
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compress_internal(dst, dstSize, src, srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
maxSymbolValue, huffLog, HUF_singleStream,
|
2018-02-26 22:52:23 +00:00
|
|
|
workSpace, wkspSize,
|
|
|
|
NULL, NULL, 0, 0 /*bmi2*/);
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t HUF_compress1X_repeat (void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
unsigned maxSymbolValue, unsigned huffLog,
|
|
|
|
void* workSpace, size_t wkspSize,
|
2018-02-15 03:20:32 +00:00
|
|
|
HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2)
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
{
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compress_internal(dst, dstSize, src, srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
maxSymbolValue, huffLog, HUF_singleStream,
|
2018-02-26 22:52:23 +00:00
|
|
|
workSpace, wkspSize, hufTable,
|
|
|
|
repeat, preferRepeat, bmi2);
|
2016-12-02 00:13:35 +00:00
|
|
|
}
|
|
|
|
|
2016-04-22 10:43:18 +00:00
|
|
|
size_t HUF_compress1X (void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
unsigned maxSymbolValue, unsigned huffLog)
|
|
|
|
{
|
2018-02-21 19:34:49 +00:00
|
|
|
unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
|
2016-12-02 01:47:30 +00:00
|
|
|
return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
|
2016-12-02 00:13:35 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 19:34:49 +00:00
|
|
|
/* HUF_compress4X_repeat():
|
|
|
|
* compress input using 4 streams.
|
|
|
|
* provide workspace to generate compression tables */
|
2016-12-02 00:13:35 +00:00
|
|
|
size_t HUF_compress4X_wksp (void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
2016-12-02 01:47:30 +00:00
|
|
|
unsigned maxSymbolValue, unsigned huffLog,
|
|
|
|
void* workSpace, size_t wkspSize)
|
2016-12-02 00:13:35 +00:00
|
|
|
{
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compress_internal(dst, dstSize, src, srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
maxSymbolValue, huffLog, HUF_fourStreams,
|
2018-02-26 22:52:23 +00:00
|
|
|
workSpace, wkspSize,
|
|
|
|
NULL, NULL, 0, 0 /*bmi2*/);
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 19:34:49 +00:00
|
|
|
/* HUF_compress4X_repeat():
|
|
|
|
* compress input using 4 streams.
|
|
|
|
* re-use an existing huffman compression table */
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
size_t HUF_compress4X_repeat (void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
unsigned maxSymbolValue, unsigned huffLog,
|
|
|
|
void* workSpace, size_t wkspSize,
|
2018-02-15 03:20:32 +00:00
|
|
|
HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2)
|
Allow compressor to repeat Huffman tables
* Compressor saves most recently used Huffman table and reuses it
if it produces better results.
* I attempted to preserve CPU usage profile.
I intentionally left all of the existing heuristics in place.
There is only a speed difference on the second block and later.
When compressing large enough blocks (say >= 4 KiB) there is
no significant difference in compression speed.
Dictionary compression of one block is the same speed for blocks
with literals <= 1 KiB, and after that the difference is not
very significant.
* In the synthetic data, with blocks 10 KB or smaller, most blocks
can't use repeated tables because the previous block did not
contain a symbol that the current block contains.
Once blocks are about 12 KB or more, most previous blocks have
valid Huffman tables for the current block, and the compression
ratio and decompression speed jumped.
* In silesia blocks as small as 4KB can frequently reuse the
previous Huffman table (85%), but it isn't as profitable, and
the previous Huffman table only gets used about 3% of the time.
* Microbenchmarks show that `HUF_validateCTable()` takes ~55 ns
and `HUF_estimateCompressedSize()` takes ~35 ns.
They are decently well optimized, the first versions took 90 ns
and 120 ns respectively. `HUF_validateCTable()` could be twice as
fast, if we cast the `HUF_CElt*` to a `U32*` and compare to 0.
However, `U32` has an alignment of 4 instead of 2, so I think that
might be undefined behavior.
* I've ran `zstreamtest` compiled normally, with UASAN and with MSAN
for 4 hours each.
The worst case for the speed difference is a bunch of small blocks
in the same frame. I modified `bench.c` to compress the input in a
single frame but with blocks of the given block size, set by `-B`.
Benchmarks on level 1:
| Program | Block size | Corpus | Ratio | Compression MB/s | Decompression MB/s |
|-----------|------------|-----------|-------|------------------|--------------------|
| zstd.base | 256 | synthetic | 2.364 | 110.0 | 297.0 |
| zstd | 256 | synthetic | 2.367 | 108.9 | 297.0 |
| zstd.base | 256 | silesia | 2.204 | 93.8 | 415.7 |
| zstd | 256 | silesia | 2.204 | 93.4 | 415.7 |
| zstd.base | 512 | synthetic | 2.594 | 144.2 | 420.0 |
| zstd | 512 | synthetic | 2.599 | 141.5 | 425.7 |
| zstd.base | 512 | silesia | 2.358 | 118.4 | 432.6 |
| zstd | 512 | silesia | 2.358 | 119.8 | 432.6 |
| zstd.base | 1024 | synthetic | 2.790 | 192.3 | 594.1 |
| zstd | 1024 | synthetic | 2.794 | 192.3 | 600.0 |
| zstd.base | 1024 | silesia | 2.524 | 148.2 | 464.2 |
| zstd | 1024 | silesia | 2.525 | 148.2 | 467.6 |
| zstd.base | 4096 | synthetic | 3.023 | 300.0 | 1000.0 |
| zstd | 4096 | synthetic | 3.024 | 300.0 | 1010.1 |
| zstd.base | 4096 | silesia | 2.779 | 223.1 | 623.5 |
| zstd | 4096 | silesia | 2.779 | 223.1 | 636.0 |
| zstd.base | 16384 | synthetic | 3.131 | 350.0 | 1150.1 |
| zstd | 16384 | synthetic | 3.152 | 350.0 | 1630.3 |
| zstd.base | 16384 | silesia | 2.871 | 296.5 | 883.3 |
| zstd | 16384 | silesia | 2.872 | 294.4 | 898.3 |
2017-03-02 01:51:56 +00:00
|
|
|
{
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compress_internal(dst, dstSize, src, srcSize,
|
2018-10-26 20:21:37 +00:00
|
|
|
maxSymbolValue, huffLog, HUF_fourStreams,
|
2018-02-26 22:52:23 +00:00
|
|
|
workSpace, wkspSize,
|
|
|
|
hufTable, repeat, preferRepeat, bmi2);
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t HUF_compress2 (void* dst, size_t dstSize,
|
|
|
|
const void* src, size_t srcSize,
|
|
|
|
unsigned maxSymbolValue, unsigned huffLog)
|
|
|
|
{
|
2018-02-21 19:34:49 +00:00
|
|
|
unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
|
2016-12-02 01:47:30 +00:00
|
|
|
return HUF_compress4X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t HUF_compress (void* dst, size_t maxDstSize, const void* src, size_t srcSize)
|
|
|
|
{
|
2018-02-26 22:52:23 +00:00
|
|
|
return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT);
|
2016-04-22 10:43:18 +00:00
|
|
|
}
|