Commit 1f53e381 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[runtime] Shrink StringTable if it is very empty

We currently never shrink the StringTable which causes excessive memory usage
on certain websites. This CL tries to mitigate this by shrinking the
StringTable if it is very empty (nof_elements * 16 < capacity) hopefully
avoiding costly reallocations.

Bug: chromium:818642, v8:5443
Change-Id: I4e6a95b3a6992b499fa6dd59ae159c51f089965a
Reviewed-on: https://chromium-review.googlesource.com/970465Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarHannes Payer <hpayer@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52070}
parent 377803f8
......@@ -1758,7 +1758,7 @@ class Heap {
void* data;
};
static const int kInitialStringTableSize = 2048;
static const int kInitialStringTableSize = StringTable::kMinCapacity;
static const int kInitialEvalCacheSize = 64;
static const int kInitialNumberStringCacheSize = 256;
......
......@@ -16528,19 +16528,21 @@ bool HashTable<Derived, Shape>::HasSufficientCapacityToAdd(
}
template <typename Derived, typename Shape>
Handle<Derived> HashTable<Derived, Shape>::Shrink(Handle<Derived> table) {
Handle<Derived> HashTable<Derived, Shape>::Shrink(Handle<Derived> table,
int additionalCapacity) {
int capacity = table->Capacity();
int nof = table->NumberOfElements();
// Shrink to fit the number of elements if only a quarter of the
// capacity is filled with elements.
if (nof > (capacity >> 2)) return table;
// Allocate a new dictionary with room for at least the current
// number of elements. The allocation method will make sure that
// there is extra room in the dictionary for additions. Don't go
// lower than room for 16 elements.
int at_least_room_for = nof;
if (at_least_room_for < 16) return table;
// Allocate a new dictionary with room for at least the current number of
// elements + {additionalCapacity}. The allocation method will make sure that
// there is extra room in the dictionary for additions. Don't go lower than
// room for {kMinShrinkCapacity} elements.
int at_least_room_for = nof + additionalCapacity;
DCHECK_LE(at_least_room_for, capacity);
if (at_least_room_for < Derived::kMinShrinkCapacity) return table;
Isolate* isolate = table->GetIsolate();
const int kMinCapacityForPretenure = 256;
......@@ -16641,8 +16643,9 @@ HashTable<ObjectHashSet, ObjectHashSetShape>::New(Isolate*, int n,
PretenureFlag,
MinimumCapacity);
template Handle<NameDictionary> HashTable<
NameDictionary, NameDictionaryShape>::Shrink(Handle<NameDictionary>);
template Handle<NameDictionary>
HashTable<NameDictionary, NameDictionaryShape>::Shrink(Handle<NameDictionary>,
int additionalCapacity);
template Handle<NameDictionary>
BaseNameDictionary<NameDictionary, NameDictionaryShape>::Add(
......@@ -17048,6 +17051,7 @@ Handle<String> StringTable::LookupKey(Isolate* isolate, StringTableKey* key) {
return handle(String::cast(table->KeyAt(entry)), isolate);
}
table = StringTable::CautiousShrink(table);
// Adding new string. Grow table if needed.
table = StringTable::EnsureCapacity(table, 1);
......@@ -17067,6 +17071,18 @@ Handle<String> StringTable::LookupKey(Isolate* isolate, StringTableKey* key) {
return Handle<String>::cast(string);
}
Handle<StringTable> StringTable::CautiousShrink(Handle<StringTable> table) {
// Only shrink if the table is very empty to avoid performance penalty.
int capacity = table->Capacity();
int nof = table->NumberOfElements();
if (capacity <= StringTable::kMinCapacity) return table;
if (nof > (capacity / kMaxEmptyFactor)) return table;
// Make sure that after shrinking the table is half empty (aka. has capacity
// for another {nof} elements).
DCHECK_LE(nof * 2, capacity);
return Shrink(table, nof);
}
namespace {
class StringTableNoAllocateKey : public StringTableKey {
......
......@@ -181,6 +181,9 @@ class HashTable : public HashTableBase {
static const int kMaxCapacity =
(FixedArray::kMaxLength - kElementsStartIndex) / kEntrySize;
// Don't shrink a HashTable below this capacity.
static const int kMinShrinkCapacity = 16;
// Maximum length to create a regular HashTable (aka. non large object).
static const int kMaxRegularCapacity = 16384;
......@@ -208,7 +211,8 @@ class HashTable : public HashTableBase {
uint32_t FindInsertionEntry(uint32_t hash);
// Attempt to shrink hash table after removal of key.
MUST_USE_RESULT static Handle<Derived> Shrink(Handle<Derived> table);
MUST_USE_RESULT static Handle<Derived> Shrink(Handle<Derived> table,
int additionalCapacity = 0);
private:
// Ensure that kMaxRegularCapacity yields a non-large object dictionary.
......
......@@ -64,6 +64,10 @@ class StringTable : public HashTable<StringTable, StringTableShape> {
static String* ForwardStringIfExists(Isolate* isolate, StringTableKey* key,
String* string);
// Shink the StringTable if it's very empty (kMaxEmptyFactor) to avoid the
// performance overhead of re-allocating the StringTable over and over again.
static Handle<StringTable> CautiousShrink(Handle<StringTable> table);
// Looks up a string that is equal to the given string and returns
// string handle if it is found, or an empty handle otherwise.
MUST_USE_RESULT static MaybeHandle<String> LookupTwoCharsStringIfExists(
......@@ -74,6 +78,10 @@ class StringTable : public HashTable<StringTable, StringTableShape> {
DECL_CAST(StringTable)
static const int kMaxEmptyFactor = 16;
static const int kMinCapacity = 2048;
static const int kMinShrinkCapacity = kMinCapacity;
private:
template <bool seq_one_byte>
friend class JsonParser;
......
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