Commit bcd00b2a authored by Darius Mercadier's avatar Darius Mercadier Committed by Commit Bot

[heap] Add FreeListMany stragety (--gc-freelist-strategy=2)

This CL adds a new FreeList strategy, that can be turned on by using
flag `--gc-freelist-strategy=2`. It uses a lot (about 50)
FreeListCategories instead of the 6 ones used in FreeListLegacy.
Allocation is done using a best-fit strategy. However, FreeListMany
could be subclassed in order to change the allocation strategy while
still using the same freelists.

Using this strategy is expected to reduce memory usage but to also
reduce allocation performances.

Bug: v8:9329
Change-Id: I201be863270a3287701fefdd9e14ba7849a8a551
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1698392
Commit-Queue: Darius Mercadier <dmercadier@google.com>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62731}
parent 23d48550
......@@ -790,7 +790,7 @@ DEFINE_BOOL(trace_evacuation_candidates, false,
"Show statistics about the pages evacuation by the compaction")
DEFINE_INT(gc_freelist_strategy, 0,
"Freelist strategy to use: "
"1=FreeListFastAlloc. Anything else=FreeListLegacy")
"1=FreeListFastAlloc. 2=FreeListMany. Anything else=FreeListLegacy")
DEFINE_INT(trace_allocation_stack_interval, -1,
"print stack trace after <n> free-list allocations")
......
......@@ -2986,6 +2986,8 @@ void FreeListCategory::Relink() {
FreeList* FreeList::CreateFreeList() {
if (FLAG_gc_freelist_strategy == 1) {
return new FreeListFastAlloc();
} else if (FLAG_gc_freelist_strategy == 2) {
return new FreeListMany();
} else {
return new FreeListLegacy();
}
......@@ -3175,6 +3177,103 @@ FreeSpace FreeListFastAlloc::Allocate(size_t size_in_bytes, size_t* node_size) {
return node;
}
// Cf. the declaration of |categories_max| in |spaces.h| to see how this is
// computed.
const size_t FreeListMany::categories_max[kNumberOfCategories] = {
24, 32, 40, 48, 56, 64, 72,
80, 88, 96, 104, 112, 120, 128,
136, 144, 152, 160, 168, 176, 184,
192, 200, 208, 216, 224, 232, 240,
248, 256, 384, 512, 768, 1024, 1536,
2048, 3072, 4080, 4088, 4096, 6144, 8192,
12288, 16384, 24576, 32768, 49152, 65536, Page::kPageSize};
FreeListMany::FreeListMany() {
wasted_bytes_ = 0;
number_of_categories_ = kNumberOfCategories;
last_category_ = number_of_categories_ - 1;
categories_ = new FreeListCategory*[number_of_categories_]();
Reset();
}
size_t FreeListMany::GuaranteedAllocatable(size_t maximum_freed) {
if (maximum_freed <= categories_max[0]) {
return 0;
}
for (int cat = kFirstCategory + 1; cat < last_category_; cat++) {
if (maximum_freed <= categories_max[cat]) {
return categories_max[cat - 1];
}
}
return maximum_freed;
}
Page* FreeListMany::GetPageForSize(size_t size_in_bytes) {
const int minimum_category =
static_cast<int>(SelectFreeListCategoryType(size_in_bytes));
Page* page = GetPageForCategoryType(last_category_);
for (int cat = last_category_ - 1; !page && cat >= minimum_category; cat--) {
page = GetPageForCategoryType(cat);
}
return page;
}
FreeListMany::~FreeListMany() { delete[] categories_; }
size_t FreeListMany::Free(Address start, size_t size_in_bytes, FreeMode mode) {
Page* page = Page::FromAddress(start);
page->DecreaseAllocatedBytes(size_in_bytes);
// Blocks have to be a minimum size to hold free list items.
if (size_in_bytes < kMinBlockSize) {
page->add_wasted_memory(size_in_bytes);
wasted_bytes_ += size_in_bytes;
return size_in_bytes;
}
// Insert other blocks at the head of a free list of the appropriate
// magnitude.
FreeListCategoryType type = SelectFreeListCategoryType(size_in_bytes);
page->free_list_category(type)->Free(start, size_in_bytes, mode);
DCHECK_EQ(page->AvailableInFreeList(),
page->AvailableInFreeListFromAllocatedBytes());
return 0;
}
FreeSpace FreeListMany::TryFindNodeIn(FreeListCategoryType type,
size_t minimum_size, size_t* node_size) {
FreeListCategory* category = categories_[type];
if (category == nullptr) return FreeSpace();
FreeSpace node = category->PickNodeFromList(minimum_size, node_size);
if (!node.is_null()) {
DCHECK(IsVeryLong() || Available() == SumFreeLists());
}
if (category->is_empty()) {
RemoveCategory(category);
}
return node;
}
FreeSpace FreeListMany::Allocate(size_t size_in_bytes, size_t* node_size) {
DCHECK_GE(kMaxBlockSize, size_in_bytes);
FreeSpace node;
// First try the allocation fast path: try to allocate the minimum element
// size of a free list category. This operation is constant time.
FreeListCategoryType type = SelectFreeListCategoryType(size_in_bytes);
for (int i = type; i <= last_category_ && node.is_null(); i++) {
node = TryFindNodeIn(static_cast<FreeListCategoryType>(i), size_in_bytes,
node_size);
}
if (!node.is_null()) {
Page::FromHeapObject(node)->IncreaseAllocatedBytes(*node_size);
}
DCHECK(IsVeryLong() || Available() == SumFreeLists());
return node;
}
size_t FreeList::EvictFreeListItems(Page* page) {
size_t sum = 0;
page->ForAllFreeListCategories([this, &sum](FreeListCategory* category) {
......
......@@ -338,6 +338,10 @@ class FreeList {
return categories_[type];
}
Page* GetPageForCategoryType(FreeListCategoryType type) {
return top(type) ? top(type)->page() : nullptr;
}
int number_of_categories_;
FreeListCategoryType last_category_;
......@@ -1868,10 +1872,6 @@ class V8_EXPORT_PRIVATE FreeListLegacy : public FreeList {
return kHuge;
}
Page* GetPageForCategoryType(FreeListCategoryType type) {
return top(type) ? top(type)->page() : nullptr;
}
friend class FreeListCategory;
friend class heap::HeapTester;
};
......@@ -1951,6 +1951,71 @@ class V8_EXPORT_PRIVATE FreeListFastAlloc : public FreeList {
}
};
// Use 49 Freelists: on per size between 24 and 256, and then a few ones for
// larger sizes. See the variable |categories_max| for the size of each
// Freelist. Allocation is done using a best-fit strategy (considering only the
// first element of each category though).
// Performances are expected to be worst than FreeListLegacy, but memory
// consumption should be lower (since fragmentation should be lower).
class V8_EXPORT_PRIVATE FreeListMany : public FreeList {
public:
// This method returns how much memory can be allocated after freeing
// maximum_freed memory.
size_t GuaranteedAllocatable(size_t maximum_freed) override;
Page* GetPageForSize(size_t size_in_bytes) override;
FreeListMany();
~FreeListMany();
size_t Free(Address start, size_t size_in_bytes, FreeMode mode) override;
V8_WARN_UNUSED_RESULT FreeSpace Allocate(size_t size_in_bytes,
size_t* node_size) override;
private:
static const size_t kMinBlockSize = 3 * kTaggedSize;
// This is a conservative upper bound. The actual maximum block size takes
// padding and alignment of data and code pages into account.
static const size_t kMaxBlockSize = Page::kPageSize;
// Categories boundaries generated with:
// perl -E '
// @cat = map {$_*8} 3..32, 48, 64;
// while ($cat[-1] <= 32768) {
// push @cat, $cat[-1]+$cat[-3], $cat[-1]*2
// }
// push @cat, 4080, 4088;
// @cat = sort { $a <=> $b } @cat;
// push @cat, "Page::kPageSize";
// say join ", ", @cat;
// say "\n", scalar @cat'
// Note the special case for 4080 and 4088 bytes: experiments have shown that
// this category classes are more used than others of similar sizes
static const int kNumberOfCategories = 49;
static const size_t categories_max[kNumberOfCategories];
// Tries to retrieve a node from the first category in a given |type|.
// Returns nullptr if the category is empty or the top entry is smaller
// than minimum_size.
FreeSpace TryFindNodeIn(FreeListCategoryType type, size_t minimum_size,
size_t* node_size);
// Searches a given |type| for a node of at least |minimum_size|.
FreeSpace SearchForNodeInList(FreeListCategoryType type, size_t* node_size,
size_t minimum_size);
FreeListCategoryType SelectFreeListCategoryType(size_t size_in_bytes) {
for (int cat = kFirstCategory; cat < last_category_; cat++) {
if (size_in_bytes <= categories_max[cat]) {
return cat;
}
}
return last_category_;
}
};
// LocalAllocationBuffer represents a linear allocation area that is created
// from a given {AllocationResult} and can be used to allocate memory without
// synchronization.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment