Speed up arguments object
authorLars Knoll <lars.knoll@digia.com>
Thu, 14 Nov 2013 10:08:41 +0000 (11:08 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Fri, 22 Nov 2013 13:54:12 +0000 (14:54 +0100)
Don't fully create the arguments object unless required.
In the 95% use case, we can avoid creating any array based
data structures for the arguments object and directly
manipulate the functions arguments. only create the full
data structure for the other 5%.

Speeds up the raytrace test by 50%, gives around 10% overall
on v8-bench.

Change-Id: If727d28b96585e83314f544031a6c3ca1817ea19
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/qml/jsruntime/qv4argumentsobject.cpp
src/qml/jsruntime/qv4argumentsobject_p.h
src/qml/jsruntime/qv4functionobject.cpp
src/qml/jsruntime/qv4object.cpp
src/qml/jsruntime/qv4objectiterator.cpp
src/qml/jsruntime/qv4objectproto.cpp

index ed010b1..4bf4d19 100644 (file)
@@ -47,10 +47,11 @@ using namespace QV4;
 DEFINE_MANAGED_VTABLE(ArgumentsObject);
 
 ArgumentsObject::ArgumentsObject(CallContext *context)
-    : Object(context->engine), context(context)
+    : Object(context->engine), context(context), fullyCreated(false)
 {
     vtbl = &static_vtbl;
     type = Type_ArgumentsObject;
+    flags &= ~SimpleArray;
 
     ExecutionEngine *v4 = context->engine;
     Scope scope(v4);
@@ -60,8 +61,8 @@ ArgumentsObject::ArgumentsObject(CallContext *context)
         internalClass = v4->strictArgumentsObjectClass;
 
         Property pd = Property::fromAccessor(v4->thrower, v4->thrower);
-        assert(CalleePropertyIndex == internalClass->find(context->engine->id_callee));
-        assert(CallerPropertyIndex == internalClass->find(context->engine->id_caller));
+        Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee));
+        Q_ASSERT(CallerPropertyIndex == internalClass->find(context->engine->id_caller));
         memberData[CalleePropertyIndex] = pd;
         memberData[CallerPropertyIndex] = pd;
 
@@ -69,27 +70,12 @@ ArgumentsObject::ArgumentsObject(CallContext *context)
         for (int i = 0; i < context->callData->argc; ++i)
             arrayData[i].value = context->callData->args[i];
         arrayDataLen = context->callData->argc;
+        fullyCreated = true;
     } else {
         internalClass = engine()->argumentsObjectClass;
         Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee));
         memberData[CalleePropertyIndex].value = context->function->asReturnedValue();
         isNonStrictArgumentsObject = true;
-
-        uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount);
-        uint argCount = qMin(context->realArgumentCount, context->callData->argc);
-        arrayReserve(argCount);
-        ensureArrayAttributes();
-        context->engine->requireArgumentsAccessors(numAccessors);
-        for (uint i = 0; i < (uint)numAccessors; ++i) {
-            mappedArguments.append(context->callData->args[i]);
-            arrayData[i] = context->engine->argumentsAccessors.at(i);
-            arrayAttributes[i] = Attr_Accessor;
-        }
-        for (uint i = numAccessors; i < argCount; ++i) {
-            arrayData[i] = Property::fromValue(context->callData->args[i]);
-            arrayAttributes[i] = Attr_Data;
-        }
-        arrayDataLen = argCount;
     }
     Q_ASSERT(LengthPropertyIndex == internalClass->find(context->engine->id_length));
     Property *lp = memberData + ArrayObject::LengthPropertyIndex;
@@ -101,8 +87,34 @@ void ArgumentsObject::destroy(Managed *that)
     static_cast<ArgumentsObject *>(that)->~ArgumentsObject();
 }
 
+void ArgumentsObject::fullyCreate()
+{
+    if (fullyCreated)
+        return;
+
+    uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount);
+    uint argCount = qMin(context->realArgumentCount, context->callData->argc);
+    arrayReserve(argCount);
+    ensureArrayAttributes();
+    context->engine->requireArgumentsAccessors(numAccessors);
+    for (uint i = 0; i < (uint)numAccessors; ++i) {
+        mappedArguments.append(context->callData->args[i]);
+        arrayData[i] = context->engine->argumentsAccessors.at(i);
+        arrayAttributes[i] = Attr_Accessor;
+    }
+    for (uint i = numAccessors; i < argCount; ++i) {
+        arrayData[i] = Property::fromValue(context->callData->args[i]);
+        arrayAttributes[i] = Attr_Data;
+    }
+    arrayDataLen = argCount;
+
+    fullyCreated = true;
+}
+
 bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs)
 {
+    fullyCreate();
+
     Scope scope(ctx);
     uint pidx = propertyIndexFromArrayIndex(index);
     Property *pd = arrayData + pidx;
@@ -143,6 +155,57 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const
     return result;
 }
 
