mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 20:17:42 +00:00
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-<max> 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.
This commit is contained in:
parent
0e900ca5df
commit
a82c56f9f7
2 changed files with 50 additions and 10 deletions
|
@ -24,6 +24,7 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/QuickSort.h>
|
||||||
#include <LibJS/Runtime/Accessor.h>
|
#include <LibJS/Runtime/Accessor.h>
|
||||||
#include <LibJS/Runtime/IndexedProperties.h>
|
#include <LibJS/Runtime/IndexedProperties.h>
|
||||||
|
|
||||||
|
@ -229,22 +230,16 @@ IndexedPropertyIterator::IndexedPropertyIterator(const IndexedProperties& indexe
|
||||||
, m_index(staring_index)
|
, m_index(staring_index)
|
||||||
, m_skip_empty(skip_empty)
|
, m_skip_empty(skip_empty)
|
||||||
{
|
{
|
||||||
while (m_skip_empty && m_index < m_indexed_properties.array_like_size()) {
|
if (m_skip_empty)
|
||||||
if (m_indexed_properties.has_index(m_index))
|
skip_empty_indices();
|
||||||
break;
|
|
||||||
m_index++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexedPropertyIterator& IndexedPropertyIterator::operator++()
|
IndexedPropertyIterator& IndexedPropertyIterator::operator++()
|
||||||
{
|
{
|
||||||
m_index++;
|
m_index++;
|
||||||
|
|
||||||
while (m_skip_empty && m_index < m_indexed_properties.array_like_size()) {
|
if (m_skip_empty)
|
||||||
if (m_indexed_properties.has_index(m_index))
|
skip_empty_indices();
|
||||||
break;
|
|
||||||
m_index++;
|
|
||||||
};
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -266,6 +261,21 @@ ValueAndAttributes IndexedPropertyIterator::value_and_attributes(Object* this_ob
|
||||||
return {};
|
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<ValueAndAttributes> IndexedProperties::get(Object* this_object, u32 index, bool evaluate_accessors) const
|
Optional<ValueAndAttributes> IndexedProperties::get(Object* this_object, u32 index, bool evaluate_accessors) const
|
||||||
{
|
{
|
||||||
auto result = m_storage->get(index);
|
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);
|
m_storage->set_array_like_size(new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<u32> IndexedProperties::indices() const
|
||||||
|
{
|
||||||
|
Vector<u32> indices;
|
||||||
|
if (m_storage->is_simple_storage()) {
|
||||||
|
const auto& storage = static_cast<const SimpleIndexedPropertyStorage&>(*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<const GenericIndexedPropertyStorage&>(*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<ValueAndAttributes> IndexedProperties::values_unordered() const
|
Vector<ValueAndAttributes> IndexedProperties::values_unordered() const
|
||||||
{
|
{
|
||||||
if (m_storage->is_simple_storage()) {
|
if (m_storage->is_simple_storage()) {
|
||||||
|
|
|
@ -130,6 +130,8 @@ public:
|
||||||
ValueAndAttributes value_and_attributes(Object* this_object, bool evaluate_accessors = true);
|
ValueAndAttributes value_and_attributes(Object* this_object, bool evaluate_accessors = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void skip_empty_indices();
|
||||||
|
|
||||||
const IndexedProperties& m_indexed_properties;
|
const IndexedProperties& m_indexed_properties;
|
||||||
u32 m_index;
|
u32 m_index;
|
||||||
bool m_skip_empty;
|
bool m_skip_empty;
|
||||||
|
@ -164,6 +166,8 @@ public:
|
||||||
size_t array_like_size() const { return m_storage->array_like_size(); }
|
size_t array_like_size() const { return m_storage->array_like_size(); }
|
||||||
void set_array_like_size(size_t);
|
void set_array_like_size(size_t);
|
||||||
|
|
||||||
|
Vector<u32> indices() const;
|
||||||
|
|
||||||
Vector<ValueAndAttributes> values_unordered() const;
|
Vector<ValueAndAttributes> values_unordered() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue