From 619cd613d022c5ea8e20acca5a6fa6b3913a92c4 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 22 Oct 2020 17:43:48 +0200 Subject: [PATCH] LibJS: Give VM a cache of single-ASCII-character PrimitiveString A large number of JS strings are a single ASCII character. This patch adds a 128-entry cache for those strings to the VM. The cost of the cache is 1536 byte of GC heap (all in same block) + 2304 bytes malloc. This avoids a lot of GC heap allocations, and packing all of these in the same heap block is nice for fragmentation as well. --- Libraries/LibJS/Runtime/PrimitiveString.cpp | 4 ++++ Libraries/LibJS/Runtime/VM.cpp | 7 +++++++ Libraries/LibJS/Runtime/VM.h | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/Libraries/LibJS/Runtime/PrimitiveString.cpp b/Libraries/LibJS/Runtime/PrimitiveString.cpp index 2aeb74e914..b62377925d 100644 --- a/Libraries/LibJS/Runtime/PrimitiveString.cpp +++ b/Libraries/LibJS/Runtime/PrimitiveString.cpp @@ -42,6 +42,10 @@ PrimitiveString* js_string(Heap& heap, String string) { if (string.is_empty()) return &heap.vm().empty_string(); + + if (string.length() == 1 && (u8)string.characters()[0] < 0x80) + return &heap.vm().single_ascii_character_string(string.characters()[0]); + return heap.allocate_without_global_object(move(string)); } diff --git a/Libraries/LibJS/Runtime/VM.cpp b/Libraries/LibJS/Runtime/VM.cpp index d53e4dd79f..63b31aa37a 100644 --- a/Libraries/LibJS/Runtime/VM.cpp +++ b/Libraries/LibJS/Runtime/VM.cpp @@ -47,6 +47,10 @@ VM::VM() : m_heap(*this) { m_empty_string = m_heap.allocate_without_global_object(String::empty()); + for (size_t i = 0; i < 128; ++i) { + m_single_ascii_character_strings[i] = m_heap.allocate_without_global_object(String::format("%c", i)); + } + #define __JS_ENUMERATE(SymbolName, snake_name) \ m_well_known_symbol_##snake_name = js_symbol(*this, "Symbol." #SymbolName, false); JS_ENUMERATE_WELL_KNOWN_SYMBOLS @@ -96,6 +100,9 @@ VM::InterpreterExecutionScope::~InterpreterExecutionScope() void VM::gather_roots(HashTable& roots) { roots.set(m_empty_string); + for (auto* string : m_single_ascii_character_strings) + roots.set(string); + if (m_exception) roots.set(m_exception); diff --git a/Libraries/LibJS/Runtime/VM.h b/Libraries/LibJS/Runtime/VM.h index 3334276640..f4adc144f9 100644 --- a/Libraries/LibJS/Runtime/VM.h +++ b/Libraries/LibJS/Runtime/VM.h @@ -108,6 +108,11 @@ public: Symbol* get_global_symbol(const String& description); PrimitiveString& empty_string() { return *m_empty_string; } + PrimitiveString& single_ascii_character_string(u8 character) + { + ASSERT(character < 0x80); + return *m_single_ascii_character_strings[character]; + } CallFrame& push_call_frame(bool strict_mode = false) { @@ -247,6 +252,7 @@ private: HashMap m_global_symbol_map; PrimitiveString* m_empty_string { nullptr }; + PrimitiveString* m_single_ascii_character_strings[128] {}; #define __JS_ENUMERATE(SymbolName, snake_name) \ Symbol* m_well_known_symbol_##snake_name { nullptr };