+ReturnedValue ArgumentsObject::getIndexed(Managed *m, uint index, bool *hasProperty)
+{
+    ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
+    if (args->fullyCreated)
+        return Object::getIndexed(m, index, hasProperty);
+
+    if (index < static_cast<uint>(args->context->callData->argc)) {
+        if (hasProperty)
+            *hasProperty = true;
+        return args->context->callData->args[index].asReturnedValue();
+    }
+    return Encode::undefined();
+}
+
+void ArgumentsObject::putIndexed(Managed *m, uint index, const ValueRef value)
+{
+    ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
+    if (!args->fullyCreated && index >= static_cast<uint>(args->context->callData->argc))
+        args->fullyCreate();
+
+    if (args->fullyCreated) {
+        Object::putIndexed(m, index, value);
+        return;
+    }
+
+    args->context->callData->args[index] = value;
+}
+
+bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index)
+{
+    ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
+    if (!args->fullyCreated)
+        args->fullyCreate();
+    return Object::deleteIndexedProperty(m, index);
+}
+
+PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index)
+{
+    const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
+    if (args->fullyCreated)
+        return Object::queryIndexed(m, index);
+
+    uint numAccessors = qMin((int)args->context->function->formalParameterCount, args->context->realArgumentCount);
+    uint argCount = qMin(args->context->realArgumentCount, args->context->callData->argc);
+    if (index >= argCount)
+        return PropertyAttributes();
+    if (index >= numAccessors)
+        return Attr_Data;
+    return Attr_Accessor;
+}
+
 DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction);
 
 ReturnedValue ArgumentsGetterFunction::call(Managed *getter, CallData *callData)
index 7a5b081..7c58c48 100644 (file)
@@ -74,20 +74,26 @@ struct ArgumentsSetterFunction: FunctionObject
 struct ArgumentsObject: Object {
     Q_MANAGED
     CallContext *context;
+    bool fullyCreated;
     QVector<SafeValue> mappedArguments;
     ArgumentsObject(CallContext *context);
     ~ArgumentsObject() {}
 
+
     enum {
         LengthPropertyIndex = 0,
         CalleePropertyIndex = 1,
         CallerPropertyIndex = 2
     };
     bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs);
-
+    static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty);
+    static void putIndexed(Managed *m, uint index, const ValueRef value);
+    static bool deleteIndexedProperty(Managed *m, uint index);
+    static PropertyAttributes queryIndexed(const Managed *m, uint index);
     static void markObjects(Managed *that, ExecutionEngine *e);
-protected:
     static void destroy(Managed *);
+
+    void fullyCreate();
 };
 
 }
index aa1cb89..8c3131c 100644 (file)
@@ -348,7 +348,7 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx)
     ScopedCallData callData(scope, len);
 
     if (len) {
-        if (arr->protoHasArray() || arr->hasAccessorProperty) {
+        if (!(arr->flags & SimpleArray) || arr->protoHasArray() || arr->hasAccessorProperty) {
             for (quint32 i = 0; i < len; ++i)
                 callData->args[i] = arr->getIndexed(i);
         } else {
index cca7d2b..e4df957 100644 (file)
@@ -1133,7 +1133,7 @@ ReturnedValue Object::arrayIndexOf(const ValueRef v, uint fromIndex, uint endInd
     Scope scope(engine());
     ScopedValue value(scope);
 
-    if (!(flags & SimpleArray) || o->protoHasArray() || o->arrayAttributes) {
+    if (!(o->flags & SimpleArray) || o->protoHasArray()) {
         // lets be safe and slow
         for (uint i = fromIndex; i < endIndex; ++i) {
             bool exists;
index 62595b5..04fa504 100644 (file)
@@ -42,6 +42,7 @@
 #include "qv4object_p.h"
 #include "qv4stringobject_p.h"
 #include "qv4identifier_p.h"
+#include "qv4argumentsobject_p.h"
 
 using namespace QV4;
 
@@ -56,6 +57,11 @@ ObjectIterator::ObjectIterator(SafeObject *scratch1, SafeObject *scratch2, const
     object = o;
     current = o;
     tmpDynamicProperty.value = Primitive::undefinedValue();
+
+    if (object && object->isNonStrictArgumentsObject) {
+        Scope scope(object->engine());
+        Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate();
+    }
 }
 
 ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags)
@@ -69,6 +75,11 @@ ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags)
     object = o;
     current = o;
     tmpDynamicProperty.value = Primitive::undefinedValue();
+
+    if (object && object->isNonStrictArgumentsObject) {
+        Scope scope(object->engine());
+        Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate();
+    }
 }
 
 Property *ObjectIterator::next(StringRef name, uint *index, PropertyAttributes *attrs)
index 4369267..f17bd7d 100644 (file)
@@ -41,6 +41,7 @@
 
 
 #include "qv4objectproto_p.h"
+#include "qv4argumentsobject_p.h"
 #include "qv4mm_p.h"
 #include "qv4scopedvalue_p.h"
 #include <QtCore/qnumeric.h>
@@ -156,6 +157,9 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx)
     if (!O)
         return ctx->throwTypeError();
 
+    if (O->isNonStrictArgumentsObject)
+        Scoped<ArgumentsObject>(scope, O)->fullyCreate();
+
     ScopedValue v(scope, ctx->argument(1));
     Scoped<String> name(scope, v->toString(ctx));
     if (scope.hasException())
@@ -283,6 +287,9 @@ ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx)
     if (!o)
         return ctx->throwTypeError();
 
+    if (o->isNonStrictArgumentsObject)
+        Scoped<ArgumentsObject>(scope, o)->fullyCreate();
+
     o->extensible = false;
 
     o->internalClass = o->internalClass->frozen();