18
18
19
19
#include < folly/logging/xlog.h>
20
20
21
+ #include < deque>
22
+ #include < forward_list>
23
+ #include < list>
24
+ #include < map>
21
25
#include < memory>
26
+ #include < queue>
27
+ #include < set>
28
+ #include < stack>
29
+ #include < string>
30
+ #include < tuple>
31
+ #include < type_traits>
32
+ #include < unordered_map>
33
+ #include < unordered_set>
34
+ #include < utility>
35
+ #include < vector>
22
36
23
37
#include " cachelib/allocator/memory/Slab.h"
24
38
39
+ // specialize a type for all of the STL containers.
40
+ namespace IsContainerImpl {
41
+ template <typename T>
42
+ struct IsContainer : std::false_type {};
43
+ template <typename T, std::size_t N>
44
+ struct IsContainer <std::array<T, N>> : std::true_type {};
45
+ template <typename ... Args>
46
+ struct IsContainer <std::vector<Args...>> : std::true_type {};
47
+ template <typename ... Args>
48
+ struct IsContainer <std::deque<Args...>> : std::true_type {};
49
+ template <typename ... Args>
50
+ struct IsContainer <std::list<Args...>> : std::true_type {};
51
+ template <typename ... Args>
52
+ struct IsContainer <std::forward_list<Args...>> : std::true_type {};
53
+ template <typename ... Args>
54
+ struct IsContainer <std::set<Args...>> : std::true_type {};
55
+ template <typename ... Args>
56
+ struct IsContainer <std::multiset<Args...>> : std::true_type {};
57
+ template <typename ... Args>
58
+ struct IsContainer <std::map<Args...>> : std::true_type {};
59
+ template <typename ... Args>
60
+ struct IsContainer <std::multimap<Args...>> : std::true_type {};
61
+ template <typename ... Args>
62
+ struct IsContainer <std::unordered_set<Args...>> : std::true_type {};
63
+ template <typename ... Args>
64
+ struct IsContainer <std::unordered_multiset<Args...>> : std::true_type {};
65
+ template <typename ... Args>
66
+ struct IsContainer <std::unordered_map<Args...>> : std::true_type {};
67
+ template <typename ... Args>
68
+ struct IsContainer <std::unordered_multimap<Args...>> : std::true_type {};
69
+ template <typename ... Args>
70
+ struct IsContainer <std::stack<Args...>> : std::true_type {};
71
+ template <typename ... Args>
72
+ struct IsContainer <std::queue<Args...>> : std::true_type {};
73
+ template <typename ... Args>
74
+ struct IsContainer <std::priority_queue<Args...>> : std::true_type {};
75
+ } // namespace IsContainerImpl
76
+
77
+ // type trait to utilize the implementation type traits as well as decay the
78
+ // type
79
+ template <typename T>
80
+ struct IsContainer {
81
+ static constexpr bool const value =
82
+ IsContainerImpl::IsContainer<std::decay_t <T>>::value;
83
+ };
84
+
25
85
namespace facebook {
26
86
namespace cachelib {
27
87
@@ -31,20 +91,22 @@ template <typename PtrType, typename AllocatorContainer>
31
91
class PtrCompressor ;
32
92
33
93
// the following are for pointer compression for the memory allocator. We
34
- // compress pointers by storing the slab index and the alloc index of the
35
- // allocation inside the slab. With slab worth kNumSlabBits of data, if we
36
- // have the min allocation size as 64 bytes, that requires kNumSlabBits - 6
37
- // bits for storing the alloc index. This leaves the remaining (32 -
38
- // (kNumSlabBits - 6)) bits for the slab index. Hence we can index 256 GiB
39
- // of memory in slabs and index anything more than 64 byte allocations inside
40
- // the slab using a 32 bit representation.
94
+ // compress pointers by storing the tier index, slab index and alloc index
95
+ // of the allocation inside the slab. With slab worth kNumSlabBits (22 bits)
96
+ // of data, if we have the min allocation size as 64 bytes, that requires
97
+ // kNumSlabBits - 6 = 16 bits for storing the alloc index. The tier id
98
+ // occupies the 32nd bit only since its value cannot exceed kMaxTiers (2).
99
+ // This leaves the remaining (32 -(kNumSlabBits - 6) - 1 bit for tier id) =
100
+ // 15 bits for the slab index. Hence we can index 128 GiB of memory in slabs
101
+ // per tier and index anything more than 64 byte allocations inside the slab
102
+ // using a 32 bit representation.
41
103
//
42
104
// This CompressedPtr makes decompression fast by staying away from division and
43
105
// modulo arithmetic and doing those during the compression time. We most often
44
106
// decompress a CompressedPtr than compress a pointer while creating one.
45
107
class CACHELIB_PACKED_ATTR CompressedPtr {
46
108
public:
47
- using PtrType = uint64_t ;
109
+ using PtrType = uint32_t ;
48
110
// Thrift doesn't support unsigned type
49
111
using SerializedPtrType = int64_t ;
50
112
@@ -103,26 +165,28 @@ class CACHELIB_PACKED_ATTR CompressedPtr {
103
165
static constexpr unsigned int kNumAllocIdxBits =
104
166
Slab::kNumSlabBits - Slab::kMinAllocPower ;
105
167
106
- // Use topmost 32 bits for TierId
107
- // XXX: optimize
108
- static constexpr unsigned int kNumTierIdxOffset = 32 ;
168
+ // Use the top bit for tier id
169
+ static constexpr unsigned int kNumTierIdxOffset = 31 ;
109
170
110
171
static constexpr PtrType kAllocIdxMask = ((PtrType)1 << kNumAllocIdxBits ) - 1 ;
111
172
112
173
// kNumTierIdxBits most significant bits
113
- static constexpr PtrType kTierIdxMask = ((( PtrType)1 << kNumTierIdxOffset ) - 1 ) << (NumBits<PtrType>::value - kNumTierIdxOffset ) ;
174
+ static constexpr PtrType kTierIdxMask = (PtrType)1 << kNumTierIdxOffset ;
114
175
115
- // Number of bits for the slab index. This will be the top 16 bits of the
176
+ // Number of bits for the slab index. This will be the 16th - 31st bits of the
116
177
// compressed ptr.
117
178
static constexpr unsigned int kNumSlabIdxBits =
118
- NumBits<PtrType>::value - kNumTierIdxOffset - kNumAllocIdxBits ;
179
+ kNumTierIdxOffset - kNumAllocIdxBits ;
119
180
120
181
// Compress the given slabIdx and allocIdx into a 64-bit compressed
121
182
// pointer.
122
- static PtrType compress (uint32_t slabIdx, uint32_t allocIdx, TierId tid) noexcept {
183
+ static PtrType compress (uint32_t slabIdx,
184
+ uint32_t allocIdx,
185
+ TierId tid) noexcept {
123
186
XDCHECK_LE (allocIdx, kAllocIdxMask );
124
187
XDCHECK_LT (slabIdx, (1u << kNumSlabIdxBits ) - 1 );
125
- return (static_cast <uint64_t >(tid) << kNumTierIdxOffset ) + (slabIdx << kNumAllocIdxBits ) + allocIdx;
188
+ return (static_cast <uint64_t >(tid) << kNumTierIdxOffset ) +
189
+ (slabIdx << kNumAllocIdxBits ) + allocIdx;
126
190
}
127
191
128
192
// Get the slab index of the compressed ptr
@@ -153,62 +217,44 @@ class CACHELIB_PACKED_ATTR CompressedPtr {
153
217
friend class PtrCompressor ;
154
218
};
155
219
156
- template <typename PtrType, typename AllocatorT>
157
- class SingleTierPtrCompressor {
158
- public:
159
- explicit SingleTierPtrCompressor (const AllocatorT& allocator) noexcept
160
- : allocator_(allocator) {}
161
-
162
- const CompressedPtr compress (const PtrType* uncompressed) const {
163
- return allocator_.compress (uncompressed);
164
- }
165
-
166
- PtrType* unCompress (const CompressedPtr compressed) const {
167
- return static_cast <PtrType*>(allocator_.unCompress (compressed));
168
- }
169
-
170
- bool operator ==(const SingleTierPtrCompressor& rhs) const noexcept {
171
- return &allocator_ == &rhs.allocator_ ;
172
- }
173
-
174
- bool operator !=(const SingleTierPtrCompressor& rhs) const noexcept {
175
- return !(*this == rhs);
176
- }
177
-
178
- private:
179
- // memory allocator that does the pointer compression.
180
- const AllocatorT& allocator_;
181
- };
182
-
183
220
template <typename PtrType, typename AllocatorContainer>
184
221
class PtrCompressor {
185
222
public:
186
223
explicit PtrCompressor (const AllocatorContainer& allocators) noexcept
187
- : allocators_(allocators) {}
224
+ : allocators_(allocators),
225
+ isContainer_(IsContainer<decltype (allocators)>::value) {}
188
226
189
227
const CompressedPtr compress (const PtrType* uncompressed) const {
190
- if (uncompressed == nullptr )
228
+ if (uncompressed == nullptr ) {
191
229
return CompressedPtr{};
192
-
193
- TierId tid;
194
- for (tid = 0 ; tid < allocators_.size (); tid++) {
195
- if (allocators_[tid]->isMemoryInAllocator (static_cast <const void *>(uncompressed)))
196
- break ;
197
230
}
198
-
199
- auto cptr = allocators_[tid]->compress (uncompressed);
200
- cptr.setTierId (tid);
201
-
202
- return cptr;
231
+ if (isContainer_) {
232
+ TierId tid;
233
+ for (tid = 0 ; tid < allocators_.size (); tid++) {
234
+ if (allocators_[tid]->isMemoryInAllocator (
235
+ static_cast <const void *>(uncompressed)))
236
+ break ;
237
+ }
238
+ auto cptr = allocators_[tid]->compress (uncompressed);
239
+ cptr.setTierId (tid);
240
+ return cptr;
241
+
242
+ } else {
243
+ return allocators_.compress (uncompressed);
244
+ }
203
245
}
204
246
205
247
PtrType* unCompress (const CompressedPtr compressed) const {
206
248
if (compressed.isNull ()) {
207
249
return nullptr ;
208
250
}
251
+ if (isContainer_) {
252
+ auto & allocator = *allocators_[compressed.getTierId ()];
253
+ return static_cast <PtrType*>(allocator.unCompress (compressed));
209
254
210
- auto &allocator = *allocators_[compressed.getTierId ()];
211
- return static_cast <PtrType*>(allocator.unCompress (compressed));
255
+ } else {
256
+ return static_cast <PtrType*>(allocators_.unCompress (compressed));
257
+ }
212
258
}
213
259
214
260
bool operator ==(const PtrCompressor& rhs) const noexcept {
@@ -222,6 +268,8 @@ class PtrCompressor {
222
268
private:
223
269
// memory allocator that does the pointer compression.
224
270
const AllocatorContainer& allocators_;
271
+
272
+ bool isContainer_{false };
225
273
};
226
274
} // namespace cachelib
227
275
} // namespace facebook
0 commit comments