asan: faster thead-local cache for memory allocator

llvm-svn: 172521
This commit is contained in:
Dmitry Vyukov 2013-01-15 09:02:20 +00:00
parent 1f48c1a9a1
commit 45a698eb58
1 changed files with 57 additions and 38 deletions

View File

@ -65,7 +65,7 @@ namespace __sanitizer {
// c32 => s: 512 diff: +32 06% l 9 cached: 64 32768; id 32 // c32 => s: 512 diff: +32 06% l 9 cached: 64 32768; id 32
template <uptr kMaxSizeLog, uptr kMaxNumCached, uptr kMaxBytesCachedLog, template <uptr kMaxSizeLog, uptr kMaxNumCachedT, uptr kMaxBytesCachedLog,
uptr kMinBatchClassT> uptr kMinBatchClassT>
class SizeClassMap { class SizeClassMap {
static const uptr kMinSizeLog = 3; static const uptr kMinSizeLog = 3;
@ -77,6 +77,7 @@ class SizeClassMap {
static const uptr M = (1 << S) - 1; static const uptr M = (1 << S) - 1;
public: public:
static const uptr kMaxNumCached = kMaxNumCachedT;
struct TransferBatch { struct TransferBatch {
TransferBatch *next; TransferBatch *next;
uptr count; uptr count;
@ -598,6 +599,7 @@ class SizeClassAllocator32 {
uptr size = SizeClassMap::Size(class_id); uptr size = SizeClassMap::Size(class_id);
uptr reg = AllocateRegion(class_id); uptr reg = AllocateRegion(class_id);
uptr n_chunks = kRegionSize / (size + kMetadataSize); uptr n_chunks = kRegionSize / (size + kMetadataSize);
uptr max_count = SizeClassMap::MaxCached(class_id);
Batch *b = 0; Batch *b = 0;
for (uptr i = reg; i < reg + n_chunks * size; i += size) { for (uptr i = reg; i < reg + n_chunks * size; i += size) {
if (b == 0) { if (b == 0) {
@ -608,7 +610,7 @@ class SizeClassAllocator32 {
b->count = 0; b->count = 0;
} }
b->batch[b->count++] = (void*)i; b->batch[b->count++] = (void*)i;
if (b->count == SizeClassMap::MaxCached(class_id)) { if (b->count == max_count) {
sci->free_list.push_back(b); sci->free_list.push_back(b);
b = 0; b = 0;
} }
@ -631,6 +633,7 @@ template<class SizeClassAllocator>
struct SizeClassAllocatorLocalCache { struct SizeClassAllocatorLocalCache {
typedef SizeClassAllocator Allocator; typedef SizeClassAllocator Allocator;
static const uptr kNumClasses = SizeClassAllocator::kNumClasses; static const uptr kNumClasses = SizeClassAllocator::kNumClasses;
// Don't need to call Init if the object is a global (i.e. zero-initialized). // Don't need to call Init if the object is a global (i.e. zero-initialized).
void Init() { void Init() {
internal_memset(this, 0, sizeof(*this)); internal_memset(this, 0, sizeof(*this));
@ -640,18 +643,10 @@ struct SizeClassAllocatorLocalCache {
CHECK_NE(class_id, 0UL); CHECK_NE(class_id, 0UL);
CHECK_LT(class_id, kNumClasses); CHECK_LT(class_id, kNumClasses);
PerClass *c = &per_class_[class_id]; PerClass *c = &per_class_[class_id];
if (c->cur == 0) { if (UNLIKELY(c->count == 0))
DCHECK_EQ(c->old, 0); Refill(allocator, class_id);
c->cur = allocator->AllocateBatch(this, class_id); void *res = c->batch[--c->count];
} PREFETCH(c->batch[c->count - 1]);
DCHECK_GT(c->cur->count, 0);
void *res = c->cur->batch[--c->cur->count];
if (c->cur->count == 0) {
if (class_id < SizeClassMap::kMinBatchClass)
Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), c->cur);
c->cur = c->old;
c->old = 0;
}
return res; return res;
} }
@ -659,31 +654,16 @@ struct SizeClassAllocatorLocalCache {
CHECK_NE(class_id, 0UL); CHECK_NE(class_id, 0UL);
CHECK_LT(class_id, kNumClasses); CHECK_LT(class_id, kNumClasses);
PerClass *c = &per_class_[class_id]; PerClass *c = &per_class_[class_id];
if (c->cur == 0 || c->cur->count == SizeClassMap::MaxCached(class_id)) { if (UNLIKELY(c->count == c->max_count))
if (c->old) Drain(allocator, class_id);
allocator->DeallocateBatch(class_id, c->old); c->batch[c->count++] = p;
c->old = c->cur;
if (class_id < SizeClassMap::kMinBatchClass)
c->cur = (Batch*)Allocate(allocator,
SizeClassMap::ClassID(sizeof(Batch)));
else
c->cur = (Batch*)p;
c->cur->count = 0;
}
c->cur->batch[c->cur->count++] = p;
} }
void Drain(SizeClassAllocator *allocator) { void Drain(SizeClassAllocator *allocator) {
for (uptr i = 0; i < kNumClasses; i++) { for (uptr class_id = 0; class_id < kNumClasses; class_id++) {
PerClass *c = &per_class_[i]; PerClass *c = &per_class_[class_id];
if (c->cur) { while (c->count > 0)
allocator->DeallocateBatch(i, c->cur); Drain(allocator, class_id);
c->cur = 0;
}
if (c->old) {
allocator->DeallocateBatch(i, c->old);
c->old = 0;
}
} }
} }
@ -691,10 +671,49 @@ struct SizeClassAllocatorLocalCache {
typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap; typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap;
typedef typename SizeClassMap::TransferBatch Batch; typedef typename SizeClassMap::TransferBatch Batch;
struct PerClass { struct PerClass {
Batch *cur; uptr count;
Batch *old; uptr max_count;
void *batch[2 * SizeClassMap::kMaxNumCached];
}; };
PerClass per_class_[kNumClasses]; PerClass per_class_[kNumClasses];
void InitCache() {
if (per_class_[0].max_count)
return;
for (uptr i = 0; i < kNumClasses; i++) {
PerClass *c = &per_class_[i];
c->max_count = 2 * SizeClassMap::MaxCached(i);
}
}
void NOINLINE Refill(SizeClassAllocator *allocator, uptr class_id) {
InitCache();
PerClass *c = &per_class_[class_id];
Batch *b = allocator->AllocateBatch(this, class_id);
for (uptr i = 0; i < b->count; i++)
c->batch[i] = b->batch[i];
c->count = b->count;
if (class_id < SizeClassMap::kMinBatchClass)
Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b);
}
void NOINLINE Drain(SizeClassAllocator *allocator, uptr class_id) {
InitCache();
PerClass *c = &per_class_[class_id];
Batch *b;
if (class_id < SizeClassMap::kMinBatchClass)
b = (Batch*)Allocate(allocator, SizeClassMap::ClassID(sizeof(Batch)));
else
b = (Batch*)c->batch[0];
uptr cnt = Min(c->max_count / 2, c->count);
for (uptr i = 0; i < cnt; i++) {
b->batch[i] = c->batch[i];
c->batch[i] = c->batch[i + c->max_count / 2];
}
b->count = cnt;
c->count -= cnt;
allocator->DeallocateBatch(class_id, b);
}
}; };
// This class can (de)allocate only large chunks of memory using mmap/unmap. // This class can (de)allocate only large chunks of memory using mmap/unmap.