Skip to content

Commit

Permalink
chore: Add benchmarks of qlist compression paths (#4543)
Browse files Browse the repository at this point in the history
1. Add testdata, move the existing test data to zstd-compressed format.
2. Add benchmarks in qlist_test that add lots of records, forcing compression of inner noded,
   or read compressed list checking the decompression.

Currently qlist uses valkey compression algorithm.

On my machine:
1. (valkey) Compression is 5 times slower than no compression.
2. Decompression is 37 times slower.

Signed-off-by: Roman Gershman <[email protected]>
  • Loading branch information
romange authored Feb 2, 2025
1 parent bff9cf6 commit 80e4012
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 760 deletions.
8 changes: 8 additions & 0 deletions .clangd
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Diagnostics:
UnusedIncludes: None
MissingIncludes: None
Includes:
IgnoreHeader: base/*.h

CompileFlags:
CompilationDatabase: build-dbg/ # Search for compile_commands.json
4 changes: 2 additions & 2 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ cxx_link(dash_bench dfly_core redis_test_lib)
cxx_test(dfly_core_test dfly_core TRDP::fast_float LABELS DFLY)
cxx_test(compact_object_test dfly_core LABELS DFLY)
cxx_test(extent_tree_test dfly_core LABELS DFLY)
cxx_test(dash_test dfly_core file redis_test_lib DATA testdata/ids.txt LABELS DFLY)
cxx_test(dash_test dfly_core file redis_test_lib DATA testdata/ids.txt.zst LABELS DFLY)
cxx_test(interpreter_test dfly_core LABELS DFLY)

cxx_test(string_set_test dfly_core LABELS DFLY)
Expand All @@ -29,4 +29,4 @@ cxx_test(score_map_test dfly_core LABELS DFLY)
cxx_test(flatbuffers_test dfly_core TRDP::flatbuffers LABELS DFLY)
cxx_test(bloom_test dfly_core LABELS DFLY)
cxx_test(allocation_tracker_test dfly_core absl::random_random LABELS DFLY)
cxx_test(qlist_test dfly_core LABELS DFLY)
cxx_test(qlist_test dfly_core DATA testdata/list.txt.zst LABELS DFLY)
9 changes: 4 additions & 5 deletions src/core/dash_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -882,12 +882,11 @@ struct BlankPolicy : public BasicDashPolicy {
// into a new segment, not every item finds a place.
TEST_F(DashTest, SplitBug) {
DashTable<uint64_t, uint64_t, BlankPolicy> table;
string path = base::ProgramRunfile("testdata/ids.txt.zst");
io::Result<io::Source*> src = io::OpenUncompressed(path);
ASSERT_TRUE(src) << src.error();

io::ReadonlyFileOrError fl_err =
io::OpenRead(base::ProgramRunfile("testdata/ids.txt"), io::ReadonlyFile::Options{});
CHECK(fl_err);
io::FileSource fs(std::move(*fl_err));
io::LineReader lr(&fs, DO_NOT_TAKE_OWNERSHIP);
io::LineReader lr(*src, TAKE_OWNERSHIP);
string_view line;
uint64_t val;
while (lr.Next(&line)) {
Expand Down
5 changes: 4 additions & 1 deletion src/core/qlist.cc
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,12 @@ bool CompressNode(QList::Node* node) {
if (((lzf->sz = lzf_compress(node->entry, node->sz, lzf->compressed, node->sz, sdata)) == 0) ||
lzf->sz + MIN_COMPRESS_IMPROVE >= node->sz) {
/* lzf_compress aborts/rejects compression if value not compressible. */
DVLOG(2) << "Uncompressable " << node->sz << " vs " << lzf->sz;
zfree(lzf);

return false;
}
DVLOG(2) << "Compressed " << node->sz << " to " << lzf->sz;

lzf = (quicklistLZF*)zrealloc(lzf, sizeof(*lzf) + lzf->sz);
zfree(node->entry);
Expand Down Expand Up @@ -352,7 +355,7 @@ void QList::Clear() {
}

