Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions benchmarks/workload.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,34 @@ struct Offsets {
size_t num;
BaseGenerator* gen;

struct EndIterator : std::iterator<std::input_iterator_tag, off_t> {
struct EndIterator {
using iterator_category = std::input_iterator_tag;
using value_type = off_t;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;

size_t num;
explicit EndIterator(size_t num) : num(num) {}
};

struct Iterator : std::iterator<std::input_iterator_tag, off_t> {
struct Iterator {
using iterator_category = std::input_iterator_tag;
using value_type = off_t;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;

BaseGenerator& gen;
explicit Iterator(BaseGenerator& gen) : gen(gen) {}

Iterator& operator++() {
gen.next();
return *this;
}
off_t operator*() const { return gen.get(); }

value_type operator*() const { return gen.get(); }

bool operator!=(const EndIterator& other) const {
return gen.index < other.num;
}
Expand Down
28 changes: 4 additions & 24 deletions include/gcache/ghost_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,45 +121,25 @@ class GhostCache {
// For each item in the LRU list, call fn in LRU order
template <typename Fn>
void for_each_lru(Fn&& fn) const {
cache.for_each_lru([&fn](Handle_t h) { fn(h.get_key()); });
cache.for_each_lru([&fn](const Handle_t h) { fn(h); });
}

// For each item in the LRU list, call fn in MRU order
template <typename Fn>
void for_each_mru(Fn&& fn) const {
cache.for_each_mru([&fn](Handle_t h) { fn(h.get_key()); });
cache.for_each_mru([&fn](const Handle_t h) { fn(h); });
}

// For each item in the LRU list, call fn in LRU order until false
template <typename Fn>
void for_each_until_lru(Fn&& fn) const {
cache.for_each_until_lru([&fn](Handle_t h) { fn(h.get_key()); });
cache.for_each_until_lru([&fn](const Handle_t h) { fn(h); });
}

// For each item in the LRU list, call fn in MRU order until false
template <typename Fn>
void for_each_until_mru(Fn&& fn) const {
cache.for_each_until_mru([&fn](Handle_t h) { fn(h.get_key()); });
}

protected:
// The for-each APIs below are unsafe because they expose the entire
// handle including size_idx; should only be called by friend classes
template <typename Fn>
void unsafe_for_each_lru(Fn&& fn) const {
cache.for_each_lru(fn);
}
template <typename Fn>
void unsafe_for_each_mru(Fn&& fn) const {
cache.for_each_mru(fn);
}
template <typename Fn>
void unsafe_for_each_until_lru(Fn&& fn) const {
cache.for_each_until_lru(fn);
}
template <typename Fn>
void unsafe_for_each_until_mru(Fn&& fn) const {
cache.for_each_until_mru(fn);
cache.for_each_until_mru([&fn](const Handle_t h) { fn(h); });
}

public:
Expand Down
19 changes: 15 additions & 4 deletions include/gcache/ghost_kv_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class SampledGhostKvCache {
public:
SampledGhostKvCache(SizeType tick, SizeType min_count, SizeType max_count)
: ghost_cache(tick, min_count, max_count) {
static_assert(SampleShift <= 32, "SampleShift must be no larger than 32");
static_assert(SampleShift <= std::numeric_limits<SizeType>::digits);
}

void access(const std::string_view key, SizeType kv_size,
Expand All @@ -48,7 +48,8 @@ class SampledGhostKvCache {
AccessMode mode = AccessMode::DEFAULT) {
// only with certain number of leading zeros is sampled
if constexpr (SampleShift > 0) {
if (key_hash >> (32 - SampleShift)) return;
if (key_hash >> (std::numeric_limits<SizeType>::digits - SampleShift))
return;
}
auto h = ghost_cache.access_impl(key_hash, key_hash, mode);
h->kv_size = kv_size;
Expand Down Expand Up @@ -106,15 +107,25 @@ class SampledGhostKvCache {
std::vector<std::tuple<SizeType, size_t, CacheStat>> curve;
SizeType curr_count = 0;
size_t curr_size = 0;
ghost_cache.unsafe_for_each_mru([&](Handle_t h) {
curr_size += h->kv_size;
ghost_cache.for_each_mru([&](const Handle_t h) {
++curr_count;
curr_size += h->kv_size;
if (curr_count >= ghost_cache.min_size &&
(curr_count - ghost_cache.min_size) % ghost_cache.tick == 0) {
curve.emplace_back(curr_count << SampleShift, curr_size << SampleShift,
ghost_cache.get_stat_shifted(curr_count));
}
});
// the last handle may not be at a tick, which can happen when the working
// set is smaller than max_size; we need to manually add this tick
if ((curr_count > ghost_cache.min_size) &&
(curr_count - ghost_cache.min_size) % ghost_cache.tick != 0) {
// round up to the next tick
auto next_count = (curr_count + ghost_cache.tick - 1) / ghost_cache.tick *
ghost_cache.tick;
curve.emplace_back(next_count << SampleShift, curr_size << SampleShift,
ghost_cache.get_stat_shifted(next_count));
}
return curve;
// should be implicitly moved by compiler
// avoid explict move for Return Value Optimization (RVO)
Expand Down
21 changes: 13 additions & 8 deletions tests/test_ghost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <random>

#include "gcache/ghost_cache.h"
#include "gcache/node.h"
Expand All @@ -15,6 +16,9 @@ constexpr const uint32_t large_bench_size = 2 * 1024 * 1024; // 8 GB cache

constexpr const uint32_t sample_shift = 5;

std::random_device rd; // non-deterministic random device
std::mt19937 urbg(rd()); // UniformRandomBitGenerator

void test1() {
std::cout << "=== Test 1 ===\n";
GhostCache<> ghost_cache(1, 3, 6);
Expand Down Expand Up @@ -134,7 +138,8 @@ void test3() {
<< std::endl;

std::vector<uint32_t> ckpt;
ghost_cache.for_each_lru([&ckpt](uint32_t key) { ckpt.emplace_back(key); });
ghost_cache.for_each_lru(
[&ckpt](GhostCache<>::Handle_t h) { ckpt.emplace_back(h.get_key()); });

GhostCache<> ghost_cache2(3, 2, 11);
for (auto key : ckpt) ghost_cache2.access(key, AccessMode::NOOP);
Expand Down Expand Up @@ -236,23 +241,23 @@ void bench3() {
}
ghost_cache.reset_stat();
sampled_ghost_cache.reset_stat();
std::random_shuffle(reqs.begin(), reqs.end());
std::shuffle(reqs.begin(), reqs.end(), urbg);

uint64_t elapse_g = 0;
uint64_t elapse_s = 0;
uint64_t ts0 = rdtsc();
for (uint32_t i = 0; i < num_ops / reqs.size(); ++i) {
for (auto j : reqs) ghost_cache.access(j);
elapse_g += rdtsc() - ts0;
std::random_shuffle(reqs.begin(), reqs.end());
std::shuffle(reqs.begin(), reqs.end(), urbg);
ts0 = rdtsc();
}

ts0 = rdtsc();
for (uint32_t i = 0; i < num_ops / reqs.size(); ++i) {
for (auto j : reqs) sampled_ghost_cache.access(j);
elapse_s += rdtsc() - ts0;
std::random_shuffle(reqs.begin(), reqs.end());
std::shuffle(reqs.begin(), reqs.end(), urbg);
ts0 = rdtsc();
}

Expand Down Expand Up @@ -291,23 +296,23 @@ void bench4() {
}
ghost_cache.reset_stat();
sampled_ghost_cache.reset_stat();
std::random_shuffle(reqs.begin(), reqs.end());
std::shuffle(reqs.begin(), reqs.end(), urbg);

uint64_t elapse_g = 0;
uint64_t elapse_s = 0;
uint64_t ts0 = rdtsc();
for (uint32_t i = 0; i < num_ops / reqs.size(); ++i) {
for (auto j : reqs) ghost_cache.access(j);
elapse_g += rdtsc() - ts0;
std::random_shuffle(reqs.begin(), reqs.end());
std::shuffle(reqs.begin(), reqs.end(), urbg);
ts0 = rdtsc();
}

ts0 = rdtsc();
for (uint32_t i = 0; i < num_ops / reqs.size(); ++i) {
for (auto j : reqs) sampled_ghost_cache.access(j);
elapse_s += rdtsc() - ts0;
std::random_shuffle(reqs.begin(), reqs.end());
std::shuffle(reqs.begin(), reqs.end(), urbg);
ts0 = rdtsc();
}

Expand Down Expand Up @@ -348,7 +353,7 @@ void bench5() {
reqs.emplace_back(rand() % large_bench_size);
ghost_cache.reset_stat();
sampled_ghost_cache.reset_stat();
std::random_shuffle(reqs.begin(), reqs.end());
std::shuffle(reqs.begin(), reqs.end(), urbg);

uint64_t ts0;
ts0 = rdtsc();
Expand Down
6 changes: 5 additions & 1 deletion tests/test_ghost_kv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <random>
#include <string>

#include "gcache/ghost_cache.h"
Expand All @@ -15,6 +16,9 @@ constexpr const uint32_t num_ops = 1 * 1024 * 1024;
constexpr const uint32_t bench_size = 1 * 1024 * 1024; // 1m keys
constexpr const uint32_t sample_shift = 5;

std::random_device rd; // non-deterministic random device
std::mt19937 urbg(rd()); // UniformRandomBitGenerator

std::string make_key(int k) {
std::ostringstream stream;
stream << std::setw(16) << std::setfill('0') << k;
Expand All @@ -37,7 +41,7 @@ void bench1() {
}

for (uint32_t i = 0; i < num_ops; ++i) reqs.emplace_back(rand() % bench_size);
std::random_shuffle(reqs.begin(), reqs.end());
std::shuffle(reqs.begin(), reqs.end(), urbg);
for (auto i : reqs) reqs2.emplace_back(i, make_key(i));

uint64_t ts0;
Expand Down