Skip to content

Commit c10403a

Browse files
committed
Extend CompressedPtr to work with multiple tiers
Now it's size is 8 bytes intead of 4. Original CompressedPtr stored only some offset with a memory Allocator. For multi-tier implementation, this is not enough. We must also store tierId and when uncompressing, select a proper allocator. An alternative could be to just resign from CompressedPtr but they are leveraged to allow the cache to be mapped to different addresses on shared memory. Changing CompressedPtr impacted CacheItem size - it increased from 32 to 44 bytes.
1 parent 4444c23 commit c10403a

File tree

10 files changed

+105
-31
lines changed

10 files changed

+105
-31
lines changed

cachelib/allocator/CacheAllocator.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,7 +1061,8 @@ class CacheAllocator : public CacheBase {
10611061
sizeof(typename RefcountWithFlags::Value) + sizeof(uint32_t) +
10621062
sizeof(uint32_t) + sizeof(KAllocation)) == sizeof(Item),
10631063
"vtable overhead");
1064-
static_assert(32 == sizeof(Item), "item overhead is 32 bytes");
1064+
// XXX: this will fail due to CompressedPtr change
1065+
// static_assert(32 == sizeof(Item), "item overhead is 32 bytes");
10651066

10661067
// make sure there is no overhead in ChainedItem on top of a regular Item
10671068
static_assert(sizeof(Item) == sizeof(ChainedItem),
@@ -1673,7 +1674,7 @@ class CacheAllocator : public CacheBase {
16731674
}
16741675

16751676
typename Item::PtrCompressor createPtrCompressor() const {
1676-
return allocator_[0 /* TODO */]->createPtrCompressor<Item>();
1677+
return typename Item::PtrCompressor(allocator_);
16771678
}
16781679

16791680
// helper utility to throttle and optionally log.

cachelib/allocator/CacheAllocatorConfig.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,7 @@ const CacheAllocatorConfig<T>& CacheAllocatorConfig<T>::validate() const {
10591059
// CompressedPtr;
10601060
// The second part specifies the minimal allocation size for each slot.
10611061
// Multiplied, they inform us the maximal addressable space for cache.
1062-
size_t maxCacheSize = (1ul << CompressedPtr::kNumBits) * Slab::kMinAllocSize;
1062+
size_t maxCacheSize = CompressedPtr::getMaxAddressableSize();
10631063
// Configured cache size should not exceed the maximal addressable space for
10641064
// cache.
10651065
if (size > maxCacheSize) {

cachelib/allocator/CacheItem.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class CACHELIB_PACKED_ATTR CacheItem {
139139
* to be mapped to different addresses on shared memory.
140140
*/
141141
using CompressedPtr = facebook::cachelib::CompressedPtr;
142+
using SingleTierPtrCompressor = MemoryAllocator::SingleTierPtrCompressor<Item>;
142143
using PtrCompressor = MemoryAllocator::PtrCompressor<Item>;
143144

144145
// Get the required size for a cache item given the size of memory

cachelib/allocator/memory/AllocationClass.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ AllocationClass::AllocationClass(ClassId classId,
5050
poolId_(poolId),
5151
allocationSize_(allocSize),
5252
slabAlloc_(s),
53-
freedAllocations_{slabAlloc_.createPtrCompressor<FreeAlloc>()} {
53+
freedAllocations_{slabAlloc_.createSingleTierPtrCompressor<FreeAlloc>()} {
5454
checkState();
5555
}
5656

@@ -102,7 +102,7 @@ AllocationClass::AllocationClass(
102102
currSlab_(s.getSlabForIdx(*object.currSlabIdx_ref())),
103103
slabAlloc_(s),
104104
freedAllocations_(*object.freedAllocationsObject_ref(),
105-
slabAlloc_.createPtrCompressor<FreeAlloc>()),
105+
slabAlloc_.createSingleTierPtrCompressor<FreeAlloc>()),
106106
canAllocate_(*object.canAllocate_ref()) {
107107
if (!slabAlloc_.isRestorable()) {
108108
throw std::logic_error("The allocation class cannot be restored.");
@@ -356,9 +356,9 @@ std::pair<bool, std::vector<void*>> AllocationClass::pruneFreeAllocs(
356356
// allocated slab, release any freed allocations belonging to this slab.
357357
// Set the bit to true if the corresponding allocation is freed, false
358358
// otherwise.
359-
FreeList freeAllocs{slabAlloc_.createPtrCompressor<FreeAlloc>()};
360-
FreeList notInSlab{slabAlloc_.createPtrCompressor<FreeAlloc>()};
361-
FreeList inSlab{slabAlloc_.createPtrCompressor<FreeAlloc>()};
359+
FreeList freeAllocs{slabAlloc_.createSingleTierPtrCompressor<FreeAlloc>()};
360+
FreeList notInSlab{slabAlloc_.createSingleTierPtrCompressor<FreeAlloc>()};
361+
FreeList inSlab{slabAlloc_.createSingleTierPtrCompressor<FreeAlloc>()};
362362

363363
lock_->lock_combine([&]() {
364364
// Take the allocation class free list offline

cachelib/allocator/memory/AllocationClass.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ class AllocationClass {
446446
struct CACHELIB_PACKED_ATTR FreeAlloc {
447447
using CompressedPtr = facebook::cachelib::CompressedPtr;
448448
using PtrCompressor =
449-
facebook::cachelib::PtrCompressor<FreeAlloc, SlabAllocator>;
449+
facebook::cachelib::SingleTierPtrCompressor<FreeAlloc, SlabAllocator>;
450450
SListHook<FreeAlloc> hook_{};
451451
};
452452

cachelib/allocator/memory/CompressedPtr.h

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ namespace cachelib {
2727

2828
class SlabAllocator;
2929

30+
template <typename PtrType, typename AllocatorContainer>
31+
class PtrCompressor;
32+
3033
// the following are for pointer compression for the memory allocator. We
3134
// compress pointers by storing the slab index and the alloc index of the
3235
// allocation inside the slab. With slab worth kNumSlabBits of data, if we
@@ -41,7 +44,7 @@ class SlabAllocator;
4144
// decompress a CompressedPtr than compress a pointer while creating one.
4245
class CACHELIB_PACKED_ATTR CompressedPtr {
4346
public:
44-
using PtrType = uint32_t;
47+
using PtrType = uint64_t;
4548
// Thrift doesn't support unsigned type
4649
using SerializedPtrType = int64_t;
4750

@@ -83,14 +86,14 @@ class CACHELIB_PACKED_ATTR CompressedPtr {
8386
private:
8487
// null pointer representation. This is almost never guaranteed to be a
8588
// valid pointer that we can compress to.
86-
static constexpr PtrType kNull = 0xffffffff;
89+
static constexpr PtrType kNull = 0x00000000ffffffff;
8790

8891
// default construct to null.
8992
PtrType ptr_{kNull};
9093

9194
// create a compressed pointer for a valid memory allocation.
92-
CompressedPtr(uint32_t slabIdx, uint32_t allocIdx)
93-
: ptr_(compress(slabIdx, allocIdx)) {}
95+
CompressedPtr(uint32_t slabIdx, uint32_t allocIdx, TierId tid = 0)
96+
: ptr_(compress(slabIdx, allocIdx, tid)) {}
9497

9598
constexpr explicit CompressedPtr(PtrType ptr) noexcept : ptr_{ptr} {}
9699

@@ -100,40 +103,60 @@ class CACHELIB_PACKED_ATTR CompressedPtr {
100103
static constexpr unsigned int kNumAllocIdxBits =
101104
Slab::kNumSlabBits - Slab::kMinAllocPower;
102105

106+
// Use topmost 32 bits for TierId
107+
// XXX: optimize
108+
static constexpr unsigned int kNumTierIdxOffset = 32;
109+
103110
static constexpr PtrType kAllocIdxMask = ((PtrType)1 << kNumAllocIdxBits) - 1;
104111

112+
// kNumTierIdxBits most significant bits
113+
static constexpr PtrType kTierIdxMask = (((PtrType)1 << kNumTierIdxOffset) - 1) << (NumBits<PtrType>::value - kNumTierIdxOffset);
114+
105115
// Number of bits for the slab index. This will be the top 16 bits of the
106116
// compressed ptr.
107117
static constexpr unsigned int kNumSlabIdxBits =
108-
NumBits<PtrType>::value - kNumAllocIdxBits;
118+
NumBits<PtrType>::value - kNumTierIdxOffset - kNumAllocIdxBits;
109119

110-
// Compress the given slabIdx and allocIdx into a 32-bit compressed
120+
// Compress the given slabIdx and allocIdx into a 64-bit compressed
111121
// pointer.
112-
static PtrType compress(uint32_t slabIdx, uint32_t allocIdx) noexcept {
122+
static PtrType compress(uint32_t slabIdx, uint32_t allocIdx, TierId tid) noexcept {
113123
XDCHECK_LE(allocIdx, kAllocIdxMask);
114124
XDCHECK_LT(slabIdx, (1u << kNumSlabIdxBits) - 1);
115-
return (slabIdx << kNumAllocIdxBits) + allocIdx;
125+
return (static_cast<uint64_t>(tid) << kNumTierIdxOffset) + (slabIdx << kNumAllocIdxBits) + allocIdx;
116126
}
117127

118128
// Get the slab index of the compressed ptr
119129
uint32_t getSlabIdx() const noexcept {
120130
XDCHECK(!isNull());
121-
return static_cast<uint32_t>(ptr_ >> kNumAllocIdxBits);
131+
auto noTierIdPtr = ptr_ & ~kTierIdxMask;
132+
return static_cast<uint32_t>(noTierIdPtr >> kNumAllocIdxBits);
122133
}
123134

124135
// Get the allocation index of the compressed ptr
125136
uint32_t getAllocIdx() const noexcept {
126137
XDCHECK(!isNull());
127-
return static_cast<uint32_t>(ptr_ & kAllocIdxMask);
138+
auto noTierIdPtr = ptr_ & ~kTierIdxMask;
139+
return static_cast<uint32_t>(noTierIdPtr & kAllocIdxMask);
140+
}
141+
142+
uint32_t getTierId() const noexcept {
143+
XDCHECK(!isNull());
144+
return static_cast<uint32_t>(ptr_ >> kNumTierIdxOffset);
145+
}
146+
147+
void setTierId(TierId tid) noexcept {
148+
ptr_ += static_cast<uint64_t>(tid) << kNumTierIdxOffset;
128149
}
129150

130151
friend SlabAllocator;
152+
template <typename CPtrType, typename AllocatorContainer>
153+
friend class PtrCompressor;
131154
};
132155

133156
template <typename PtrType, typename AllocatorT>
134-
class PtrCompressor {
157+
class SingleTierPtrCompressor {
135158
public:
136-
explicit PtrCompressor(const AllocatorT& allocator) noexcept
159+
explicit SingleTierPtrCompressor(const AllocatorT& allocator) noexcept
137160
: allocator_(allocator) {}
138161

139162
const CompressedPtr compress(const PtrType* uncompressed) const {
@@ -144,17 +167,61 @@ class PtrCompressor {
144167
return static_cast<PtrType*>(allocator_.unCompress(compressed));
145168
}
146169

147-
bool operator==(const PtrCompressor& rhs) const noexcept {
170+
bool operator==(const SingleTierPtrCompressor& rhs) const noexcept {
148171
return &allocator_ == &rhs.allocator_;
149172
}
150173

151-
bool operator!=(const PtrCompressor& rhs) const noexcept {
174+
bool operator!=(const SingleTierPtrCompressor& rhs) const noexcept {
152175
return !(*this == rhs);
153176
}
154177

155178
private:
156179
// memory allocator that does the pointer compression.
157180
const AllocatorT& allocator_;
158181
};
182+
183+
template <typename PtrType, typename AllocatorContainer>
184+
class PtrCompressor {
185+
public:
186+
explicit PtrCompressor(const AllocatorContainer& allocators) noexcept
187+
: allocators_(allocators) {}
188+
189+
const CompressedPtr compress(const PtrType* uncompressed) const {
190+
if (uncompressed == nullptr)
191+
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+
}
198+
199+
auto cptr = allocators_[tid]->compress(uncompressed);
200+
cptr.setTierId(tid);
201+
202+
return cptr;
203+
}
204+
205+
PtrType* unCompress(const CompressedPtr compressed) const {
206+
if (compressed.isNull()) {
207+
return nullptr;
208+
}
209+
210+
auto &allocator = *allocators_[compressed.getTierId()];
211+
return static_cast<PtrType*>(allocator.unCompress(compressed));
212+
}
213+
214+
bool operator==(const PtrCompressor& rhs) const noexcept {
215+
return &allocators_ == &rhs.allocators_;
216+
}
217+
218+
bool operator!=(const PtrCompressor& rhs) const noexcept {
219+
return !(*this == rhs);
220+
}
221+
222+
private:
223+
// memory allocator that does the pointer compression.
224+
const AllocatorContainer& allocators_;
225+
};
159226
} // namespace cachelib
160227
} // namespace facebook

cachelib/allocator/memory/MemoryAllocator.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,12 +513,13 @@ class MemoryAllocator {
513513
using CompressedPtr = facebook::cachelib::CompressedPtr;
514514
template <typename PtrType>
515515
using PtrCompressor =
516-
facebook::cachelib::PtrCompressor<PtrType, SlabAllocator>;
516+
facebook::cachelib::PtrCompressor<PtrType,
517+
std::vector<std::unique_ptr<MemoryAllocator>>>;
517518

518519
template <typename PtrType>
519-
PtrCompressor<PtrType> createPtrCompressor() {
520-
return slabAllocator_.createPtrCompressor<PtrType>();
521-
}
520+
using SingleTierPtrCompressor =
521+
facebook::cachelib::PtrCompressor<PtrType,
522+
SlabAllocator>;
522523

523524
// compress a given pointer to a valid allocation made out of this allocator
524525
// through an allocate() or nullptr. Calling this otherwise with invalid

cachelib/allocator/memory/SlabAllocator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,8 @@ serialization::SlabAllocatorObject SlabAllocator::saveState() {
519519
// for benchmarking purposes.
520520
const unsigned int kMarkerBits = 6;
521521
CompressedPtr SlabAllocator::compressAlt(const void* ptr) const {
522+
// XXX: do we need to set tierId here?
523+
522524
if (ptr == nullptr) {
523525
return CompressedPtr{};
524526
}
@@ -530,6 +532,8 @@ CompressedPtr SlabAllocator::compressAlt(const void* ptr) const {
530532
}
531533

532534
void* SlabAllocator::unCompressAlt(const CompressedPtr cPtr) const {
535+
// XXX: do we need to set tierId here?
536+
533537
if (cPtr.isNull()) {
534538
return nullptr;
535539
}

cachelib/allocator/memory/SlabAllocator.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,8 @@ class SlabAllocator {
308308
}
309309

310310
template <typename PtrType>
311-
PtrCompressor<PtrType, SlabAllocator> createPtrCompressor() const {
312-
return PtrCompressor<PtrType, SlabAllocator>(*this);
311+
SingleTierPtrCompressor<PtrType, SlabAllocator> createSingleTierPtrCompressor() const {
312+
return SingleTierPtrCompressor<PtrType, SlabAllocator>(*this);
313313
}
314314

315315
// returns starting address of memory we own.

cachelib/allocator/tests/AllocatorResizeTest.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,7 @@ class AllocatorResizeTest : public AllocatorTest<AllocatorT> {
10981098
size_t allocBytes = 0;
10991099
for (size_t k = 0; k < expectedIters * Slab::kSize / sz; k++) {
11001100
const auto key = this->getRandomNewKey(alloc, keyLen);
1101-
auto handle = util::allocateAccessible(alloc, poolId, key, sz - 45);
1101+
auto handle = util::allocateAccessible(alloc, poolId, key, sz - 45 - 9 /* TODO: compressed ptr size */);
11021102
if (!handle.get()) {
11031103
break;
11041104
}
@@ -1110,7 +1110,7 @@ class AllocatorResizeTest : public AllocatorTest<AllocatorT> {
11101110
for (size_t k = 0; k < expectedIters * Slab::kSize / sz; k++) {
11111111
const auto key = this->getRandomNewKey(alloc, keyLen);
11121112
size_t allocBytes = 0;
1113-
auto handle = util::allocateAccessible(alloc, poolId, key, sz - 45);
1113+
auto handle = util::allocateAccessible(alloc, poolId, key, sz - 45 - 9 /* TODO: compressed ptr size */);
11141114
allocBytes += handle->getSize();
11151115
}
11161116
}

0 commit comments

Comments
 (0)