Skip to content

Commit

Permalink
chore: Implement AddMany method
Browse files Browse the repository at this point in the history
1. Fix a performance bug in Find2 that made redundant comparisons
2. Provide a method to StringSet that adds several items in a batch
3. Use AddMany inside set_family

Before:
```
BM_Add        4253939 ns      4253713 ns          991
```

After:
```
BM_Add        3482177 ns      3482050 ns         1206
BM_AddMany    3101622 ns      3101507 ns         1360
```

Signed-off-by: Roman Gershman <[email protected]>
  • Loading branch information
romange committed Oct 4, 2024
1 parent a86fcf8 commit 28b5d44
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 8 deletions.
21 changes: 15 additions & 6 deletions src/core/dense_set.cc
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,11 @@ void DenseSet::AddUnique(void* obj, bool has_ttl, uint64_t hashcode) {
++size_;
}

void DenseSet::Prefetch(uint64_t hash) {
uint32_t bid = BucketId(hash);
PREFETCH_READ(&entries_[bid]);
}

auto DenseSet::Find2(const void* ptr, uint32_t bid, uint32_t cookie)
-> tuple<size_t, DensePtr*, DensePtr*> {
DCHECK_LT(bid, entries_.size());
Expand All @@ -563,19 +568,23 @@ auto DenseSet::Find2(const void* ptr, uint32_t bid, uint32_t cookie)
// first look for displaced nodes since this is quicker than iterating a potential long chain
if (bid > 0) {
curr = &entries_[bid - 1];
ExpireIfNeeded(nullptr, curr);
if (curr->IsDisplaced() && curr->GetDisplacedDirection() == -1) {
ExpireIfNeeded(nullptr, curr);

if (Equal(*curr, ptr, cookie)) {
return {bid - 1, nullptr, curr};
if (Equal(*curr, ptr, cookie)) {
return {bid - 1, nullptr, curr};
}
}
}

if (bid + 1 < entries_.size()) {
curr = &entries_[bid + 1];
ExpireIfNeeded(nullptr, curr);
if (curr->IsDisplaced() && curr->GetDisplacedDirection() == 1) {
ExpireIfNeeded(nullptr, curr);

if (Equal(*curr, ptr, cookie)) {
return {bid + 1, nullptr, curr};
if (Equal(*curr, ptr, cookie)) {
return {bid + 1, nullptr, curr};
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/core/dense_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ class DenseSet {

public:
using MemoryResource = PMR_NS::memory_resource;
static constexpr uint32_t kMaxBatchLen = 32;

explicit DenseSet(MemoryResource* mr = PMR_NS::get_default_resource());
virtual ~DenseSet();
Expand Down Expand Up @@ -317,6 +318,8 @@ class DenseSet {
// Assumes that the object does not exist in the set.
void AddUnique(void* obj, bool has_ttl, uint64_t hashcode);

void Prefetch(uint64_t hash);

private:
DenseSet(const DenseSet&) = delete;
DenseSet& operator=(DenseSet&) = delete;
Expand Down
32 changes: 32 additions & 0 deletions src/core/string_set.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,38 @@ bool StringSet::Add(string_view src, uint32_t ttl_sec) {
return true;
}

void StringSet::AddMany(string_view elems[], size_t count, uint32_t ttl_sec, bool* res) {
uint64_t hash[kMaxBatchLen];
string_view* data = elems;
bool has_ttl = ttl_sec != UINT32_MAX;

while (count >= kMaxBatchLen) {
for (unsigned i = 0; i < kMaxBatchLen; ++i) {
hash[i] = CompactObj::HashCode(data[i]);
Prefetch(hash[i]);
}

for (unsigned i = 0; i < kMaxBatchLen; ++i) {
void* prev = FindInternal(data + i, hash[i], 1);
if (prev != nullptr) {
res[i] = false;
} else {
res[i] = true;
sds field = MakeSetSds(data[i], ttl_sec);
AddUnique(field, has_ttl, hash[i]);
}
}

count -= kMaxBatchLen;
data += kMaxBatchLen;
res += kMaxBatchLen;
}

for (unsigned i = 0; i < count; ++i) {
res[i] = Add(data[i], ttl_sec);
}
}

std::optional<std::string> StringSet::Pop() {
sds str = (sds)PopInternal();

Expand Down
2 changes: 2 additions & 0 deletions src/core/string_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class StringSet : public DenseSet {
// Returns true if elem was added.
bool Add(std::string_view s1, uint32_t ttl_sec = UINT32_MAX);

void AddMany(std::string_view elems[], size_t count, uint32_t ttl_sec, bool* res);

bool Erase(std::string_view str) {
return EraseInternal(&str, 1);
}
Expand Down
31 changes: 31 additions & 0 deletions src/core/string_set_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -552,4 +552,35 @@ void BM_Add(benchmark::State& state) {
}
BENCHMARK(BM_Add);

void BM_AddMany(benchmark::State& state) {
vector<string> strs;
mt19937 generator(0);
StringSet ss;
unsigned elems = 100000;
for (size_t i = 0; i < elems; ++i) {
string str = random_string(generator, 16);
strs.push_back(str);
}
ss.Reserve(elems);
array<string_view, 32> str_views;

while (state.KeepRunning()) {
unsigned offset = 0;
while (offset < elems) {
bool res[str_views.size()];
unsigned len = min(elems - offset, 32u);
for (size_t i = 0; i < len; ++i) {
str_views[i] = strs[offset + i];
}
offset += len;
ss.AddMany(str_views.data(), len, UINT32_MAX, res);
}
state.PauseTiming();
ss.Clear();
ss.Reserve(elems);
state.ResumeTiming();
}
}
BENCHMARK(BM_AddMany);

} // namespace dfly
21 changes: 19 additions & 2 deletions src/server/set_family.cc
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,25 @@ struct StringSetWrapper {

unsigned Add(const NewEntries& entries, uint32_t ttl_sec) const {
unsigned res = 0;
for (string_view member : EntriesRange(entries))
res += ss->Add(member, ttl_sec);
string_view members[StringSet::kMaxBatchLen];
bool res_arr[StringSet::kMaxBatchLen];
unsigned len = 0;
for (string_view member : EntriesRange(entries)) {
members[len++] = member;
if (len == StringSet::kMaxBatchLen) {
ss->AddMany(members, StringSet::kMaxBatchLen, ttl_sec, res_arr);
for (unsigned i = 0; i < StringSet::kMaxBatchLen; ++i)
res += res_arr[i];
len = 0;
}
}

if (len) {
ss->AddMany(members, len, ttl_sec, res_arr);
for (unsigned i = 0; i < len; ++i)
res += res_arr[i];
}

return res;
}

Expand Down

0 comments on commit 28b5d44

Please sign in to comment.