void QList::Push(string_view value, Where where) {
DVLOG(2) << "Push " << absl::CHexEscape(value) << " " << (where == HEAD ? "HEAD" : "TAIL");
DVLOG(3) << "Push " << absl::CHexEscape(value) << " " << (where == HEAD ? "HEAD" : "TAIL");

/* The head and tail should never be compressed (we don't attempt to decompress them) */
if (head_) {
Expand Down
65 changes: 60 additions & 5 deletions src/core/qlist_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "base/gtest.h"
#include "base/logging.h"
#include "core/mi_memory_resource.h"
#include "io/file.h"
#include "io/line_reader.h"

extern "C" {
#include "redis/listpack.h"
Expand All @@ -24,7 +26,7 @@ using namespace std;
using namespace testing;
using absl::StrCat;

static int _ql_verify_compress(const QList& ql) {
static int ql_verify_compress(const QList& ql) {
int errors = 0;
unsigned compress_param = ql.compress_param();
if (compress_param > 0) {
Expand Down Expand Up @@ -115,19 +117,23 @@ static int ql_verify(const QList& ql, uint32_t nc, uint32_t count, uint32_t head
errors++;
}

errors += _ql_verify_compress(ql);
errors += ql_verify_compress(ql);
return errors;
}

static void SetupMalloc() {
// configure redis lib zmalloc which requires mimalloc heap to work.
auto* tlh = mi_heap_get_backing();
init_zmalloc_threadlocal(tlh);
}

class QListTest : public ::testing::Test {
protected:
QListTest() : mr_(mi_heap_get_backing()) {
}

static void SetUpTestSuite() {
// configure redis lib zmalloc which requires mimalloc heap to work.
auto* tlh = mi_heap_get_backing();
init_zmalloc_threadlocal(tlh);
SetupMalloc();
}

static void TearDownTestSuite() {
Expand Down Expand Up @@ -848,4 +854,53 @@ TEST_P(OptionsTest, IndexFrom500) {
ASSERT_FALSE(it.Next());
}

static void BM_QListCompress(benchmark::State& state) {
SetupMalloc();

string path = base::ProgramRunfile("testdata/list.txt.zst");
io::Result<io::Source*> src = io::OpenUncompressed(path);
CHECK(src) << src.error();
io::LineReader lr(*src, TAKE_OWNERSHIP);
string_view line;
vector<string> lines;
while (lr.Next(&line)) {
lines.push_back(string(line));
}

while (state.KeepRunning()) {
QList ql(-2, state.range(0)); // uses differrent compression modes, see below.
for (const string& l : lines) {
ql.Push(l, QList::TAIL);
}
DVLOG(1) << ql.node_count() << ", " << ql.MallocUsed(true);
}
}
BENCHMARK(BM_QListCompress)
->Arg(0) // no compression
->Arg(1) // compress all nodes but edges.
->Arg(4); // compress all nodes but 4 nodes from edges.

static void BM_QListUncompress(benchmark::State& state) {
SetupMalloc();

string path = base::ProgramRunfile("testdata/list.txt.zst");
io::Result<io::Source*> src = io::OpenUncompressed(path);
CHECK(src) << src.error();
io::LineReader lr(*src, TAKE_OWNERSHIP);
string_view line;
QList ql(-2, state.range(0));

while (lr.Next(&line)) {
ql.Push(line, QList::TAIL);
}

LOG(INFO) << "MallocUsed " << ql.compress_param() << ": " << ql.MallocUsed(true) << ", "
<< ql.MallocUsed(false);

while (state.KeepRunning()) {
ql.Iterate([](const QList::Entry& e) { return true; }, 0, -1);
}
}
BENCHMARK(BM_QListUncompress)->Arg(0)->Arg(1)->Arg(4);

} // namespace dfly
Loading

0 comments on commit 80e4012

Please sign in to comment.