-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathheterogeneousLookup.dox
113 lines (87 loc) · 3.99 KB
/
heterogeneousLookup.dox
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
Using Heterogeneous Lookup {#heterogeneousLookup}
============================
This page is meant to guide you through the usage of heterogeneous lookup.
## What is Heterogeneous Lookup and Why Should I Care?
Heterogeneous lookup is the process of using a different key type for performing lookups in a set or in an associative container.
It is mainly useful to help with performance in cases where it would be possible to construct a "cheaper" version of a key when performing lookups.
Let's take a look at a small cache example _not_ using heterogeneous lookup to get a better grasp of the problem it addresses.
## Typical Example (Cachemere < 0.3)
```cpp
#include <string>
#include <cachemere.h>
struct CompositeKey {
std::string first;
std::string second;
std::string third;
template<typename H> friend H AbslHashValue(H h, const CompositeKey& s);
bool operator==(const CompositeKey& other) const;
size_t capacity() const;
};
using Value = std::string;
using CacheT = cachemere::presets::memory::LRUCache<CompositeKey,
Value,
cachemere::measurement::CapacityDynamicallyAllocated<Value>,
cachemere::measurement::CapacityDynamicallyAllocated<CompositeKey>>;
bool contains(const CacheT& cache, std::string_view a, const std::string_view b, const std::string_view c) {
// We need allocate three strings to construct the key.
const std::string a_str{a};
const std::string b_str{b};
const std::string c_str{c};
const CompositeKey key{std::move(a_str), std::move(b_str), std::move(c_str)};
// Only to use a reference to it.
return cache.find(key).has_value();
}
int main() {
CacheT cache{1024 * 1024 * 1024};
cache.insert({"a", "b", "c"}, "some_value");
if (contains(cache, "a", "b", "c")) {
// [...]
}
return 0;
}
```
In this example, we often need to allocate temporary strings, because we have no way of using a structure of `std::string_view` as a key.
This is the problem that heterogeneous lookup solves. By using Cachemere's heterogeneous lookup primitives, one can use lightweight key types to do
faster lookups in a cache.
## Heterogeneous Lookup Example (Cachemere >= 0.3)
```cpp
#include <string>
#include <cachemere.h>
struct CompositeKey {
std::string first;
std::string second;
std::string third;
template<typename H> friend H AbslHashValue(H h, const CompositeKey& s);
bool operator==(const CompositeKey& other) const;
size_t capacity() const;
};
struct CompositeView {
std::string_view a;
std::string_view b;
std::string_view c;
template<typename H> friend H AbslHashValue(H h, const CompositeView& s);
bool operator==(const CompositeView& other) const;
bool operator==(const CompositeKey& other) const; // Key view must be comparable to the regular key.
};
// This hasher is able to hash both `CompositeKey` and `CompositeView`.
using MultiHasher = cachemere::MultiHash<CompositeKey, absl::Hash<CompositeKey>, CompositeView, absl::Hash<CompositeView>>;
using Value = std::string;
using CacheT = cachemere::presets::memory::LRUCache<CompositeKey,
Value,
cachemere::measurement::CapacityDynamicallyAllocated<Value>,
cachemere::measurement::CapacityDynamicallyAllocated<CompositeKey>,
MultiHasher>;
bool contains(const CacheT& cache, std::string_view a, const std::string_view b, const std::string_view c) {
// We can use the lightweight key directly, without allocating a single string.
const CompositeView key{a, b, c};
return cache.find(key).has_value();
}
int main() {
CacheT cache{1024 * 1024 * 1024};
cache.insert({"a", "b", "c"}, "some_value");
if (contains(cache, "a", "b", "c")) {
// [...]
}
return 0;
}
```