Skip to content

Commit 70133b9

Browse files
committed
chore: several improvements around sorted map
1. Pass memory_resource to sorted_map. 2. Get rid of GetDict leaky accessor in SortedMap and introduce a proper Scan method. 3. Introduce correct BPTree type inside SortedMap::DFImpl. 4. Added a test for bptree_test that covers sds comparison (apparently, sdscmp can return values outside of [-1, 1] range). Fixed bptree code to support a proper spec for three-way comparison. 5. Expose pointers to internal objects allocated by score_map so we could insert them into bptree. Signed-off-by: Roman Gershman <[email protected]>
1 parent 82c3690 commit 70133b9

File tree

12 files changed

+230
-111
lines changed

12 files changed

+230
-111
lines changed

src/core/bptree_set.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ template <typename T, typename Policy = BPTreePolicy<T>> class BPTree {
6565

6666
void Clear();
6767

68-
BPTreeNode* DEBUG_root() {
68+
const BPTreeNode* DEBUG_root() const {
6969
return root_;
7070
}
7171

@@ -299,10 +299,10 @@ void BPTree<T, Policy>::InsertToFullLeaf(KeyT item, const BPTreePath& path) {
299299
assert(node->NumItems() < Layout::kMaxLeafKeys);
300300

301301
if (insert_pos <= node->NumItems()) {
302-
assert(Comp()(item, median) == -1);
302+
assert(Comp()(item, median) < 0);
303303
node->LeafInsert(insert_pos, item);
304304
} else {
305-
assert(Comp()(item, median) == 1);
305+
assert(Comp()(item, median) > 0);
306306
right->LeafInsert(insert_pos - node->NumItems() - 1, item);
307307
}
308308

@@ -359,12 +359,12 @@ void BPTree<T, Policy>::InsertToFullLeaf(KeyT item, const BPTreePath& path) {
359359
assert(node->NumItems() < Layout::kMaxInnerKeys);
360360

361361
if (pos <= node->NumItems()) {
362-
assert(Comp()(median, next_median) == -1);
362+
assert(Comp()(median, next_median) < 0);
363363

364364
node->InnerInsert(pos, median, right);
365365
node->IncreaseTreeCount(1);
366366
} else {
367-
assert(Comp()(median, next_median) == 1);
367+
assert(Comp()(median, next_median) > 0);
368368

369369
next_right->InnerInsert(pos - node->NumItems() - 1, median, right);
370370

@@ -566,7 +566,7 @@ template <typename T, typename Policy> void BPTree<T, Policy>::Delete(BPTreePath
566566
path.DigRight();
567567

568568
BPTreeNode* leaf = path.Last().first;
569-
assert(Comp()(leaf->Key(leaf->NumItems() - 1), node->Key(key_pos)) == -1);
569+
assert(Comp()(leaf->Key(leaf->NumItems() - 1), node->Key(key_pos)) < 0);
570570

571571
// set a new separator.
572572
node->SetKey(key_pos, leaf->Key(leaf->NumItems() - 1));

src/core/bptree_set_test.cc

Lines changed: 71 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,35 +22,14 @@ using namespace std;
2222

2323
namespace dfly {
2424

25-
class BPTreeSetTest : public ::testing::Test {
26-
using Node = detail::BPTreeNode<uint64_t>;
27-
28-
protected:
29-
static constexpr size_t kNumElems = 7000;
30-
31-
BPTreeSetTest() : mi_alloc_(mi_heap_get_backing()), bptree_(&mi_alloc_) {
32-
}
33-
static void SetUpTestSuite() {
34-
}
25+
namespace {
3526

36-
void FillTree(unsigned factor = 1) {
37-
for (unsigned i = 0; i < kNumElems; ++i) {
38-
bptree_.Insert(i * factor);
39-
}
40-
}
41-
42-
bool Validate();
43-
44-
static bool Validate(const Node* node, uint64_t ubound);
45-
46-
MiMemoryResource mi_alloc_;
47-
BPTree<uint64_t> bptree_;
48-
mt19937 generator_{1};
49-
};
27+
template <typename Node, typename Policy>
28+
bool ValidateNode(const Node* node, typename Node::KeyT ubound) {
29+
typename Policy::KeyCompareTo cmp;
5030

51-
bool BPTreeSetTest::Validate(const Node* node, uint64_t ubound) {
5231
for (unsigned i = 1; i < node->NumItems(); ++i) {
53-
if (node->Key(i - 1) >= node->Key(i))
32+
if (cmp(node->Key(i - 1), node->Key(i)) > -1)
5433
return false;
5534
}
5635

@@ -71,25 +50,72 @@ bool BPTreeSetTest::Validate(const Node* node, uint64_t ubound) {
7150
}
7251
}
7352

74-
return node->Key(node->NumItems() - 1) < ubound;
53+
return cmp(node->Key(node->NumItems() - 1), ubound) == -1;
7554
}
7655

56+
struct ZsetPolicy {
57+
struct KeyT {
58+
double d;
59+
sds s;
60+
};
61+
62+
struct KeyCompareTo {
63+
int operator()(const KeyT& left, const KeyT& right) {
64+
if (left.d < right.d)
65+
return -1;
66+
if (left.d > right.d)
67+
return 1;
68+
69+
// Note that sdscmp can return values outside of [-1, 1] range.
70+
return sdscmp(left.s, right.s);
71+
}
72+
};
73+
};
74+
75+
using SDSTree = BPTree<ZsetPolicy::KeyT, ZsetPolicy>;
76+
77+
} // namespace
78+
79+
class BPTreeSetTest : public ::testing::Test {
80+
using Node = detail::BPTreeNode<uint64_t>;
81+
82+
protected:
83+
static constexpr size_t kNumElems = 7000;
84+
85+
BPTreeSetTest() : mi_alloc_(mi_heap_get_backing()), bptree_(&mi_alloc_) {
86+
}
87+
static void SetUpTestSuite() {
88+
}
89+
90+
void FillTree(unsigned factor = 1) {
91+
for (unsigned i = 0; i < kNumElems; ++i) {
92+
bptree_.Insert(i * factor);
93+
}
94+
}
95+
96+
bool Validate();
97+
98+
MiMemoryResource mi_alloc_;
99+
BPTree<uint64_t> bptree_;
100+
mt19937 generator_{1};
101+
};
102+
77103
bool BPTreeSetTest::Validate() {
78104
auto* root = bptree_.DEBUG_root();
79105
if (!root)
80106
return true;
81107

82108
// node, upper bound
83-
std::vector<pair<Node*, uint64_t>> stack;
109+
vector<pair<const Node*, uint64_t>> stack;
84110

85111
stack.emplace_back(root, UINT64_MAX);
86112

87113
while (!stack.empty()) {
88-
Node* node = stack.back().first;
114+
const Node* node = stack.back().first;
89115
uint64_t ubound = stack.back().second;
90116
stack.pop_back();
91117

92-
if (!Validate(node, ubound))
118+
if (!ValidateNode<Node, BPTreePolicy<uint64_t>>(node, ubound))
93119
return false;
94120

95121
if (!node->IsLeaf()) {
@@ -353,25 +379,24 @@ TEST_F(BPTreeSetTest, MemoryUsage) {
353379
LOG(INFO) << "btree after: " << mi_alloc.used() << " bytes";
354380
}
355381

356-
struct ZsetPolicy {
357-
struct KeyT {
358-
double d;
359-
sds s;
360-
};
382+
TEST_F(BPTreeSetTest, InsertSDS) {
383+
vector<ZsetPolicy::KeyT> vals;
384+
for (unsigned i = 0; i < 256; ++i) {
385+
sds s = sdsempty();
361386

362-
struct KeyCompareTo {
363-
int operator()(const KeyT& left, const KeyT& right) {
364-
if (left.d < right.d)
365-
return -1;
366-
if (left.d > right.d)
367-
return 1;
387+
s = sdscatfmt(s, "a%u", i);
388+
vals.emplace_back(ZsetPolicy::KeyT{.d = 1000, .s = s});
389+
}
368390

369-
return sdscmp(left.s, right.s);
370-
}
371-
};
372-
};
391+
SDSTree tree(&mi_alloc_);
392+
for (size_t i = 0; i < vals.size(); ++i) {
393+
ASSERT_TRUE(tree.Insert(vals[i]));
394+
}
373395

374-
using SDSTree = BPTree<ZsetPolicy::KeyT, ZsetPolicy>;
396+
for (auto v : vals) {
397+
sdsfree(v.s);
398+
}
399+
}
375400

376401
static string RandomString(mt19937& rand, unsigned len) {
377402
const string_view alpanum = "1234567890abcdefghijklmnopqrstuvwxyz";

src/core/compact_object.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ int RobjWrapper::ZsetAdd(double score, sds ele, int in_flags, int* out_flags, do
430430
* becomes too long *before* executing zzlInsert. */
431431
if (zl_len + 1 > server.zset_max_listpack_entries ||
432432
sdslen(ele) > server.zset_max_listpack_value || !lpSafeToAdd(lp, sdslen(ele))) {
433-
unique_ptr<SortedMap> ss = SortedMap::FromListPack(lp);
433+
unique_ptr<SortedMap> ss = SortedMap::FromListPack(tl.local_mr, lp);
434434
lpFree(lp);
435435
inner_obj_ = ss.release();
436436
encoding_ = OBJ_ENCODING_SKIPLIST;

src/core/score_map.cc

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,7 @@ inline double GetValue(sds key) {
3131
return u.d;
3232
}
3333

34-
} // namespace
35-
36-
ScoreMap::~ScoreMap() {
37-
Clear();
38-
}
39-
40-
bool ScoreMap::AddOrUpdate(string_view field, double value) {
34+
void* AllocateScored(string_view field, double value) {
4135
size_t meta_offset = field.size() + 1;
4236
DoubleUnion u;
4337
u.d = value;
@@ -52,21 +46,33 @@ bool ScoreMap::AddOrUpdate(string_view field, double value) {
5246

5347
absl::little_endian::Store64(newkey + meta_offset, u.u);
5448

49+
return newkey;
50+
}
51+
52+
} // namespace
53+
54+
ScoreMap::~ScoreMap() {
55+
Clear();
56+
}
57+
58+
pair<void*, bool> ScoreMap::AddOrUpdate(string_view field, double value) {
59+
void* newkey = AllocateScored(field, value);
60+
5561
// Replace the whole entry.
5662
sds prev_entry = (sds)AddOrReplaceObj(newkey, false);
5763
if (prev_entry) {
5864
ObjDelete(prev_entry, false);
59-
return false;
65+
return {newkey, false};
6066
}
6167

62-
return true;
68+
return {newkey, true};
6369
}
6470

65-
bool ScoreMap::AddOrSkip(std::string_view field, double value) {
71+
std::pair<void*, bool> ScoreMap::AddOrSkip(std::string_view field, double value) {
6672
void* obj = FindInternal(&field, 1); // 1 - string_view
6773

6874
if (obj)
69-
return false;
75+
return {obj, false};
7076

7177
return AddOrUpdate(field, value);
7278
}

src/core/score_map.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ class ScoreMap : public DenseSet {
7979

8080
// Returns true if field was added
8181
// otherwise updates its value and returns false.
82-
bool AddOrUpdate(std::string_view field, double value);
82+
std::pair<void*, bool> AddOrUpdate(std::string_view field, double value);
8383

8484
// Returns true if field was added
8585
// false, if already exists. In that case no update is done.
86-
bool AddOrSkip(std::string_view field, double value);
86+
std::pair<void*, bool> AddOrSkip(std::string_view field, double value);
8787

8888
bool Erase(std::string_view s1);
8989

@@ -92,6 +92,11 @@ class ScoreMap : public DenseSet {
9292
/// @return sds
9393
std::optional<double> Find(std::string_view key);
9494

95+
// returns the internal object if found, otherwise nullptr.
96+
void* FindObj(sds ele) {
97+
return FindInternal(ele, 0);
98+
}
99+
95100
void Clear();
96101

97102
iterator begin() {

src/core/score_map_test.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class ScoreMapTest : public ::testing::Test {
5454
};
5555

5656
TEST_F(ScoreMapTest, Basic) {
57-
EXPECT_TRUE(sm_->AddOrUpdate("foo", 5));
57+
EXPECT_TRUE(sm_->AddOrUpdate("foo", 5).second);
5858
EXPECT_EQ(5, sm_->Find("foo"));
5959

6060
auto it = sm_->begin();
@@ -70,13 +70,13 @@ TEST_F(ScoreMapTest, Basic) {
7070
}
7171

7272
size_t sz = sm_->ObjMallocUsed();
73-
EXPECT_FALSE(sm_->AddOrUpdate("foo", 17));
73+
EXPECT_FALSE(sm_->AddOrUpdate("foo", 17).second);
7474
EXPECT_EQ(sm_->ObjMallocUsed(), sz);
7575

7676
it = sm_->begin();
7777
EXPECT_EQ(17, it->second);
7878

79-
EXPECT_FALSE(sm_->AddOrSkip("foo", 31));
79+
EXPECT_FALSE(sm_->AddOrSkip("foo", 31).second);
8080
EXPECT_EQ(17, it->second);
8181
}
8282

0 commit comments

Comments
 (0)