From ce26bfcb3952dcd1238e7aa07b9ab1d9585c9871 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 14 Nov 2013 11:08:41 +0100 Subject: [PATCH] Speed up arguments object 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 --- src/qml/jsruntime/qv4argumentsobject.cpp | 101 +++++++++++++++++++++++++------ src/qml/jsruntime/qv4argumentsobject_p.h | 10 ++- src/qml/jsruntime/qv4functionobject.cpp | 2 +- src/qml/jsruntime/qv4object.cpp | 2 +- src/qml/jsruntime/qv4objectiterator.cpp | 11 ++++ src/qml/jsruntime/qv4objectproto.cpp | 7 +++ 6 files changed, 110 insertions(+), 23 deletions(-) diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index ed010b1..4bf4d19 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -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(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(m); + if (args->fullyCreated) + return Object::getIndexed(m, index, hasProperty); + + if (index < static_cast(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(m); + if (!args->fullyCreated && index >= static_cast(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(m); + if (!args->fullyCreated) + args->fullyCreate(); + return Object::deleteIndexedProperty(m, index); +} + +PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) +{ + const ArgumentsObject *args = static_cast(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) diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 7a5b081..7c58c48 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -74,20 +74,26 @@ struct ArgumentsSetterFunction: FunctionObject struct ArgumentsObject: Object { Q_MANAGED CallContext *context; + bool fullyCreated; QVector 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(); }; } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index aa1cb89..8c3131c 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -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 { diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index cca7d2b..e4df957 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -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; diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 62595b5..04fa504 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -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 (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 (scope, object->asReturnedValue())->fullyCreate(); + } } Property *ObjectIterator::next(StringRef name, uint *index, PropertyAttributes *attrs) diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 4369267..f17bd7d 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -41,6 +41,7 @@ #include "qv4objectproto_p.h" +#include "qv4argumentsobject_p.h" #include "qv4mm_p.h" #include "qv4scopedvalue_p.h" #include @@ -156,6 +157,9 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx) if (!O) return ctx->throwTypeError(); + if (O->isNonStrictArgumentsObject) + Scoped(scope, O)->fullyCreate(); + ScopedValue v(scope, ctx->argument(1)); Scoped 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(scope, o)->fullyCreate(); + o->extensible = false; o->internalClass = o->internalClass->frozen(); -- 2.7.4