From a82c56f9f7eb19fa6ac8a09cb7845cf681006f47 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Tue, 20 Oct 2020 00:24:53 +0100 Subject: [PATCH] LibJS: Speed up IndexedPropertyIterator by computing non-empty indices This provides a huge speed-up for objects with large numbers as property keys in some situation. Previously we would simply iterate from 0- and check if there's a non-empty value at each index - now we're being smarter and compute a list of non-empty indices upfront, by checking each value in the packed elements vector and appending the sparse elements hashmap keys (for GenericIndexedPropertyStorage). Consider this example, an object with a single own property, which is a number increasing by a factor of 10 each iteration: for (let i = 0; i < 10; ++i) { const o = {[10 ** i]: "foo"}; const start = Date.now(); Object.getOwnPropertyNames(o); // <-- IndexedPropertyIterator const end = Date.now(); console.log(`${10 ** i} -> ${(end - start) / 1000}s`); } Before this change: 1 -> 0.0000s 10 -> 0.0000s 100 -> 0.0000s 1000 -> 0.0000s 10000 -> 0.0005s 100000 -> 0.0039s 1000000 -> 0.0295s 10000000 -> 0.2489s 100000000 -> 2.4758s 1000000000 -> 25.5669s After this change: 1 -> 0.0000s 10 -> 0.0000s 100 -> 0.0000s 1000 -> 0.0000s 10000 -> 0.0000s 100000 -> 0.0000s 1000000 -> 0.0000s 10000000 -> 0.0000s 100000000 -> 0.0000s 1000000000 -> 0.0000s Fixes #3805. --- Libraries/LibJS/Runtime/IndexedProperties.cpp | 56 +++++++++++++++---- Libraries/LibJS/Runtime/IndexedProperties.h | 4 ++ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Libraries/LibJS/Runtime/IndexedProperties.cpp b/Libraries/LibJS/Runtime/IndexedProperties.cpp index 12538bf075..83a79b9be8 100644 --- a/Libraries/LibJS/Runtime/IndexedProperties.cpp +++ b/Libraries/LibJS/Runtime/IndexedProperties.cpp @@ -24,6 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include @@ -229,22 +230,16 @@ IndexedPropertyIterator::IndexedPropertyIterator(const IndexedProperties& indexe , m_index(staring_index) , m_skip_empty(skip_empty) { - while (m_skip_empty && m_index < m_indexed_properties.array_like_size()) { - if (m_indexed_properties.has_index(m_index)) - break; - m_index++; - } + if (m_skip_empty) + skip_empty_indices(); } IndexedPropertyIterator& IndexedPropertyIterator::operator++() { m_index++; - while (m_skip_empty && m_index < m_indexed_properties.array_like_size()) { - if (m_indexed_properties.has_index(m_index)) - break; - m_index++; - }; + if (m_skip_empty) + skip_empty_indices(); return *this; } @@ -266,6 +261,21 @@ ValueAndAttributes IndexedPropertyIterator::value_and_attributes(Object* this_ob return {}; } +void IndexedPropertyIterator::skip_empty_indices() +{ + auto indices = m_indexed_properties.indices(); + if (indices.is_empty()) { + m_index = m_indexed_properties.array_like_size(); + return; + } + for (auto i : indices) { + if (i < m_index) + continue; + m_index = i; + break; + } +} + Optional IndexedProperties::get(Object* this_object, u32 index, bool evaluate_accessors) const { auto result = m_storage->get(index); @@ -354,6 +364,32 @@ void IndexedProperties::set_array_like_size(size_t new_size) m_storage->set_array_like_size(new_size); } +Vector IndexedProperties::indices() const +{ + Vector indices; + if (m_storage->is_simple_storage()) { + const auto& storage = static_cast(*m_storage); + const auto& elements = storage.elements(); + indices.ensure_capacity(storage.array_like_size()); + for (size_t i = 0; i < elements.size(); ++i) { + if (!elements.at(i).is_empty()) + indices.unchecked_append(i); + } + } else { + const auto& storage = static_cast(*m_storage); + const auto packed_elements = storage.packed_elements(); + indices.ensure_capacity(storage.array_like_size()); + for (size_t i = 0; i < packed_elements.size(); ++i) { + if (!packed_elements.at(i).value.is_empty()) + indices.unchecked_append(i); + } + auto sparse_elements_keys = storage.sparse_elements().keys(); + quick_sort(sparse_elements_keys); + indices.append(move(sparse_elements_keys)); + } + return indices; +} + Vector IndexedProperties::values_unordered() const { if (m_storage->is_simple_storage()) { diff --git a/Libraries/LibJS/Runtime/IndexedProperties.h b/Libraries/LibJS/Runtime/IndexedProperties.h index a6d89b77c3..c0ce391c0e 100644 --- a/Libraries/LibJS/Runtime/IndexedProperties.h +++ b/Libraries/LibJS/Runtime/IndexedProperties.h @@ -130,6 +130,8 @@ public: ValueAndAttributes value_and_attributes(Object* this_object, bool evaluate_accessors = true); private: + void skip_empty_indices(); + const IndexedProperties& m_indexed_properties; u32 m_index; bool m_skip_empty; @@ -164,6 +166,8 @@ public: size_t array_like_size() const { return m_storage->array_like_size(); } void set_array_like_size(size_t); + Vector indices() const; + Vector values_unordered() const; private: