1818
1919#include < folly/logging/xlog.h>
2020
21+ #include < deque>
22+ #include < forward_list>
23+ #include < list>
24+ #include < map>
2125#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>
2236
2337#include " cachelib/allocator/memory/Slab.h"
2438
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+
2585namespace facebook {
2686namespace cachelib {
2787
@@ -31,20 +91,22 @@ template <typename PtrType, typename AllocatorContainer>
3191class PtrCompressor ;
3292
3393// 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.
41103//
42104// This CompressedPtr makes decompression fast by staying away from division and
43105// modulo arithmetic and doing those during the compression time. We most often
44106// decompress a CompressedPtr than compress a pointer while creating one.
45107class CACHELIB_PACKED_ATTR CompressedPtr {
46108 public:
47- using PtrType = uint64_t ;
109+ using PtrType = uint32_t ;
48110 // Thrift doesn't support unsigned type
49111 using SerializedPtrType = int64_t ;
50112
@@ -103,26 +165,28 @@ class CACHELIB_PACKED_ATTR CompressedPtr {
103165 static constexpr unsigned int kNumAllocIdxBits =
104166 Slab::kNumSlabBits - Slab::kMinAllocPower ;
105167
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 ;
109170
110171 static constexpr PtrType kAllocIdxMask = ((PtrType)1 << kNumAllocIdxBits ) - 1 ;
111172
112173 // 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 ;
114175
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
116177 // compressed ptr.
117178 static constexpr unsigned int kNumSlabIdxBits =
118- NumBits<PtrType>::value - kNumTierIdxOffset - kNumAllocIdxBits ;
179+ kNumTierIdxOffset - kNumAllocIdxBits ;
119180
120181 // Compress the given slabIdx and allocIdx into a 64-bit compressed
121182 // 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 {
123186 XDCHECK_LE (allocIdx, kAllocIdxMask );
124187 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;
126190 }
127191
128192 // Get the slab index of the compressed ptr
@@ -153,62 +217,44 @@ class CACHELIB_PACKED_ATTR CompressedPtr {
153217 friend class PtrCompressor ;
154218};
155219
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-
183220template <typename PtrType, typename AllocatorContainer>
184221class PtrCompressor {
185222 public:
186223 explicit PtrCompressor (const AllocatorContainer& allocators) noexcept
187- : allocators_(allocators) {}
224+ : allocators_(allocators),
225+ isContainer_(IsContainer<decltype (allocators)>::value) {}
188226
189227 const CompressedPtr compress (const PtrType* uncompressed) const {
190- if (uncompressed == nullptr )
228+ if (uncompressed == nullptr ) {
191229 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 ;
197230 }
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+ }
203245 }
204246
205247 PtrType* unCompress (const CompressedPtr compressed) const {
206248 if (compressed.isNull ()) {
207249 return nullptr ;
208250 }
251+ if (isContainer_) {
252+ auto & allocator = *allocators_[compressed.getTierId ()];
253+ return static_cast <PtrType*>(allocator.unCompress (compressed));
209254
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+ }
212258 }
213259
214260 bool operator ==(const PtrCompressor& rhs) const noexcept {
@@ -222,6 +268,8 @@ class PtrCompressor {
222268 private:
223269 // memory allocator that does the pointer compression.
224270 const AllocatorContainer& allocators_;
271+
272+ bool isContainer_{false };
225273};
226274} // namespace cachelib
227275} // namespace facebook
0 commit comments