From 99594f518a5fb657b75f68bba73537c4e9208e46 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 3 May 2017 08:45:28 +0200 Subject: Re-add some inline property storage It turns out that not using any inline property storage comes at a relatively high price in terms of memory consumption, as we always need to also create a memberData for any object. This avoids the memberData creation in quite a few cases, as we use the additional padding we have up to the 32 byte boundary given by the memory manager to store some property data. This complicates property access somewhat. To avoid performance regressions because of this, add specialized QV4::Lookup functions that optimize for properties that are inline or in the memberData struct. Change seems to be performance neutral on v8-bench on x86_64, but reduces peak memory usage when running the benchmark by around 20%. Change-Id: I0127d31a2d6038aaa540c4c4a1156f45ca3b7464 Reviewed-by: Simon Hausmann Reviewed-by: Robin Burchell --- src/qml/jsruntime/qv4object.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 12157af728..9595158ce9 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,9 +61,13 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; + uint nInline = d()->vtable()->nInlineProperties; + if (ic->size <= nInline) + return; bool hasMD = d()->memberData != nullptr; - if ((!hasMD && ic->size) || (hasMD && d()->memberData->size < ic->size)) - d()->memberData = MemberData::allocate(ic->engine, ic->size, d()->memberData); + uint requiredSize = ic->size - nInline; + if (!hasMD || (hasMD && d()->memberData->size < requiredSize)) + d()->memberData = MemberData::allocate(ic->engine, requiredSize, d()->memberData); } void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const @@ -261,6 +265,13 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) o->arrayData->mark(e); if (o->prototype) o->prototype->mark(e); + uint nInline = o->vtable()->nInlineProperties; + Value *v = reinterpret_cast(o) + o->vt->inlinePropertyOffset; + const Value *end = v + nInline; + while (v < end) { + v->mark(e); + ++v; + } } void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) @@ -495,8 +506,15 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { if (attrs.isData()) { - if (l->level == 0) - l->getter = Lookup::getter0; + if (l->level == 0) { + uint nInline = o->d()->vt->nInlineProperties; + if (l->index < nInline) + l->getter = Lookup::getter0Inline; + else { + l->index -= nInline; + l->getter = Lookup::getter0MemberData; + } + } else if (l->level == 1) l->getter = Lookup::getter1; else if (l->level == 2) -- cgit v1.2.3 From 799f4a526375701d2b68d2ca1c85648994468394 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 3 May 2017 15:11:55 +0200 Subject: Optimize other lookups Add some more optimized lookups for accessing properties stored inline or in the memberData. Change-Id: Id74901d1dd91fd60933bf164c2bf90fed86232e3 Reviewed-by: Simon Hausmann Reviewed-by: Robin Burchell --- src/qml/jsruntime/qv4object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 9595158ce9..3d8e8f3ddf 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -549,7 +549,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) if (idx != UINT_MAX && o->internalClass()->propertyData[idx].isData() && o->internalClass()->propertyData[idx].isWritable()) { l->classList[0] = o->internalClass(); l->index = idx; - l->setter = Lookup::setter0; + l->setter = idx < o->d()->vt->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; *o->propertyData(idx) = value; return; } -- cgit v1.2.3 From cdbc4b83d59e08189d6ece9ccd88a646be155c08 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 15:01:07 +0200 Subject: Properly encapsulate all accesses to the vtable Change-Id: I3f6ae59d01c7b6c898e98d3b6f65b84a19b8851a Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4object.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 3d8e8f3ddf..f5dafa7914 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -266,7 +266,7 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) if (o->prototype) o->prototype->mark(e); uint nInline = o->vtable()->nInlineProperties; - Value *v = reinterpret_cast(o) + o->vt->inlinePropertyOffset; + Value *v = reinterpret_cast(o) + o->vtable()->inlinePropertyOffset; const Value *end = v + nInline; while (v < end) { v->mark(e); @@ -507,7 +507,7 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) if (v != Primitive::emptyValue().asReturnedValue()) { if (attrs.isData()) { if (l->level == 0) { - uint nInline = o->d()->vt->nInlineProperties; + uint nInline = o->d()->vtable()->nInlineProperties; if (l->index < nInline) l->getter = Lookup::getter0Inline; else { @@ -549,7 +549,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) if (idx != UINT_MAX && o->internalClass()->propertyData[idx].isData() && o->internalClass()->propertyData[idx].isWritable()) { l->classList[0] = o->internalClass(); l->index = idx; - l->setter = idx < o->d()->vt->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; + l->setter = idx < o->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; *o->propertyData(idx) = value; return; } -- cgit v1.2.3 From cae7975a036352ca4bbcf1381a445362f8e01367 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 May 2017 15:12:45 +0200 Subject: Move the internalClass field from Heap::Object to Heap::Base And do not store the vtable in Heap::Base anymore. This change makes the internal class the main distinguishing feature of all garbage collected objects. It also saves one pointer on all Objects. No measurable impact on runtime performance. Change-Id: I040a28b7581b993f1886b5219e279173dfa567e8 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4object.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index f5dafa7914..838ae96c59 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,6 +61,7 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; + Q_ASSERT(ic && ic->vtable); uint nInline = d()->vtable()->nInlineProperties; if (ic->size <= nInline) return; -- cgit v1.2.3 From afbb57ae84ecbee5fab9eb6e58356b19d7995ea5 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 15 May 2017 09:56:05 +0200 Subject: Move the prototype into the internal class This saves another pointer on all Objects. Currently introduces a slight performance regression on some of the v8 benchmarks, that needs addressing. Change-Id: I87de8e1d198d2683f4e903c467ce2a60ba542243 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4object.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 838ae96c59..4abe508e10 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -88,13 +88,14 @@ void Object::setProperty(uint index, const Property *p) bool Object::setPrototype(Object *proto) { - Heap::Object *pp = proto ? proto->d() : 0; + Heap::Object *p = proto ? proto->d() : 0; + Heap::Object *pp = p; while (pp) { if (pp == d()) return false; - pp = pp->prototype; + pp = pp->prototype(); } - d()->prototype = proto ? proto->d() : 0; + setInternalClass(internalClass()->changePrototype(p)); return true; } @@ -264,8 +265,6 @@ void Object::markObjects(Heap::Base *that, ExecutionEngine *e) o->memberData->mark(e); if (o->arrayData) o->arrayData->mark(e); - if (o->prototype) - o->prototype->mark(e); uint nInline = o->vtable()->nInlineProperties; Value *v = reinterpret_cast(o) + o->vtable()->inlinePropertyOffset; const Value *end = v + nInline; @@ -345,7 +344,7 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) return o->propertyData(attrs->isAccessor() ? idx + SetterOffset : idx); } - o = o->prototype; + o = o->prototype(); } *attrs = Attr_Invalid; return 0; @@ -368,7 +367,7 @@ Value *Object::getValueOrSetter(uint index, PropertyAttributes *attrs) return reinterpret_cast(0x1); } } - o = o->prototype; + o = o->prototype(); } *attrs = Attr_Invalid; return 0; @@ -1204,7 +1203,7 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) // 15.3.5.3, 4 while (v) { // 15.3.5.3, 4, a - v = v->prototype; + v = v->prototype(); // 15.3.5.3, 4, b if (!v) -- cgit v1.2.3 From 10b237882cfe76521b4dc65300a2a0473faca174 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 15 May 2017 12:42:56 +0200 Subject: Optimize lookups based on IC changes Change-Id: I1f4f4aaad0c8194bce2ebde4503df38cab0990a2 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4object.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 4abe508e10..c3ebb53622 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -505,7 +505,9 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) PropertyAttributes attrs; ReturnedValue v = l->lookup(o, &attrs); if (v != Primitive::emptyValue().asReturnedValue()) { + l->proto = l->classList[0]->prototype; if (attrs.isData()) { + Q_ASSERT(l->classList[0] == o->internalClass()); if (l->level == 0) { uint nInline = o->d()->vtable()->nInlineProperties; if (l->index < nInline) -- cgit v1.2.3 From c03eedaf8c6846dc80ab014face4d514deb9665d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 16 May 2017 15:03:39 +0200 Subject: Optimize lookups in the internalClass Inline the version taking an identifier, and use that one where it makes sense. Change-Id: I414c5999e61cdba219ecd1080957f3037dfebc1b Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4object.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index c3ebb53622..cac9d8ad7d 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -294,7 +294,10 @@ void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p if (idx != UINT_MAX) return getOwnProperty(idx, attrs, p); - uint member = internalClass()->find(name); + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + + uint member = internalClass()->find(id); if (member < UINT_MAX) { *attrs = internalClass()->propertyData[member]; if (p) { @@ -336,9 +339,12 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) { Q_ASSERT(name->asArrayIndex() == UINT_MAX); + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + Heap::Object *o = d(); while (o) { - uint idx = o->internalClass->find(name); + uint idx = o->internalClass->find(id); if (idx < UINT_MAX) { *attrs = o->internalClass->propertyData[idx]; return o->propertyData(attrs->isAccessor() ? idx + SetterOffset : idx); @@ -411,7 +417,10 @@ bool Object::hasOwnProperty(String *name) const if (idx != UINT_MAX) return hasOwnProperty(idx); - if (internalClass()->find(name) < UINT_MAX) + name->makeIdentifier(engine()); + Identifier *id = name->identifier(); + + if (internalClass()->find(id) < UINT_MAX) return true; if (!query(name).isEmpty()) return true; @@ -468,8 +477,11 @@ PropertyAttributes Object::query(const Managed *m, String *name) if (idx != UINT_MAX) return queryIndexed(m, idx); + name->makeIdentifier(m->internalClass()->engine); + Identifier *id = name->identifier(); + const Object *o = static_cast(m); - idx = o->internalClass()->find(name); + idx = o->internalClass()->find(id); if (idx < UINT_MAX) return o->internalClass()->propertyData[idx]; @@ -670,10 +682,11 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const Scope scope(engine()); name->makeIdentifier(scope.engine); + Identifier *id = name->identifier(); ScopedObject o(scope, this); while (o) { - uint idx = o->internalClass()->find(name); + uint idx = o->internalClass()->find(id); if (idx < UINT_MAX) { if (hasProperty) *hasProperty = true; @@ -736,8 +749,9 @@ void Object::internalPut(String *name, const Value &value) return putIndexed(idx, value); name->makeIdentifier(engine()); + Identifier *id = name->identifier(); - uint member = internalClass()->find(name); + uint member = internalClass()->find(id); Value *v = 0; PropertyAttributes attrs; if (member < UINT_MAX) { @@ -890,7 +904,7 @@ bool Object::internalDeleteProperty(String *name) name->makeIdentifier(engine()); - uint memberIdx = internalClass()->find(name); + uint memberIdx = internalClass()->find(name->identifier()); if (memberIdx != UINT_MAX) { if (internalClass()->propertyData[memberIdx].isConfigurable()) { InternalClass::removeMember(this, name->identifier()); @@ -961,7 +975,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const } // Clause 1 - memberIndex = internalClass()->find(name); + memberIndex = internalClass()->find(name->identifier()); if (memberIndex == UINT_MAX) { // clause 3 -- cgit v1.2.3 From 5bc38a50308665bdc185eb96dbcc9ba7948ab4e0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 May 2017 12:39:52 +0200 Subject: Get rid of the old way of defining builtin functions The old calling convention used for builtin functions is very inefficient. It was still being used in a few places. Clean those up and convert them to the new and much more effiecient calling convention. Change-Id: I6b769c6185df7e9be1e80709330fc1ca868576c1 Reviewed-by: Robin Burchell --- src/qml/jsruntime/qv4object.cpp | 40 ---------------------------------------- 1 file changed, 40 deletions(-) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index cac9d8ad7d..04336d4f88 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -162,17 +162,6 @@ void Object::defineDefaultProperty(const QString &name, const Value &value) defineDefaultProperty(s, value); } -void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ScopedString s(scope, e->newIdentifier(name)); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(s, function); -} - void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) { ExecutionEngine *e = engine(); @@ -184,16 +173,6 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built defineDefaultProperty(s, function); } -void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ExecutionContext *global = e->rootContext(); - ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); - defineDefaultProperty(name, function); -} - void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) { ExecutionEngine *e = engine(); @@ -204,25 +183,6 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct defineDefaultProperty(name, function); } -void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) -{ - ExecutionEngine *e = engine(); - Scope scope(e); - ScopedString s(scope, e->newIdentifier(name)); - defineAccessorProperty(s, getter, setter); -} - -void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *)) -{ - ExecutionEngine *v4 = engine(); - QV4::Scope scope(v4); - ScopedProperty p(scope); - ExecutionContext *global = v4->rootContext(); - p->setGetter(ScopedFunctionObject(scope, (getter ? BuiltinFunction::create(global, name, getter) : 0))); - p->setSetter(ScopedFunctionObject(scope, (setter ? BuiltinFunction::create(global, name, setter) : 0))); - insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); -} - void Object::defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), void (*setter)(const BuiltinFunction *, Scope &, CallData *)) { -- cgit v1.2.3 From 8bc243f569e3feb1005fbca426bf24f59c38af2e Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 19 May 2017 15:50:22 +0200 Subject: Move the engine() accessor from Object to Managed We can easily do this now that Managed has a pointer to an internal class (which always has a back pointer to the ExecutionEngine). Remove the extra engine pointer from ExecutionContext, and clean up tow methods in String. Change-Id: I98d750b1afbdeadf42e66ae0c92c48db1a7adc31 Reviewed-by: Robin Burchell --- src/qml/jsruntime/qv4object.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/qml/jsruntime/qv4object.cpp') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 04336d4f88..98f5c7464f 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -254,7 +254,7 @@ void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p if (idx != UINT_MAX) return getOwnProperty(idx, attrs, p); - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); uint member = internalClass()->find(id); @@ -299,7 +299,7 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) { Q_ASSERT(name->asArrayIndex() == UINT_MAX); - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); Heap::Object *o = d(); @@ -377,7 +377,7 @@ bool Object::hasOwnProperty(String *name) const if (idx != UINT_MAX) return hasOwnProperty(idx); - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); if (internalClass()->find(id) < UINT_MAX) @@ -437,7 +437,7 @@ PropertyAttributes Object::query(const Managed *m, String *name) if (idx != UINT_MAX) return queryIndexed(m, idx); - name->makeIdentifier(m->internalClass()->engine); + name->makeIdentifier(); Identifier *id = name->identifier(); const Object *o = static_cast(m); @@ -640,10 +640,10 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const if (idx != UINT_MAX) return getIndexed(idx, hasProperty); - Scope scope(engine()); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); Identifier *id = name->identifier(); + Scope scope(engine()); ScopedObject o(scope, this); while (o) { uint idx = o->internalClass()->find(id); @@ -708,7 +708,7 @@ void Object::internalPut(String *name, const Value &value) if (idx != UINT_MAX) return putIndexed(idx, value); - name->makeIdentifier(engine()); + name->makeIdentifier(); Identifier *id = name->identifier(); uint member = internalClass()->find(id); @@ -862,7 +862,7 @@ bool Object::internalDeleteProperty(String *name) if (idx != UINT_MAX) return deleteIndexedProperty(idx); - name->makeIdentifier(engine()); + name->makeIdentifier(); uint memberIdx = internalClass()->find(name->identifier()); if (memberIdx != UINT_MAX) { @@ -901,7 +901,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const return __defineOwnProperty__(engine, idx, p, attrs); Scope scope(engine); - name->makeIdentifier(scope.engine); + name->makeIdentifier(); uint memberIndex; -- cgit v1.2.3