1 //===-- local_cache.h -------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef SCUDO_LOCAL_CACHE_H_
10 #define SCUDO_LOCAL_CACHE_H_
12 #include "internal_defs.h"
18 template <class SizeClassAllocator> struct SizeClassAllocatorLocalCache {
19 typedef typename SizeClassAllocator::SizeClassMap SizeClassMap;
21 struct TransferBatch {
22 static const u32 MaxNumCached = SizeClassMap::MaxNumCachedHint;
23 void setFromArray(void **Array, u32 N) {
24 DCHECK_LE(N, MaxNumCached);
25 for (u32 I = 0; I < N; I++)
29 void clear() { Count = 0; }
31 DCHECK_LT(Count, MaxNumCached);
34 void copyToArray(void **Array) const {
35 for (u32 I = 0; I < Count; I++)
38 u32 getCount() const { return Count; }
39 void *get(u32 I) const {
43 static u32 getMaxCached(uptr Size) {
44 return Min(MaxNumCached, SizeClassMap::getMaxCachedHint(Size));
50 void *Batch[MaxNumCached];
53 void initLinkerInitialized(GlobalStats *S, SizeClassAllocator *A) {
54 Stats.initLinkerInitialized();
60 void init(GlobalStats *S, SizeClassAllocator *A) {
61 memset(this, 0, sizeof(*this));
62 initLinkerInitialized(S, A);
65 void destroy(GlobalStats *S) {
71 void *allocate(uptr ClassId) {
72 CHECK_LT(ClassId, NumClasses);
73 PerClass *C = &PerClassArray[ClassId];
75 if (UNLIKELY(!refill(C, ClassId)))
77 DCHECK_GT(C->Count, 0);
79 // We read ClassSize first before accessing Chunks because it's adjacent to
80 // Count, while Chunks might be further off (depending on Count). That keeps
81 // the memory accesses in close quarters.
82 const uptr ClassSize = C->ClassSize;
83 void *P = C->Chunks[--C->Count];
84 // The jury is still out as to whether any kind of PREFETCH here increases
85 // performance. It definitely decreases performance on Android though.
86 // if (!SCUDO_ANDROID) PREFETCH(P);
87 Stats.add(StatAllocated, ClassSize);
91 void deallocate(uptr ClassId, void *P) {
92 CHECK_LT(ClassId, NumClasses);
93 PerClass *C = &PerClassArray[ClassId];
94 // We still have to initialize the cache in the event that the first heap
95 // operation in a thread is a deallocation.
97 if (C->Count == C->MaxCount)
99 // See comment in allocate() about memory accesses.
100 const uptr ClassSize = C->ClassSize;
101 C->Chunks[C->Count++] = P;
102 Stats.sub(StatAllocated, ClassSize);
106 for (uptr I = 0; I < NumClasses; I++) {
107 PerClass *C = &PerClassArray[I];
113 TransferBatch *createBatch(uptr ClassId, void *B) {
114 if (ClassId != SizeClassMap::BatchClassId)
115 B = allocate(SizeClassMap::BatchClassId);
116 return reinterpret_cast<TransferBatch *>(B);
119 LocalStats &getStats() { return Stats; }
122 static const uptr NumClasses = SizeClassMap::NumClasses;
127 void *Chunks[2 * TransferBatch::MaxNumCached];
129 PerClass PerClassArray[NumClasses];
131 SizeClassAllocator *Allocator;
133 ALWAYS_INLINE void initCacheMaybe(PerClass *C) {
134 if (LIKELY(C->MaxCount))
137 DCHECK_NE(C->MaxCount, 0U);
140 NOINLINE void initCache() {
141 for (uptr I = 0; I < NumClasses; I++) {
142 PerClass *P = &PerClassArray[I];
143 const uptr Size = SizeClassAllocator::getSizeByClassId(I);
144 P->MaxCount = 2 * TransferBatch::getMaxCached(Size);
149 void destroyBatch(uptr ClassId, void *B) {
150 if (ClassId != SizeClassMap::BatchClassId)
151 deallocate(SizeClassMap::BatchClassId, B);
154 NOINLINE bool refill(PerClass *C, uptr ClassId) {
156 TransferBatch *B = Allocator->popBatch(this, ClassId);
159 DCHECK_GT(B->getCount(), 0);
160 B->copyToArray(C->Chunks);
161 C->Count = B->getCount();
162 destroyBatch(ClassId, B);
166 NOINLINE void drain(PerClass *C, uptr ClassId) {
167 const u32 Count = Min(C->MaxCount / 2, C->Count);
168 const uptr FirstIndexToDrain = C->Count - Count;
169 TransferBatch *B = createBatch(ClassId, C->Chunks[FirstIndexToDrain]);
172 SizeClassAllocator::getSizeByClassId(SizeClassMap::BatchClassId));
173 B->setFromArray(&C->Chunks[FirstIndexToDrain], Count);
175 Allocator->pushBatch(ClassId, B);
181 #endif // SCUDO_LOCAL_CACHE_H_