mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-11 10:25:47 +02:00
chore: introduce DEBUG COMPRESSION (#4620)
This function analyzes the compressability of the keys using a single huffman tree. For example, ``` >debug POPULATE 1000000 keyabcdef 10 OK > debug compression 1) max_symbol 2) (integer) 121 3) max_bits 4) (integer) 5 5) raw_size 6) (integer) 7861817 7) compressed_size 8) (integer) 4372270 9) ratio 10) "0.5561398847111297" ``` Signed-off-by: Roman Gershman <roman@dragonflydb.io>
This commit is contained in:
parent
afad75f17b
commit
77b9a8f699
12 changed files with 539 additions and 13 deletions
|
@ -2,6 +2,7 @@ default_stages: [commit]
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)(
|
(?x)(
|
||||||
src/redis/.* |
|
src/redis/.* |
|
||||||
|
src/huff/.* |
|
||||||
contrib/charts/dragonfly/ci/.* |
|
contrib/charts/dragonfly/ci/.* |
|
||||||
patches/.*
|
patches/.*
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,22 +15,22 @@ but you can also run Dragonfly on older kernels as well.
|
||||||
On Debian/Ubuntu:
|
On Debian/Ubuntu:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install ninja-build libunwind-dev libboost-fiber-dev libssl-dev \
|
sudo apt install ninja-build libunwind-dev libboost-context-dev libssl-dev \
|
||||||
autoconf-archive libtool cmake g++ libzstd-dev bison libxml2-dev zlib1g-dev
|
autoconf-archive libtool cmake g++ bison zlib1g-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
On Fedora:
|
On Fedora:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install -y automake boost-devel g++ git cmake libtool ninja-build libzstd-devel \
|
sudo dnf install -y automake boost-devel g++ git cmake libtool ninja-build \
|
||||||
openssl-devel libunwind-devel autoconf-archive patch bison libxml2-devel libstdc++-static
|
openssl-devel libunwind-devel autoconf-archive patch bison libstdc++-static
|
||||||
```
|
```
|
||||||
|
|
||||||
On openSUSE:
|
On openSUSE:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo zypper install automake boost-devel gcc-c++ git cmake libtool ninja libzstd-devel \
|
sudo zypper install automake boost-devel gcc-c++ git cmake libtool ninja \
|
||||||
openssl-devel libunwind-devel autoconf-archive patch bison libxml2-devel \
|
openssl-devel libunwind-devel autoconf-archive patch bison \
|
||||||
libboost_context-devel libboost_system-devel
|
libboost_context-devel libboost_system-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
2
helio
2
helio
|
@ -1 +1 @@
|
||||||
Subproject commit 875cedd2a0cd084fd15a3d5dbfe20150e19ffcef
|
Subproject commit 6e29a5af38cb9e99be37469a87c6faac7114f55b
|
|
@ -34,8 +34,6 @@ cxx_link(dash_bench dfly_core redis_test_lib)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
find_library(ZSTD_LIB NAMES libzstd.a libzstdstatic.a zstd NAMES_PER_DIR REQUIRED)
|
|
||||||
|
|
||||||
cxx_test(dfly_core_test dfly_core TRDP::fast_float ${PCRE2_LIB} ${RE2_LIB} LABELS DFLY)
|
cxx_test(dfly_core_test dfly_core TRDP::fast_float ${PCRE2_LIB} ${RE2_LIB} LABELS DFLY)
|
||||||
cxx_test(compact_object_test dfly_core LABELS DFLY)
|
cxx_test(compact_object_test dfly_core LABELS DFLY)
|
||||||
cxx_test(extent_tree_test dfly_core LABELS DFLY)
|
cxx_test(extent_tree_test dfly_core LABELS DFLY)
|
||||||
|
@ -51,7 +49,7 @@ cxx_test(flatbuffers_test dfly_core TRDP::flatbuffers LABELS DFLY)
|
||||||
cxx_test(bloom_test dfly_core LABELS DFLY)
|
cxx_test(bloom_test dfly_core LABELS DFLY)
|
||||||
cxx_test(allocation_tracker_test dfly_core absl::random_random LABELS DFLY)
|
cxx_test(allocation_tracker_test dfly_core absl::random_random LABELS DFLY)
|
||||||
cxx_test(qlist_test dfly_core DATA testdata/list.txt.zst LABELS DFLY)
|
cxx_test(qlist_test dfly_core DATA testdata/list.txt.zst LABELS DFLY)
|
||||||
cxx_test(zstd_test dfly_core ${ZSTD_LIB} LABELS DFLY)
|
cxx_test(zstd_test dfly_core TRDP::zstd LABELS DFLY)
|
||||||
cxx_test(top_keys_test dfly_core LABELS DFLY)
|
cxx_test(top_keys_test dfly_core LABELS DFLY)
|
||||||
|
|
||||||
if(LIB_PCRE2)
|
if(LIB_PCRE2)
|
||||||
|
|
30
src/huff/LICENSE
Normal file
30
src/huff/LICENSE
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
BSD License
|
||||||
|
|
||||||
|
For Zstandard software
|
||||||
|
|
||||||
|
Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
* Neither the name Facebook, nor Meta, nor the names of its contributors may
|
||||||
|
be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
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 HOLDER 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.
|
4
src/huff/README.md
Normal file
4
src/huff/README.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
The code in this folder exposes internal functions that are used by ZSTD.
|
||||||
|
These functions are part of https://github.com/Cyan4973/FiniteStateEntropy project.
|
||||||
|
|
||||||
|
Since we already link to ZSTD, it is convenient that we get this functionality for free.
|
82
src/huff/hist.h
Normal file
82
src/huff/hist.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/* ******************************************************************
|
||||||
|
* hist : Histogram functions
|
||||||
|
* part of Finite State Entropy project
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* You can contact the author at :
|
||||||
|
* - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||||
|
* - Public forum : https://groups.google.com/forum/#!forum/lz4c
|
||||||
|
*
|
||||||
|
* This source code is licensed under both the BSD-style license (found in the
|
||||||
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
||||||
|
* in the COPYING file in the root directory of this source tree).
|
||||||
|
* You may select, at your option, one of the above-listed licenses.
|
||||||
|
****************************************************************** */
|
||||||
|
|
||||||
|
/* --- dependencies --- */
|
||||||
|
#include <stddef.h> /* size_t */
|
||||||
|
|
||||||
|
|
||||||
|
/* --- simple histogram functions --- */
|
||||||
|
|
||||||
|
/*! HIST_count():
|
||||||
|
* Provides the precise count of each byte within a table 'count'.
|
||||||
|
* 'count' is a table of unsigned int, of minimum size (*maxSymbolValuePtr+1).
|
||||||
|
* Updates *maxSymbolValuePtr with actual largest symbol value detected.
|
||||||
|
* @return : count of the most frequent symbol (which isn't identified).
|
||||||
|
* or an error code, which can be tested using HIST_isError().
|
||||||
|
* note : if return == srcSize, there is only one symbol.
|
||||||
|
*/
|
||||||
|
size_t HIST_count(unsigned* count, unsigned* maxSymbolValuePtr,
|
||||||
|
const void* src, size_t srcSize);
|
||||||
|
|
||||||
|
unsigned HIST_isError(size_t code); /**< tells if a return value is an error code */
|
||||||
|
|
||||||
|
|
||||||
|
/* --- advanced histogram functions --- */
|
||||||
|
|
||||||
|
#define HIST_WKSP_SIZE_U32 1024
|
||||||
|
#define HIST_WKSP_SIZE (HIST_WKSP_SIZE_U32 * sizeof(unsigned))
|
||||||
|
/** HIST_count_wksp() :
|
||||||
|
* Same as HIST_count(), but using an externally provided scratch buffer.
|
||||||
|
* Benefit is this function will use very little stack space.
|
||||||
|
* `workSpace` is a writable buffer which must be 4-bytes aligned,
|
||||||
|
* `workSpaceSize` must be >= HIST_WKSP_SIZE
|
||||||
|
*/
|
||||||
|
size_t HIST_count_wksp(unsigned* count, unsigned* maxSymbolValuePtr,
|
||||||
|
const void* src, size_t srcSize,
|
||||||
|
void* workSpace, size_t workSpaceSize);
|
||||||
|
|
||||||
|
/** HIST_countFast() :
|
||||||
|
* same as HIST_count(), but blindly trusts that all byte values within src are <= *maxSymbolValuePtr.
|
||||||
|
* This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr`
|
||||||
|
*/
|
||||||
|
size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr,
|
||||||
|
const void* src, size_t srcSize);
|
||||||
|
|
||||||
|
/** HIST_countFast_wksp() :
|
||||||
|
* Same as HIST_countFast(), but using an externally provided scratch buffer.
|
||||||
|
* `workSpace` is a writable buffer which must be 4-bytes aligned,
|
||||||
|
* `workSpaceSize` must be >= HIST_WKSP_SIZE
|
||||||
|
*/
|
||||||
|
size_t HIST_countFast_wksp(unsigned* count, unsigned* maxSymbolValuePtr,
|
||||||
|
const void* src, size_t srcSize,
|
||||||
|
void* workSpace, size_t workSpaceSize);
|
||||||
|
|
||||||
|
/*! HIST_count_simple() :
|
||||||
|
* Same as HIST_countFast(), this function is unsafe,
|
||||||
|
* and will segfault if any value within `src` is `> *maxSymbolValuePtr`.
|
||||||
|
* It is also a bit slower for large inputs.
|
||||||
|
* However, it does not need any additional memory (not even on stack).
|
||||||
|
* @return : count of the most frequent symbol.
|
||||||
|
* Note this function doesn't produce any error (i.e. it must succeed).
|
||||||
|
*/
|
||||||
|
unsigned HIST_count_simple(unsigned* count, unsigned* maxSymbolValuePtr,
|
||||||
|
const void* src, size_t srcSize);
|
||||||
|
|
||||||
|
/*! HIST_add() :
|
||||||
|
* Lowest level: just add nb of occurrences of characters from @src into @count.
|
||||||
|
* @count is not reset. @count array is presumed large enough (i.e. 1 KB).
|
||||||
|
@ This function does not need any additional stack memory.
|
||||||
|
*/
|
||||||
|
void HIST_add(unsigned* count, const void* src, size_t srcSize);
|
275
src/huff/huf.h
Normal file
275
src/huff/huf.h
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
/* ******************************************************************
|
||||||
|
* huff0 huffman codec,
|
||||||
|
* part of Finite State Entropy library
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* You can contact the author at :
|
||||||
|
* - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
|
||||||
|
*
|
||||||
|
* This source code is licensed under both the BSD-style license (found in the
|
||||||
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
||||||
|
* in the COPYING file in the root directory of this source tree).
|
||||||
|
* You may select, at your option, one of the above-listed licenses.
|
||||||
|
****************************************************************** */
|
||||||
|
|
||||||
|
#ifndef HUF_H_298734234
|
||||||
|
#define HUF_H_298734234
|
||||||
|
|
||||||
|
/* *** Dependencies *** */
|
||||||
|
#include <stddef.h> /* size_t */
|
||||||
|
#include "mem.h" /* U32 */
|
||||||
|
|
||||||
|
/* *** Tool functions *** */
|
||||||
|
#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */
|
||||||
|
size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */
|
||||||
|
|
||||||
|
/* Error Management */
|
||||||
|
unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */
|
||||||
|
const char* HUF_getErrorName(size_t code); /**< provides error code string (useful for debugging) */
|
||||||
|
|
||||||
|
|
||||||
|
#define HUF_WORKSPACE_SIZE ((8 << 10) + 512 /* sorting scratch space */)
|
||||||
|
#define HUF_WORKSPACE_SIZE_U64 (HUF_WORKSPACE_SIZE / sizeof(U64))
|
||||||
|
|
||||||
|
/* *** Constants *** */
|
||||||
|
#define HUF_TABLELOG_MAX 12 /* max runtime value of tableLog (due to static allocation); can be modified up to HUF_TABLELOG_ABSOLUTEMAX */
|
||||||
|
#define HUF_TABLELOG_DEFAULT 11 /* default tableLog value when none specified */
|
||||||
|
#define HUF_SYMBOLVALUE_MAX 255
|
||||||
|
|
||||||
|
#define HUF_TABLELOG_ABSOLUTEMAX 12 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */
|
||||||
|
#if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX)
|
||||||
|
# error "HUF_TABLELOG_MAX is too large !"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************************************
|
||||||
|
* Static allocation
|
||||||
|
******************************************/
|
||||||
|
/* HUF buffer bounds */
|
||||||
|
#define HUF_CTABLEBOUND 129
|
||||||
|
#define HUF_BLOCKBOUND(size) (size + (size>>8) + 8) /* only true when incompressible is pre-filtered with fast heuristic */
|
||||||
|
#define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */
|
||||||
|
|
||||||
|
/* static allocation of HUF's Compression Table */
|
||||||
|
/* this is a private definition, just exposed for allocation and strict aliasing purpose. never EVER access its members directly */
|
||||||
|
typedef size_t HUF_CElt; /* consider it an incomplete type */
|
||||||
|
#define HUF_CTABLE_SIZE_ST(maxSymbolValue) ((maxSymbolValue)+2) /* Use tables of size_t, for proper alignment */
|
||||||
|
#define HUF_CTABLE_SIZE(maxSymbolValue) (HUF_CTABLE_SIZE_ST(maxSymbolValue) * sizeof(size_t))
|
||||||
|
#define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \
|
||||||
|
HUF_CElt name[HUF_CTABLE_SIZE_ST(maxSymbolValue)] /* no final ; */
|
||||||
|
|
||||||
|
/* static allocation of HUF's DTable */
|
||||||
|
typedef U32 HUF_DTable;
|
||||||
|
#define HUF_DTABLE_SIZE(maxTableLog) (1 + (1<<(maxTableLog)))
|
||||||
|
#define HUF_CREATE_STATIC_DTABLEX1(DTable, maxTableLog) \
|
||||||
|
HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = { ((U32)((maxTableLog)-1) * 0x01000001) }
|
||||||
|
#define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) \
|
||||||
|
HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = { ((U32)(maxTableLog) * 0x01000001) }
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************************************
|
||||||
|
* Advanced decompression functions
|
||||||
|
******************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Huffman flags bitset.
|
||||||
|
* For all flags, 0 is the default value.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
/**
|
||||||
|
* If compiled with DYNAMIC_BMI2: Set flag only if the CPU supports BMI2 at runtime.
|
||||||
|
* Otherwise: Ignored.
|
||||||
|
*/
|
||||||
|
HUF_flags_bmi2 = (1 << 0),
|
||||||
|
/**
|
||||||
|
* If set: Test possible table depths to find the one that produces the smallest header + encoded size.
|
||||||
|
* If unset: Use heuristic to find the table depth.
|
||||||
|
*/
|
||||||
|
HUF_flags_optimalDepth = (1 << 1),
|
||||||
|
/**
|
||||||
|
* If set: If the previous table can encode the input, always reuse the previous table.
|
||||||
|
* If unset: If the previous table can encode the input, reuse the previous table if it results in a smaller output.
|
||||||
|
*/
|
||||||
|
HUF_flags_preferRepeat = (1 << 2),
|
||||||
|
/**
|
||||||
|
* If set: Sample the input and check if the sample is uncompressible, if it is then don't attempt to compress.
|
||||||
|
* If unset: Always histogram the entire input.
|
||||||
|
*/
|
||||||
|
HUF_flags_suspectUncompressible = (1 << 3),
|
||||||
|
/**
|
||||||
|
* If set: Don't use assembly implementations
|
||||||
|
* If unset: Allow using assembly implementations
|
||||||
|
*/
|
||||||
|
HUF_flags_disableAsm = (1 << 4),
|
||||||
|
/**
|
||||||
|
* If set: Don't use the fast decoding loop, always use the fallback decoding loop.
|
||||||
|
* If unset: Use the fast decoding loop when possible.
|
||||||
|
*/
|
||||||
|
HUF_flags_disableFast = (1 << 5)
|
||||||
|
} HUF_flags_e;
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************************************
|
||||||
|
* HUF detailed API
|
||||||
|
* ****************************************/
|
||||||
|
#define HUF_OPTIMAL_DEPTH_THRESHOLD ZSTD_btultra
|
||||||
|
|
||||||
|
/*! HUF_compress() does the following:
|
||||||
|
* 1. count symbol occurrence from source[] into table count[] using FSE_count() (exposed within "fse.h")
|
||||||
|
* 2. (optional) refine tableLog using HUF_optimalTableLog()
|
||||||
|
* 3. build Huffman table from count using HUF_buildCTable()
|
||||||
|
* 4. save Huffman table to memory buffer using HUF_writeCTable()
|
||||||
|
* 5. encode the data stream using HUF_compress4X_usingCTable()
|
||||||
|
*
|
||||||
|
* The following API allows targeting specific sub-functions for advanced tasks.
|
||||||
|
* For example, it's possible to compress several blocks using the same 'CTable',
|
||||||
|
* or to save and regenerate 'CTable' using external methods.
|
||||||
|
*/
|
||||||
|
unsigned HUF_minTableLog(unsigned symbolCardinality);
|
||||||
|
unsigned HUF_cardinality(const unsigned* count, unsigned maxSymbolValue);
|
||||||
|
unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, void* workSpace,
|
||||||
|
size_t wkspSize, HUF_CElt* table, const unsigned* count, int flags); /* table is used as scratch space for building and testing tables, not a return value */
|
||||||
|
size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize);
|
||||||
|
size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags);
|
||||||
|
size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue);
|
||||||
|
int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HUF_repeat_none, /**< Cannot use the previous table */
|
||||||
|
HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, 4}X_repeat */
|
||||||
|
HUF_repeat_valid /**< Can use the previous table and it is assumed to be valid */
|
||||||
|
} HUF_repeat;
|
||||||
|
|
||||||
|
/** HUF_compress4X_repeat() :
|
||||||
|
* Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
|
||||||
|
* If it uses hufTable it does not modify hufTable or repeat.
|
||||||
|
* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
|
||||||
|
* If preferRepeat then the old table will always be used if valid.
|
||||||
|
* If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */
|
||||||
|
size_t HUF_compress4X_repeat(void* dst, size_t dstSize,
|
||||||
|
const void* src, size_t srcSize,
|
||||||
|
unsigned maxSymbolValue, unsigned tableLog,
|
||||||
|
void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
|
||||||
|
HUF_CElt* hufTable, HUF_repeat* repeat, int flags);
|
||||||
|
|
||||||
|
/** HUF_buildCTable_wksp() :
|
||||||
|
* Same as HUF_buildCTable(), but using externally allocated scratch buffer.
|
||||||
|
* `workSpace` must be aligned on 4-bytes boundaries, and its size must be >= HUF_CTABLE_WORKSPACE_SIZE.
|
||||||
|
*/
|
||||||
|
#define HUF_CTABLE_WORKSPACE_SIZE_U32 ((4 * (HUF_SYMBOLVALUE_MAX + 1)) + 192)
|
||||||
|
#define HUF_CTABLE_WORKSPACE_SIZE (HUF_CTABLE_WORKSPACE_SIZE_U32 * sizeof(unsigned))
|
||||||
|
size_t HUF_buildCTable_wksp (HUF_CElt* tree,
|
||||||
|
const unsigned* count, U32 maxSymbolValue, U32 maxNbBits,
|
||||||
|
void* workSpace, size_t wkspSize);
|
||||||
|
|
||||||
|
/*! HUF_readStats() :
|
||||||
|
* Read compact Huffman tree, saved by HUF_writeCTable().
|
||||||
|
* `huffWeight` is destination buffer.
|
||||||
|
* @return : size read from `src` , or an error Code .
|
||||||
|
* Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */
|
||||||
|
size_t HUF_readStats(BYTE* huffWeight, size_t hwSize,
|
||||||
|
U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr,
|
||||||
|
const void* src, size_t srcSize);
|
||||||
|
|
||||||
|
/*! HUF_readStats_wksp() :
|
||||||
|
* Same as HUF_readStats() but takes an external workspace which must be
|
||||||
|
* 4-byte aligned and its size must be >= HUF_READ_STATS_WORKSPACE_SIZE.
|
||||||
|
* If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0.
|
||||||
|
*/
|
||||||
|
#define HUF_READ_STATS_WORKSPACE_SIZE_U32 FSE_DECOMPRESS_WKSP_SIZE_U32(6, HUF_TABLELOG_MAX-1)
|
||||||
|
#define HUF_READ_STATS_WORKSPACE_SIZE (HUF_READ_STATS_WORKSPACE_SIZE_U32 * sizeof(unsigned))
|
||||||
|
size_t HUF_readStats_wksp(BYTE* huffWeight, size_t hwSize,
|
||||||
|
U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr,
|
||||||
|
const void* src, size_t srcSize,
|
||||||
|
void* workspace, size_t wkspSize,
|
||||||
|
int flags);
|
||||||
|
|
||||||
|
/** HUF_readCTable() :
|
||||||
|
* Loading a CTable saved with HUF_writeCTable() */
|
||||||
|
size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned *hasZeroWeights);
|
||||||
|
|
||||||
|
/** HUF_getNbBitsFromCTable() :
|
||||||
|
* Read nbBits from CTable symbolTable, for symbol `symbolValue` presumed <= HUF_SYMBOLVALUE_MAX
|
||||||
|
* Note 1 : If symbolValue > HUF_readCTableHeader(symbolTable).maxSymbolValue, returns 0
|
||||||
|
* Note 2 : is not inlined, as HUF_CElt definition is private
|
||||||
|
*/
|
||||||
|
U32 HUF_getNbBitsFromCTable(const HUF_CElt* symbolTable, U32 symbolValue);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
BYTE tableLog;
|
||||||
|
BYTE maxSymbolValue;
|
||||||
|
BYTE unused[sizeof(size_t) - 2];
|
||||||
|
} HUF_CTableHeader;
|
||||||
|
|
||||||
|
/** HUF_readCTableHeader() :
|
||||||
|
* @returns The header from the CTable specifying the tableLog and the maxSymbolValue.
|
||||||
|
*/
|
||||||
|
HUF_CTableHeader HUF_readCTableHeader(HUF_CElt const* ctable);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HUF_decompress() does the following:
|
||||||
|
* 1. select the decompression algorithm (X1, X2) based on pre-computed heuristics
|
||||||
|
* 2. build Huffman table from save, using HUF_readDTableX?()
|
||||||
|
* 3. decode 1 or 4 segments in parallel using HUF_decompress?X?_usingDTable()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** HUF_selectDecoder() :
|
||||||
|
* Tells which decoder is likely to decode faster,
|
||||||
|
* based on a set of pre-computed metrics.
|
||||||
|
* @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 .
|
||||||
|
* Assumption : 0 < dstSize <= 128 KB */
|
||||||
|
U32 HUF_selectDecoder (size_t dstSize, size_t cSrcSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum workspace size for the `workSpace` used in
|
||||||
|
* HUF_readDTableX1_wksp() and HUF_readDTableX2_wksp().
|
||||||
|
*
|
||||||
|
* The space used depends on HUF_TABLELOG_MAX, ranging from ~1500 bytes when
|
||||||
|
* HUF_TABLE_LOG_MAX=12 to ~1850 bytes when HUF_TABLE_LOG_MAX=15.
|
||||||
|
* Buffer overflow errors may potentially occur if code modifications result in
|
||||||
|
* a required workspace size greater than that specified in the following
|
||||||
|
* macro.
|
||||||
|
*/
|
||||||
|
#define HUF_DECOMPRESS_WORKSPACE_SIZE ((2 << 10) + (1 << 9))
|
||||||
|
#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
|
||||||
|
|
||||||
|
|
||||||
|
/* ====================== */
|
||||||
|
/* single stream variants */
|
||||||
|
/* ====================== */
|
||||||
|
|
||||||
|
size_t HUF_compress1X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable, int flags);
|
||||||
|
/** HUF_compress1X_repeat() :
|
||||||
|
* Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none.
|
||||||
|
* If it uses hufTable it does not modify hufTable or repeat.
|
||||||
|
* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used.
|
||||||
|
* If preferRepeat then the old table will always be used if valid.
|
||||||
|
* If suspectUncompressible then some sampling checks will be run to potentially skip huffman coding */
|
||||||
|
size_t HUF_compress1X_repeat(void* dst, size_t dstSize,
|
||||||
|
const void* src, size_t srcSize,
|
||||||
|
unsigned maxSymbolValue, unsigned tableLog,
|
||||||
|
void* workSpace, size_t wkspSize, /**< `workSpace` must be aligned on 4-bytes boundaries, `wkspSize` must be >= HUF_WORKSPACE_SIZE */
|
||||||
|
HUF_CElt* hufTable, HUF_repeat* repeat, int flags);
|
||||||
|
|
||||||
|
size_t HUF_decompress1X_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags);
|
||||||
|
#ifndef HUF_FORCE_DECOMPRESS_X1
|
||||||
|
size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags); /**< double-symbols decoder */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* BMI2 variants.
|
||||||
|
* If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0.
|
||||||
|
*/
|
||||||
|
size_t HUF_decompress1X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags);
|
||||||
|
#ifndef HUF_FORCE_DECOMPRESS_X2
|
||||||
|
size_t HUF_decompress1X1_DCtx_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags);
|
||||||
|
#endif
|
||||||
|
size_t HUF_decompress4X_usingDTable(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int flags);
|
||||||
|
size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int flags);
|
||||||
|
#ifndef HUF_FORCE_DECOMPRESS_X2
|
||||||
|
size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags);
|
||||||
|
#endif
|
||||||
|
#ifndef HUF_FORCE_DECOMPRESS_X1
|
||||||
|
size_t HUF_readDTableX2_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int flags);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* HUF_H_298734234 */
|
24
src/huff/mem.h
Normal file
24
src/huff/mem.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under both the BSD-style license (found in the
|
||||||
|
* LICENSE file in the root directory of this source tree) and the GPLv2 (found
|
||||||
|
* in the COPYING file in the root directory of this source tree).
|
||||||
|
* You may select, at your option, one of the above-listed licenses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MEM_H_MODULE
|
||||||
|
#define MEM_H_MODULE
|
||||||
|
|
||||||
|
/*-****************************************
|
||||||
|
* Dependencies
|
||||||
|
******************************************/
|
||||||
|
#include <stddef.h> /* size_t, ptrdiff_t */
|
||||||
|
#include <stdint.h> /* intptr_t */
|
||||||
|
#define MEM_STATIC
|
||||||
|
|
||||||
|
typedef uint32_t U32;
|
||||||
|
typedef uint8_t BYTE;
|
||||||
|
|
||||||
|
#endif /* MEM_H_MODULE */
|
|
@ -77,8 +77,6 @@ if (WITH_ASAN OR WITH_USAN)
|
||||||
target_compile_definitions(dfly_transaction PRIVATE SANITIZERS)
|
target_compile_definitions(dfly_transaction PRIVATE SANITIZERS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_library(ZSTD_LIB NAMES libzstd.a libzstdstatic.a zstd NAMES_PER_DIR REQUIRED)
|
|
||||||
|
|
||||||
if (WITH_AWS)
|
if (WITH_AWS)
|
||||||
SET(AWS_LIB awsv2_lib)
|
SET(AWS_LIB awsv2_lib)
|
||||||
add_definitions(-DWITH_AWS)
|
add_definitions(-DWITH_AWS)
|
||||||
|
@ -87,7 +85,7 @@ endif()
|
||||||
cxx_link(dfly_transaction dfly_core strings_lib TRDP::fast_float)
|
cxx_link(dfly_transaction dfly_core strings_lib TRDP::fast_float)
|
||||||
cxx_link(dragonfly_lib dfly_transaction dfly_facade redis_lib ${AWS_LIB} jsonpath
|
cxx_link(dragonfly_lib dfly_transaction dfly_facade redis_lib ${AWS_LIB} jsonpath
|
||||||
strings_lib html_lib gcp_lib
|
strings_lib html_lib gcp_lib
|
||||||
http_client_lib absl::random_random TRDP::jsoncons ${ZSTD_LIB} TRDP::lz4
|
http_client_lib absl::random_random TRDP::jsoncons TRDP::zstd TRDP::lz4
|
||||||
TRDP::croncpp TRDP::flatbuffers)
|
TRDP::croncpp TRDP::flatbuffers)
|
||||||
|
|
||||||
if (DF_USE_SSL)
|
if (DF_USE_SSL)
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
//
|
//
|
||||||
#include "server/debugcmd.h"
|
#include "server/debugcmd.h"
|
||||||
|
|
||||||
|
#define HUF_STATIC_LINKING_ONLY
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include "huff/hist.h"
|
||||||
|
#include "huff/huf.h"
|
||||||
#include "redis/redis_aux.h"
|
#include "redis/redis_aux.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +15,8 @@ extern "C" {
|
||||||
#include <absl/random/random.h>
|
#include <absl/random/random.h>
|
||||||
#include <absl/strings/match.h>
|
#include <absl/strings/match.h>
|
||||||
#include <absl/strings/str_cat.h>
|
#include <absl/strings/str_cat.h>
|
||||||
|
#include <lz4.h>
|
||||||
|
#include <zdict.h>
|
||||||
#include <zstd.h>
|
#include <zstd.h>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
@ -312,6 +318,64 @@ void DoBuildObjHist(EngineShard* shard, ConnectionContext* cntx, ObjHistMap* obj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FillDictSamples(PrimeTable* table, string* sample_buf) {
|
||||||
|
PrimeTable::Cursor cursor;
|
||||||
|
unsigned steps = 0;
|
||||||
|
string scratch;
|
||||||
|
constexpr size_t kMaxLen = 512;
|
||||||
|
do {
|
||||||
|
cursor = table->Traverse(cursor, [&](PrimeIterator it) {
|
||||||
|
it->first.GetString(&scratch);
|
||||||
|
size_t len = std::min(scratch.size(), kMaxLen);
|
||||||
|
sample_buf->append(scratch.data(), len);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sample_buf->size() > 1_MB)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (steps >= 20000) {
|
||||||
|
steps = 0;
|
||||||
|
ThisFiber::Yield();
|
||||||
|
}
|
||||||
|
} while (cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HufHist {
|
||||||
|
static constexpr unsigned kMaxSymbol = 255;
|
||||||
|
unsigned hist[kMaxSymbol + 1]; // histogram of symbols.
|
||||||
|
unsigned max_symbol = 0; // what is the max symbol of the histogram.
|
||||||
|
|
||||||
|
HufHist() {
|
||||||
|
memset(hist, 0, sizeof(hist));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Merge(const HufHist& other) {
|
||||||
|
max_symbol = std::max(max_symbol, other.max_symbol);
|
||||||
|
for (unsigned i = 0; i <= max_symbol; ++i) {
|
||||||
|
hist[i] += other.hist[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void DoComputeHist(EngineShard* shard, ConnectionContext* cntx, HufHist* dest) {
|
||||||
|
auto& db_slice = cntx->ns->GetDbSlice(shard->shard_id());
|
||||||
|
DbTable* dbt = db_slice.GetDBTable(cntx->db_index());
|
||||||
|
CHECK(dbt);
|
||||||
|
|
||||||
|
string sample_buf;
|
||||||
|
FillDictSamples(&dbt->prime, &sample_buf);
|
||||||
|
|
||||||
|
if (sample_buf.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dest->max_symbol = 255;
|
||||||
|
|
||||||
|
unique_ptr<uint32_t[]> wksp(new uint32_t[HIST_WKSP_SIZE_U32]);
|
||||||
|
size_t max_freq = HIST_count_wksp(dest->hist, &dest->max_symbol, sample_buf.data(),
|
||||||
|
sample_buf.size(), wksp.get(), HIST_WKSP_SIZE);
|
||||||
|
CHECK(!HIST_isError(max_freq));
|
||||||
|
}
|
||||||
|
|
||||||
ObjInfo InspectOp(ConnectionContext* cntx, string_view key) {
|
ObjInfo InspectOp(ConnectionContext* cntx, string_view key) {
|
||||||
auto& db_slice = cntx->ns->GetCurrentDbSlice();
|
auto& db_slice = cntx->ns->GetCurrentDbSlice();
|
||||||
auto db_index = cntx->db_index();
|
auto db_index = cntx->db_index();
|
||||||
|
@ -514,6 +578,8 @@ void DebugCmd::Run(CmdArgList args, facade::SinkReplyBuilder* builder) {
|
||||||
" traffic logging is stopped.",
|
" traffic logging is stopped.",
|
||||||
"RECVSIZE [<tid> | ENABLE | DISABLE]",
|
"RECVSIZE [<tid> | ENABLE | DISABLE]",
|
||||||
" Prints the histogram of the received request sizes on the given thread",
|
" Prints the histogram of the received request sizes on the given thread",
|
||||||
|
"COMPRESSION"
|
||||||
|
" Estimate the compressability of values of the given type",
|
||||||
"HELP",
|
"HELP",
|
||||||
" Prints this help.",
|
" Prints this help.",
|
||||||
};
|
};
|
||||||
|
@ -585,6 +651,10 @@ void DebugCmd::Run(CmdArgList args, facade::SinkReplyBuilder* builder) {
|
||||||
return Keys(args.subspan(1), builder);
|
return Keys(args.subspan(1), builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (subcmd == "COMPRESSION") {
|
||||||
|
return Compression(builder);
|
||||||
|
}
|
||||||
|
|
||||||
string reply = UnknownSubCmd(subcmd, "DEBUG");
|
string reply = UnknownSubCmd(subcmd, "DEBUG");
|
||||||
return builder->SendError(reply, kSyntaxErrType);
|
return builder->SendError(reply, kSyntaxErrType);
|
||||||
}
|
}
|
||||||
|
@ -1209,4 +1279,47 @@ void DebugCmd::Keys(CmdArgList args, facade::SinkReplyBuilder* builder) {
|
||||||
return builder->SendError(kSyntaxErr);
|
return builder->SendError(kSyntaxErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugCmd::Compression(facade::SinkReplyBuilder* builder) {
|
||||||
|
auto* rb = static_cast<RedisReplyBuilder*>(builder);
|
||||||
|
|
||||||
|
fb2::Mutex mu;
|
||||||
|
HufHist hist;
|
||||||
|
shard_set->RunBlockingInParallel([&](EngineShard* shard) {
|
||||||
|
HufHist local;
|
||||||
|
DoComputeHist(shard, cntx_, &local);
|
||||||
|
std::unique_lock lk(mu);
|
||||||
|
hist.Merge(local);
|
||||||
|
});
|
||||||
|
|
||||||
|
HUF_CREATE_STATIC_CTABLE(huf_ctable, HufHist::kMaxSymbol);
|
||||||
|
|
||||||
|
size_t num_bits = 0, compressed_size = 0, raw_size = 0;
|
||||||
|
|
||||||
|
if (hist.max_symbol) {
|
||||||
|
unique_ptr<uint32_t[]> wrkspace(new uint32_t[HUF_CTABLE_WORKSPACE_SIZE_U32]);
|
||||||
|
constexpr size_t kWspSize = HUF_CTABLE_WORKSPACE_SIZE;
|
||||||
|
num_bits =
|
||||||
|
HUF_buildCTable_wksp(huf_ctable, hist.hist, hist.max_symbol, 0, wrkspace.get(), kWspSize);
|
||||||
|
|
||||||
|
compressed_size = HUF_estimateCompressedSize(huf_ctable, hist.hist, hist.max_symbol);
|
||||||
|
raw_size = 0;
|
||||||
|
for (unsigned i = 0; i < hist.max_symbol; i++) {
|
||||||
|
raw_size += hist.hist[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rb->StartCollection(5, RedisReplyBuilder::CollectionType::MAP);
|
||||||
|
rb->SendSimpleString("max_symbol");
|
||||||
|
rb->SendLong(hist.max_symbol);
|
||||||
|
rb->SendSimpleString("max_bits");
|
||||||
|
rb->SendLong(num_bits);
|
||||||
|
rb->SendSimpleString("raw_size");
|
||||||
|
rb->SendLong(raw_size);
|
||||||
|
rb->SendSimpleString("compressed_size");
|
||||||
|
rb->SendLong(compressed_size);
|
||||||
|
rb->SendSimpleString("ratio");
|
||||||
|
double ratio = raw_size > 0 ? static_cast<double>(compressed_size) / raw_size : 0;
|
||||||
|
rb->SendDouble(ratio);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dfly
|
} // namespace dfly
|
||||||
|
|
|
@ -57,6 +57,7 @@ class DebugCmd {
|
||||||
void RecvSize(std::string_view param, facade::SinkReplyBuilder* builder);
|
void RecvSize(std::string_view param, facade::SinkReplyBuilder* builder);
|
||||||
void Topk(CmdArgList args, facade::SinkReplyBuilder* builder);
|
void Topk(CmdArgList args, facade::SinkReplyBuilder* builder);
|
||||||
void Keys(CmdArgList args, facade::SinkReplyBuilder* builder);
|
void Keys(CmdArgList args, facade::SinkReplyBuilder* builder);
|
||||||
|
void Compression(facade::SinkReplyBuilder* builder);
|
||||||
|
|
||||||
ServerFamily& sf_;
|
ServerFamily& sf_;
|
||||||
cluster::ClusterFamily& cf_;
|
cluster::ClusterFamily& cf_;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue