Skip to content

Commit 6237fbd

Browse files
committed
feat: add Clear() and tests
1 parent 9da231d commit 6237fbd

File tree

2 files changed

+103
-69
lines changed

2 files changed

+103
-69
lines changed

src/core/intrusive_string_set.h

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ class ISLEntry {
3535
data_ = data;
3636
}
3737

38+
static void Destroy(ISLEntry entry) {
39+
free(entry.Raw());
40+
}
41+
3842
operator bool() const {
3943
return data_;
4044
}
@@ -56,6 +60,17 @@ class ISLEntry {
5660
return res;
5761
}
5862

63+
void SetExpiryTime(uint32_t ttl_sec) {
64+
if (HasExpiry()) {
65+
auto* ttl_pos = Raw() + sizeof(char*);
66+
std::memcpy(ttl_pos, &ttl_sec, sizeof(ttl_sec));
67+
} else {
68+
ISLEntry new_entry = Create(Key(), ttl_sec);
69+
std::swap(data_, new_entry.data_);
70+
Destroy(new_entry);
71+
}
72+
}
73+
5974
private:
6075
static ISLEntry Create(std::string_view key, uint32_t ttl_sec = UINT32_MAX) {
6176
char* next = nullptr;
@@ -85,10 +100,6 @@ class ISLEntry {
85100
return res;
86101
}
87102

88-
static void Destroy(ISLEntry entry) {
89-
free(entry.Raw());
90-
}
91-
92103
ISLEntry Next() const {
93104
ISLEntry next;
94105
std::memcpy(&next.data_, Raw(), sizeof(next));
@@ -160,7 +171,12 @@ class IntrusiveStringList {
160171
return start_;
161172
}
162173

163-
ISLEntry Pop() {
174+
// TODO rewrite, because it's dangerous operations, ISLEntry shouldn't returned without owner
175+
[[nodiscard]] ISLEntry Pop(uint32_t curr_time) {
176+
for (auto it = start_; it && it.ExpiryTime() < curr_time; it = start_) {
177+
start_ = it.Next();
178+
ISLEntry::Destroy(it);
179+
}
164180
auto res = start_;
165181
if (start_) {
166182
start_ = start_.Next();
@@ -173,10 +189,12 @@ class IntrusiveStringList {
173189
return start_;
174190
}
175191

192+
// TODO consider to wrap ISLEntry to prevent usage out of the list
176193
ISLEntry Emplace(std::string_view key, uint32_t ttl_sec = UINT32_MAX) {
177194
return Insert(ISLEntry::Create(key, ttl_sec));
178195
}
179196

197+
// TODO consider to wrap ISLEntry to prevent usage out of the list
180198
ISLEntry Find(std::string_view str) const {
181199
auto it = start_;
182200
for (; it && it.Key() != str; it = it.Next())
@@ -240,10 +258,9 @@ class IntrusiveStringSet {
240258
: entries_(mr) {
241259
}
242260

243-
// TODO add TTL processing
244261
ISLEntry Add(std::string_view str, uint32_t ttl_sec = UINT32_MAX) {
245262
if (entries_.empty() || size_ >= entries_.size()) {
246-
Resize(Capacity() * 2);
263+
Reserve(Capacity() * 2);
247264
}
248265
const auto bucket_id = BucketId(Hash(str));
249266
auto& bucket = entries_[bucket_id];
@@ -256,7 +273,7 @@ class IntrusiveStringSet {
256273
return AddUnique(str, bucket, ttl_sec);
257274
}
258275

259-
void Resize(size_t sz) {
276+
void Reserve(size_t sz) {
260277
sz = absl::bit_ceil(sz);
261278
if (sz > entries_.size()) {
262279
size_t prev_size = entries_.size();
@@ -266,14 +283,19 @@ class IntrusiveStringSet {
266283
}
267284
}
268285

286+
void Clear() {
287+
capacity_log_ = 0;
288+
entries_.resize(0);
289+
}
290+
269291
ISLEntry AddUnique(std::string_view str, IntrusiveStringList& bucket,
270292
uint32_t ttl_sec = UINT32_MAX) {
271293
++size_;
272294
return bucket.Emplace(str, ttl_sec);
273295
}
274296

275297
unsigned AddMany(absl::Span<std::string_view> span, uint32_t ttl_sec, bool keepttl) {
276-
Resize(span.size());
298+
Reserve(span.size());
277299
unsigned res = 0;
278300
for (auto& s : span) {
279301
const auto bucket_id = BucketId(Hash(s));
@@ -322,6 +344,17 @@ class IntrusiveStringSet {
322344
return entries_idx << (32 - capacity_log_);
323345
}
324346

347+
// return unowned ISLEntry, so it should be destroyed manually
348+
[[nodiscard]] ISLEntry Pop() {
349+
for (auto& bucket : entries_) {
350+
if (auto res = bucket.Pop(time_now_); res) {
351+
--size_;
352+
return res;
353+
}
354+
}
355+
return {};
356+
}
357+
325358
bool Erase(std::string_view str) {
326359
if (entries_.empty())
327360
return false;
@@ -373,7 +406,7 @@ class IntrusiveStringSet {
373406
void Rehash(size_t prev_size) {
374407
for (int64_t i = prev_size - 1; i >= 0; --i) {
375408
auto list = std::move(entries_[i]);
376-
for (auto entry = list.Pop(); entry; entry = list.Pop()) {
409+
for (auto entry = list.Pop(time_now_); entry; entry = list.Pop(time_now_)) {
377410
auto bucket_id = BucketId(Hash(entry.Key()));
378411
entries_[bucket_id].Insert(entry);
379412
}

src/core/intrusive_string_set_test.cc

Lines changed: 60 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "base/gtest.h"
1414
#include "core/mi_memory_resource.h"
15+
#include "glog/logging.h"
1516

1617
extern "C" {
1718
#include "redis/zmalloc.h"
@@ -405,33 +406,33 @@ TEST_F(IntrusiveStringSetTest, XtremeScanGrow) {
405406
EXPECT_EQ(seen.size(), to_see.size());
406407
}
407408

408-
// TEST_F(IntrusiveStringSetTest, Pop) {
409-
// constexpr size_t num_items = 8;
410-
// unordered_set<string> to_insert;
409+
TEST_F(IntrusiveStringSetTest, Pop) {
410+
constexpr size_t num_items = 8;
411+
unordered_set<string> to_insert;
411412

412-
// while (to_insert.size() != num_items) {
413-
// auto str = random_string(generator_, 10);
414-
// if (to_insert.count(str)) {
415-
// continue;
416-
// }
413+
while (to_insert.size() != num_items) {
414+
auto str = random_string(generator_, 10);
415+
if (to_insert.count(str)) {
416+
continue;
417+
}
417418

418-
// to_insert.insert(str);
419-
// EXPECT_TRUE(ss_->Add(str));
420-
// }
419+
to_insert.insert(str);
420+
EXPECT_TRUE(ss_->Add(str));
421+
}
421422

422-
// while (!ss_->Empty()) {
423-
// size_t size = ss_->UpperBoundSize();
424-
// auto str = ss_->Pop();
425-
// DCHECK(ss_->UpperBoundSize() == to_insert.size() - 1);
426-
// DCHECK(str.has_value());
427-
// DCHECK(to_insert.count(str.value()));
428-
// DCHECK_EQ(ss_->UpperBoundSize(), size - 1);
429-
// to_insert.erase(str.value());
430-
// }
423+
while (!ss_->Empty()) {
424+
size_t size = ss_->UpperBoundSize();
425+
auto str = ss_->Pop();
426+
DCHECK(ss_->UpperBoundSize() == to_insert.size() - 1);
427+
DCHECK(str);
428+
DCHECK(to_insert.count(std::string(str.Key())));
429+
DCHECK_EQ(ss_->UpperBoundSize(), size - 1);
430+
to_insert.erase(std::string(str.Key()));
431+
}
431432

432-
// DCHECK(ss_->Empty());
433-
// DCHECK(to_insert.empty());
434-
// }
433+
DCHECK(ss_->Empty());
434+
DCHECK(to_insert.empty());
435+
}
435436

436437
// TEST_F(IntrusiveStringSetTest, Iteration) {
437438
// ss_->Add("foo");
@@ -461,15 +462,15 @@ TEST_F(IntrusiveStringSetTest, XtremeScanGrow) {
461462
// EXPECT_EQ(to_insert.size(), 0);
462463
// }
463464

464-
// TEST_F(IntrusiveStringSetTest, SetFieldExpireHasExpiry) {
465-
// EXPECT_TRUE(ss_->Add("k1", 100));
466-
// auto k = ss_->Find("k1");
467-
// EXPECT_TRUE(k.HasExpiry());
468-
// EXPECT_EQ(k.ExpiryTime(), 100);
469-
// k.SetExpiryTime(1);
470-
// EXPECT_TRUE(k.HasExpiry());
471-
// EXPECT_EQ(k.ExpiryTime(), 1);
472-
// }
465+
TEST_F(IntrusiveStringSetTest, SetFieldExpireHasExpiry) {
466+
EXPECT_TRUE(ss_->Add("k1", 100));
467+
auto k = ss_->Find("k1");
468+
EXPECT_TRUE(k.HasExpiry());
469+
EXPECT_EQ(k.ExpiryTime(), 100);
470+
k.SetExpiryTime(1);
471+
EXPECT_TRUE(k.HasExpiry());
472+
EXPECT_EQ(k.ExpiryTime(), 1);
473+
}
473474

474475
// TEST_F(IntrusiveStringSetTest, SetFieldExpireNoHasExpiry) {
475476
// EXPECT_TRUE(ss_->Add("k1"));
@@ -558,14 +559,14 @@ TEST_F(IntrusiveStringSetTest, XtremeScanGrow) {
558559
// }
559560
// }
560561

561-
// size_t memUsed(StringSet& obj) {
562+
// size_t memUsed(IntrusiveStringSet& obj) {
562563
// return obj.ObjMallocUsed() + obj.SetMallocUsed();
563564
// }
564565

565566
// void BM_Clone(benchmark::State& state) {
566567
// vector<string> strs;
567568
// mt19937 generator(0);
568-
// StringSet ss1, ss2;
569+
// IntrusiveStringSet ss1, ss2;
569570
// unsigned elems = state.range(0);
570571
// for (size_t i = 0; i < elems; ++i) {
571572
// string str = random_string(generator, 10);
@@ -588,7 +589,7 @@ TEST_F(IntrusiveStringSetTest, XtremeScanGrow) {
588589
// unsigned elems = state.range(0);
589590
// vector<string> strs;
590591
// mt19937 generator(0);
591-
// StringSet ss1, ss2;
592+
// IntrusiveStringSet ss1, ss2;
592593
// for (size_t i = 0; i < elems; ++i) {
593594
// string str = random_string(generator, 10);
594595
// ss1.Add(str);
@@ -619,30 +620,30 @@ TEST_F(IntrusiveStringSetTest, XtremeScanGrow) {
619620
// }
620621
// BENCHMARK(BM_Clear)->ArgName("elements")->Arg(32000);
621622

622-
// void BM_Add(benchmark::State& state) {
623-
// vector<string> strs;
624-
// mt19937 generator(0);
625-
// StringSet ss;
626-
// unsigned elems = state.range(0);
627-
// unsigned keySize = state.range(1);
628-
// for (size_t i = 0; i < elems; ++i) {
629-
// string str = random_string(generator, keySize);
630-
// strs.push_back(str);
631-
// }
632-
// ss.Reserve(elems);
633-
// while (state.KeepRunning()) {
634-
// for (auto& str : strs)
635-
// ss.Add(str);
636-
// state.PauseTiming();
637-
// state.counters["Memory_Used"] = memUsed(ss);
638-
// ss.Clear();
639-
// ss.Reserve(elems);
640-
// state.ResumeTiming();
641-
// }
642-
// }
643-
// BENCHMARK(BM_Add)
644-
// ->ArgNames({"elements", "Key Size"})
645-
// ->ArgsProduct({{1000, 10000, 100000}, {10, 100, 1000}});
623+
void BM_Add(benchmark::State& state) {
624+
vector<string> strs;
625+
mt19937 generator(0);
626+
IntrusiveStringSet ss;
627+
unsigned elems = state.range(0);
628+
unsigned keySize = state.range(1);
629+
for (size_t i = 0; i < elems; ++i) {
630+
string str = random_string(generator, keySize);
631+
strs.push_back(str);
632+
}
633+
ss.Reserve(elems);
634+
while (state.KeepRunning()) {
635+
for (auto& str : strs)
636+
ss.Add(str);
637+
state.PauseTiming();
638+
// state.counters["Memory_Used"] = memUsed(ss);
639+
ss.Clear();
640+
ss.Reserve(elems);
641+
state.ResumeTiming();
642+
}
643+
}
644+
BENCHMARK(BM_Add)
645+
->ArgNames({"elements", "Key Size"})
646+
->ArgsProduct({{1000, 10000, 100000}, {10, 100, 1000}});
646647

647648
// void BM_AddMany(benchmark::State& state) {
648649
// vector<string> strs;

0 commit comments

Comments
 (0)