2016-04-22 10:43:18 +00:00
|
|
|
/* ******************************************************************
|
|
|
|
Huffman encoder, part of New Generation Entropy library
|
|
|
|
Copyright (C) 2013-2016, Yann Collet.
|
|
|
|
|
|
|
|
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions are
|
|
|
|
met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above
|
|
|
|
copyright notice, this list of conditions and the following disclaimer
|
|
|
|
in the documentation and/or other materials provided with the
|
|
|
|
distribution.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
You can contact the author at :
|
|
|
|
- FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
|
|
|
- Public forum : https://groups.google.com/forum/#!forum/lz4c
|
|
|
|
****************************************************************** */
|
|
|
|
|
|
|
|
/* **************************************************************
|
|
|
|
* 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) */
|
2018-02-15 03:20:32 +00:00
|
|
|
#include "compiler.h"
|
2018-06-13 23:49:31 +00:00
|
|
|
#include "bitstream.h"
|
|
|
|
#include "hist.h"
|
2016-06-04 22:58:01 +00:00
|
|
|
#define FSE_STATIC_LINKING_ONLY /* FSE_optimalTableLog_internal */
|
|
|
|
#include "fse.h" /* header compression */
|
2016-06-04 22:42:28 +00:00
|
|
|
#define HUF_STATIC_LINKING_ONLY
|
|
|
|
#include "huf.h"
|
2017-08-15 18:23:28 +00:00
|
|
|
#include "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 */
|
2017-07-25 19:52:01 +00:00
|
|
|
#define CHECK_V_F(e, f) size_t const e = f; if (ERR_isError(e)) return e
|
2016-12-02 00:24:04 +00:00
|
|
|
#define CHECK_F(f) { CHECK_V_F(_var_err__, f); }
|
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 */
|
2016-12-02 00:24:04 +00:00
|
|
|
{ CHECK_V_F(hSize, FSE_writeNCount(op, 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)) );
|
2016-12-02 00:24:04 +00:00
|
|
|
{ CHECK_V_F(cSize, FSE_compress_usingCTable(op, 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return op-ostart;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize)
|
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 */
|
2016-07-24 12:26:11 +00:00
|
|
|
{ U32 n; for (n=0; n<nbSymbols; n++) {
|
|
|
|
const U32 w = huffWeight[n];
|
|
|
|
CTable[n].nbBits = (BYTE)(tableLog + 1 - w);
|
|
|
|
} }
|
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);
|
|
|
|
U32 n = lastNonNull;
|
|
|
|
|
|
|
|
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
|
|
|
int pos;
|
|
|
|
|
|
|
|
/* Get pos of last (smallest) symbol per rank */
|
|
|
|
memset(rankLast, 0xF0, sizeof(rankLast));
|
|
|
|
{ U32 currentNbBits = maxNbBits;
|
|
|
|
for (pos=n ; pos >= 0; pos--) {
|
|
|
|
if (huffNode[pos].nbBits >= currentNbBits) continue;
|
|
|
|
currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */
|
|
|
|
rankLast[maxNbBits-currentNbBits] = pos;
|
|
|
|
} }
|
|
|
|
|
|
|
|
while (totalCost > 0) {
|
|
|
|
U32 nBitsToDecrease = BIT_highbit32(totalCost) + 1;
|
|
|
|
for ( ; nBitsToDecrease > 1; nBitsToDecrease--) {
|
|
|
|
U32 highPos = rankLast[nBitsToDecrease];
|
|
|
|
U32 lowPos = rankLast[nBitsToDecrease-1];
|
|
|
|
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--;
|
|
|
|
rankLast[1] = n+1;
|
|
|
|
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;
|
|
|
|
|
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
|
|
|
static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue)
|
2016-04-22 10:43:18 +00:00
|
|
|
{
|
|
|
|
rankPos rank[32];
|
|
|
|
U32 n;
|
|
|
|
|
|
|
|
memset(rank, 0, sizeof(rank));
|
|
|
|
for (n=0; n<=maxSymbolValue; n++) {
|
|
|
|
U32 r = BIT_highbit32(count[n] + 1);
|
|
|
|
rank[r].base ++;
|
|
|
|
}
|
|
|
|
for (n=30; n>0; n--) rank[n-1].base += rank[n].base;
|
|
|
|
for (n=0; n<32; n++) rank[n].current = rank[n].base;
|
|
|
|
for (n=0; n<=maxSymbolValue; n++) {
|
|
|
|
U32 const c = count[n];
|
|
|
|
U32 const r = BIT_highbit32(c+1) + 1;
|
|
|
|
U32 pos = rank[r].current++;
|
2018-02-22 23:44:26 +00:00
|
|
|
while ((pos > rank[r].base) && (c > huffNode[pos-1].count)) {
|
|
|
|
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.
|
2018-02-26 22:52:23 +00:00
|
|
|
* `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of HUF_CTABLE_WORKSPACE_SIZE_U32 unsigned.
|
2016-12-02 00:13:35 +00:00
|
|
|
*/
|
2016-05-20 12:36:36 +00:00
|
|
|
#define STARTNODE (HUF_SYMBOLVALUE_MAX+1)
|
2018-02-26 22:52:23 +00:00
|
|
|
typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32];
|
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
|
|
|
{
|
2016-12-02 01:47:30 +00:00
|
|
|
nodeElt* const huffNode0 = (nodeElt*)workSpace;
|
|
|
|
nodeElt* const huffNode = huffNode0+1;
|
2016-04-22 10:43:18 +00:00
|
|
|
U32 n, nonNullRank;
|
|
|
|
int lowS, lowN;
|
|
|
|
U16 nodeNb = STARTNODE;
|
|
|
|
U32 nodeRoot;
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
if (wkspSize < sizeof(huffNodeTable)) return ERROR(workSpace_tooSmall);
|
2016-05-20 12:36:36 +00:00
|
|
|
if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;
|
2018-02-26 22:52:23 +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 */
|
|
|
|
HUF_sort(huffNode, count, maxSymbolValue);
|
|
|
|
|
|
|
|
/* init for parents */
|
|
|
|
nonNullRank = maxSymbolValue;
|
|
|
|
while(huffNode[nonNullRank].count == 0) nonNullRank--;
|
|
|
|
lowS = nonNullRank; nodeRoot = nodeNb + lowS - 1; lowN = nodeNb;
|
|
|
|
huffNode[nodeNb].count = huffNode[lowS].count + huffNode[lowS-1].count;
|
|
|
|
huffNode[lowS].parent = huffNode[lowS-1].parent = nodeNb;
|
|
|
|
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) {
|
|
|
|
U32 n1 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;
|
|
|
|
U32 n2 = (huffNode[lowS].count < huffNode[lowN].count) ? lowS-- : lowN++;
|
|
|
|
huffNode[nodeNb].count = huffNode[n1].count + huffNode[n2].count;
|
|
|
|
huffNode[n1].parent = huffNode[n2].parent = nodeNb;
|
|
|
|
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 */
|
|
|
|
maxNbBits = HUF_setMaxHeight(huffNode, nonNullRank, maxNbBits);
|
|
|
|
|
|
|
|
/* 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};
|
|
|
|
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;
|
|
|
|
for (n=maxNbBits; n>0; n--) {
|
|
|
|
valPerRank[n] = min; /* get starting value within each rank */
|
|
|
|
min += nbPerRank[n];
|
|
|
|
min >>= 1;
|
|
|
|
} }
|
|
|
|
for (n=0; n<=maxSymbolValue; n++)
|
|
|
|
tree[huffNode[n].byte].nbBits = huffNode[n].nbBits; /* push nbBits per symbol, symbol order */
|
|
|
|
for (n=0; n<=maxSymbolValue; n++)
|
|
|
|
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
|
|
|
{
|
|
|
|
huffNodeTable nodeTable;
|
|
|
|
return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, nodeTable, sizeof(nodeTable));
|
|
|
|
}
|
|
|
|
|
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_estimateCompressedSize(HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue)
|
|
|
|
{
|
|
|
|
size_t nbBits = 0;
|
|
|
|
int s;
|
|
|
|
for (s = 0; s <= (int)maxSymbolValue; ++s) {
|
|
|
|
nbBits += CTable[s].nbBits * count[s];
|
|
|
|
}
|
|
|
|
return nbBits >> 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue) {
|
|
|
|
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 */
|
|
|
|
{ size_t const initErr = BIT_initCStream(&bitC, op, oend-op);
|
|
|
|
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 */
|
|
|
|
|
|
|
|
{ CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
|
|
|
|
if (cSize==0) return 0;
|
|
|
|
assert(cSize <= 65535);
|
|
|
|
MEM_writeLE16(ostart, (U16)cSize);
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip += segmentSize;
|
|
|
|
{ CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
|
|
|
|
if (cSize==0) return 0;
|
|
|
|
assert(cSize <= 65535);
|
|
|
|
MEM_writeLE16(ostart+2, (U16)cSize);
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip += segmentSize;
|
|
|
|
{ CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, segmentSize, CTable, bmi2) );
|
|
|
|
if (cSize==0) return 0;
|
|
|
|
assert(cSize <= 65535);
|
|
|
|
MEM_writeLE16(ostart+4, (U16)cSize);
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip += segmentSize;
|
|
|
|
{ CHECK_V_F(cSize, HUF_compress1X_usingCTable_internal(op, oend-op, ip, iend-ip, CTable, bmi2) );
|
|
|
|
if (cSize==0) return 0;
|
|
|
|
op += cSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
return op-ostart;
|
|
|
|
}
|
|
|
|
|
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) ?
|
2018-02-15 03:20:32 +00:00
|
|
|
HUF_compress1X_usingCTable_internal(op, oend - op, src, srcSize, CTable, bmi2) :
|
|
|
|
HUF_compress4X_usingCTable_internal(op, 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 */
|
|
|
|
if ((size_t)(op-ostart) >= srcSize-1) { return 0; }
|
|
|
|
return op-ostart;
|
|
|
|
}
|
|
|
|
|
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];
|
|
|
|
huffNodeTable nodeTable;
|
|
|
|
} 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;
|
|
|
|
|
|
|
|
/* 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,
|
|
|
|
table->nodeTable, sizeof(table->nodeTable));
|
|
|
|
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
|
|
|
}
|