Speed up Context creation and handling
authorLars Knoll <lars.knoll@digia.com>
Wed, 3 Apr 2013 13:59:17 +0000 (15:59 +0200)
committerSimon Hausmann <simon.hausmann@digia.com>
Wed, 3 Apr 2013 17:55:03 +0000 (19:55 +0200)
Creating context and locals in two mallocs wasn't very good
for performance. This allocates them in one go again, while
still managing them through the garbage collector.

This brings performance up by around 20%.

Change-Id: I9b31d669e1a502c90a117bacf5fee5d23e9821b4
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
src/v4/qv4argumentsobject.cpp
src/v4/qv4context.cpp
src/v4/qv4context.h
src/v4/qv4engine.cpp
src/v4/qv4managed.h
src/v4/qv4mm.cpp
src/v4/qv4mm.h

index 7667aaa..b891d02 100644 (file)
@@ -168,6 +168,7 @@ Value ArgumentsSetterFunction::call(Managed *setter, ExecutionContext *ctx, cons
 void ArgumentsObject::markObjects(Managed *that)
 {
     ArgumentsObject *o = static_cast<ArgumentsObject *>(that);
+    o->context->mark();
     for (int i = 0; i < o->mappedArguments.size(); ++i) {
         Managed *m = o->mappedArguments.at(i).asManaged();
         if (m)
index 41b0b2a..51a80f4 100644 (file)
@@ -79,8 +79,6 @@ String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const
     return ctx->engine->newString(msg);
 }
 
-DEFINE_MANAGED_VTABLE(ExecutionContext);
-
 void ExecutionContext::createMutableBinding(String *name, bool deletable)
 {
     if (!activation)
@@ -177,6 +175,7 @@ unsigned int ExecutionContext::variableCount() const
 
 void ExecutionContext::init(ExecutionEngine *eng)
 {
+    marked = false;
     engine = eng;
     outer = 0;
     thisObject = eng->globalObject;
@@ -184,6 +183,7 @@ void ExecutionContext::init(ExecutionEngine *eng)
     function = 0;
     lookups = 0;
 
+    locals = 0;
     arguments = 0;
     argumentCount = 0;
     locals = 0;
@@ -197,6 +197,7 @@ void ExecutionContext::init(ExecutionEngine *eng)
 
 void ExecutionContext::init(ExecutionContext *p, Object *with)
 {
+    marked = false;
     engine = p->engine;
     outer = p;
     thisObject = p->thisObject;
@@ -204,6 +205,7 @@ void ExecutionContext::init(ExecutionContext *p, Object *with)
     function = 0;
     lookups = p->lookups;
 
+    locals = 0;
     arguments = 0;
     argumentCount = 0;
     locals = 0;
@@ -217,6 +219,7 @@ void ExecutionContext::init(ExecutionContext *p, Object *with)
 
 void ExecutionContext::initForCatch(ExecutionContext *p, String *exceptionVarName, const Value &exceptionValue)
 {
+    marked = false;
     engine = p->engine;
     outer = p;
     thisObject = p->thisObject;
@@ -269,76 +272,29 @@ bool ExecutionContext::needsOwnArguments() const
     return function && (function->needsActivation || argumentCount < function->formalParameterCount);
 }
 
-void ExecutionContext::destroy(Managed *that)
-{
-    ExecutionContext *ctx = static_cast<ExecutionContext *>(that);
-    if (ctx->locals)
-        delete [] ctx->locals;
-    ctx->_data = 0;
-    ctx->vtbl = 0;
-}
-
-void ExecutionContext::markObjects(Managed *that)
-{
-    ExecutionContext *ctx = static_cast<ExecutionContext *>(that);
-    ctx->thisObject.mark();
-    if (ctx->function)
-        ctx->function->mark();
-    for (unsigned arg = 0, lastArg = ctx->argumentCount; arg < lastArg; ++arg)
-        ctx->arguments[arg].mark();
-    for (unsigned local = 0, lastLocal = ctx->variableCount(); local < lastLocal; ++local)
-        ctx->locals[local].mark();
-    if (ctx->activation)
-        ctx->activation->mark();
-    if (ctx->withObject)
-        ctx->withObject->mark();
-    if (ctx->exceptionVarName)
-        ctx->exceptionVarName->mark();
-    ctx->exceptionValue.mark();
-}
-
-Value ExecutionContext::get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty)
-{
-    Q_UNIMPLEMENTED();
-    Q_UNREACHABLE();
-}
-
-Value ExecutionContext::getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty)
-{
-    Q_UNIMPLEMENTED();
-    Q_UNREACHABLE();
-}
-
-void ExecutionContext::put(Managed *m, ExecutionContext *ctx, String *name, const Value &value)
-{
-}
-
-void ExecutionContext::putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value)
+void ExecutionContext::mark()
 {
-}
-
-PropertyFlags ExecutionContext::query(Managed *m, ExecutionContext *ctx, String *name)
-{
-    Q_UNIMPLEMENTED();
-    Q_UNREACHABLE();
-}
-
-PropertyFlags ExecutionContext::queryIndexed(Managed *m, ExecutionContext *ctx, uint index)
-{
-    Q_UNIMPLEMENTED();
-    Q_UNREACHABLE();
-}
-
-bool ExecutionContext::deleteProperty(Managed *m, ExecutionContext *ctx, String *name)
-{
-    Q_UNIMPLEMENTED();
-    Q_UNREACHABLE();
-}
-
-bool ExecutionContext::deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index)
-{
-    Q_UNIMPLEMENTED();
-    Q_UNREACHABLE();
+    if (marked)
+        return;
+    marked = true;
+
+    if (outer)
+        outer->mark();
+
+    thisObject.mark();
+    if (function)
+        function->mark();
+    for (unsigned arg = 0, lastArg = argumentCount; arg < lastArg; ++arg)
+        arguments[arg].mark();
+    for (unsigned local = 0, lastLocal = variableCount(); local < lastLocal; ++local)
+        locals[local].mark();
+    if (activation)
+        activation->mark();
+    if (withObject)
+        withObject->mark();
+    if (exceptionVarName)
+        exceptionVarName->mark();
+    exceptionValue.mark();
 }
 
 void ExecutionContext::setProperty(String *name, const Value& value)
@@ -583,6 +539,7 @@ void ExecutionContext::throwURIError(Value msg)
 
 void ExecutionContext::initCallContext(ExecutionEngine *engine)
 {
+    marked = false;
     this->engine = engine;
     outer = function->scope;
 
@@ -599,27 +556,22 @@ void ExecutionContext::initCallContext(ExecutionEngine *engine)
         lookups = function->function->lookups;
 
     uint argc = argumentCount;
-    uint valuesToAlloc = function->varCount;
-    bool copyArgs = needsOwnArguments();
-    if (copyArgs)
-        valuesToAlloc += qMax(argc, function->formalParameterCount);
-
-    if (valuesToAlloc) {
-        locals = new Value[valuesToAlloc];
-        if (function->varCount)
-            std::fill(locals, locals + function->varCount, Value::undefinedValue());
-
-        if (copyArgs) {
-            Value *args = arguments;
-            argumentCount = qMax(argc, function->formalParameterCount);
-            arguments = locals + function->varCount;
-            if (argc)
-                ::memcpy(arguments, args, argc * sizeof(Value));
-            if (argc < function->formalParameterCount)
-                std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue());
 
-        }
+    locals = (Value *)(this + 1);
+    if (function->varCount)
+        std::fill(locals, locals + function->varCount, Value::undefinedValue());
+
+    if (needsOwnArguments()) {
+        Value *args = arguments;
+        argumentCount = qMax(argc, function->formalParameterCount);
+        arguments = locals + function->varCount;
+        if (argc)
+            ::memcpy(arguments, args, argc * sizeof(Value));
+        if (argc < function->formalParameterCount)
+            std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue());
+
     }
+
     if (function->usesArgumentsObject) {
         ArgumentsObject *args = new (engine->memoryManager) ArgumentsObject(this, function->formalParameterCount, argc);
         args->prototype = engine->objectPrototype;
index 35c586f..d2b05bb 100644 (file)
@@ -43,7 +43,6 @@
 
 #include "qv4global.h"
 #include <qv4runtime.h>
-#include "qv4managed.h"
 
 QT_BEGIN_NAMESPACE
 
@@ -53,7 +52,6 @@ namespace VM {
 struct Value;
 struct Object;
 struct ExecutionEngine;
-struct ExecutionContext;
 struct DeclarativeEnvironment;
 struct Lookup;
 
@@ -75,8 +73,9 @@ struct Q_V4_EXPORT DiagnosticMessage
     String *buildFullMessage(ExecutionContext *ctx) const;
 };
 
-struct ExecutionContext : public Managed
+struct ExecutionContext
 {
+    ExecutionContext *next; // used in the GC
     ExecutionEngine *engine;
     ExecutionContext *outer;
     Value thisObject;
@@ -91,33 +90,28 @@ struct ExecutionContext : public Managed
     String *exceptionVarName;
     Value exceptionValue;
 
-    String * const *formals() const;
-    unsigned int formalCount() const;
-    String * const *variables() const;
-    unsigned int variableCount() const;
-
     bool strictMode;
     bool qmlObject; // ## temporary until we do proper QML contexts
+    bool marked;
 
     Object *activation;
     Object *withObject;
 
-    ExecutionContext()
-        : Managed()
-        , locals(0)
-    { vtbl = &static_vtbl; }
+    String * const *formals() const;
+    unsigned int formalCount() const;
+    String * const *variables() const;
+    unsigned int variableCount() const;
 
     void init(ExecutionEngine *e);
     void init(ExecutionContext *p, Object *with);
     void initForCatch(ExecutionContext *p, String *exceptionVarName, const QQmlJS::VM::Value &exceptionValue);
+    void initCallContext(QQmlJS::VM::ExecutionEngine *engine);
 
     void createMutableBinding(String *name, bool deletable);
     bool setMutableBinding(ExecutionContext *scope, String *name, const Value &value);
     Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const;
     bool deleteBinding(ExecutionContext *ctx, String *name);
 
-    void initCallContext(QQmlJS::VM::ExecutionEngine *engine);
-
     void wireUpPrototype();
 
     void Q_NORETURN throwError(const Value &value);
@@ -145,21 +139,12 @@ struct ExecutionContext : public Managed
 
     bool needsOwnArguments() const;
 
-protected:
-    static void destroy(Managed *that);
-    static void markObjects(Managed *that);
-    static Value get(Managed *m, ExecutionContext *ctx, String *name, bool *hasProperty);
-    static Value getIndexed(Managed *m, ExecutionContext *ctx, uint index, bool *hasProperty);
-    static void put(Managed *m, ExecutionContext *ctx, String *name, const Value &value);
-    static void putIndexed(Managed *m, ExecutionContext *ctx, uint index, const Value &value);
-    static PropertyFlags query(Managed *m, ExecutionContext *ctx, String *name);
-    static PropertyFlags queryIndexed(Managed *m, ExecutionContext *ctx, uint index);
-    static bool deleteProperty(Managed *m, ExecutionContext *ctx, String *name);
-    static bool deleteIndexedProperty(Managed *m, ExecutionContext *ctx, uint index);
-
-    static const ManagedVTable static_vtbl;
+    void mark();
 };
 
+/* Function *f, int argc */
+#define requiredMemoryForExecutionContect(f, argc) \
+    sizeof(ExecutionContext) + sizeof(Value) * (f->varCount + qMax((uint)argc, f->formalParameterCount))
 
 } // namespace VM
 } // namespace QQmlJS
index f7c0098..d944de8 100644 (file)
@@ -243,7 +243,6 @@ ExecutionEngine::~ExecutionEngine()
 {
     delete regExpCache;
     delete globalObject.asObject();
-    delete rootContext;
     delete [] contextStack;
     UnwindHelper::deregisterFunctions(functions);
     qDeleteAll(functions);
@@ -254,10 +253,10 @@ ExecutionEngine::~ExecutionEngine()
 void ExecutionEngine::initRootContext()
 {
     ensureContextStackSize();
-    rootContext = new (memoryManager) ExecutionContext();
-    rootContext->init(this);
+    rootContext = memoryManager->allocContext(sizeof(ExecutionContext));
     current = rootContext;
     contextStack[0] = rootContext;
+    current->init(this);
 }
 
 void ExecutionEngine::ensureContextStackSize()
@@ -270,6 +269,7 @@ void ExecutionEngine::ensureContextStackSize()
     if (contextStack)
         memcpy(newStack, contextStack, contextStackSize*sizeof(ExecutionContext *));
     memset(newStack + contextStackSize, 0, (stackSize - contextStackSize)*sizeof(ExecutionContext *));
+    delete [] contextStack;
     contextStackSize = stackSize;
     contextStack = newStack;
 }
@@ -279,11 +279,11 @@ ExecutionContext *ExecutionEngine::newWithContext(Object *with)
     ensureContextStackSize();
     assert(contextStack[contextStackPosition + 1] == 0);
 
-    ExecutionContext *ctx = new (memoryManager) ExecutionContext();
-    ctx->init(current, with);
-    current = ctx;
-
+    ExecutionContext *c = current;
+    current = memoryManager->allocContext(sizeof(ExecutionContext));
     contextStack[++contextStackPosition] = current;
+
+    current->init(c, with);
     return current;
 }
 
@@ -292,11 +292,11 @@ ExecutionContext *ExecutionEngine::newCatchContext(String *exceptionVarName, con
     ensureContextStackSize();
     assert(contextStack[contextStackPosition + 1] == 0);
 
-    ExecutionContext *ctx = new (memoryManager) ExecutionContext();
-    ctx->initForCatch(current, exceptionVarName, exceptionValue);
-    current = ctx;
-
+    ExecutionContext *c = current;
+    current = memoryManager->allocContext(sizeof(ExecutionContext));
     contextStack[++contextStackPosition] = current;
+
+    current->initForCatch(c, exceptionVarName, exceptionValue);
     return current;
 }
 
@@ -305,14 +305,15 @@ ExecutionContext *ExecutionEngine::newCallContext(FunctionObject *f, const Value
     ensureContextStackSize();
     assert(contextStack[contextStackPosition + 1] == 0);
 
-    current = new (memoryManager) ExecutionContext();
+    current = memoryManager->allocContext(requiredMemoryForExecutionContect(f, argc));
+    contextStack[++contextStackPosition] = current;
+
     current->function = f;
     current->thisObject = thisObject;
     current->arguments = args;
     current->argumentCount = argc;
     current->initCallContext(this);
 
-    contextStack[++contextStackPosition] = current;
     return current;
 }
 
@@ -530,6 +531,8 @@ void ExecutionEngine::markObjects()
         pd.set->mark();
     }
 
+    for (int i = 0; i <= contextStackPosition; ++i)
+        contextStack[i]->mark();
 
     for (int i = 0; i < functions.size(); ++i)
         functions.at(i)->mark();
index a69adf3..264c92c 100644 (file)
@@ -236,7 +236,6 @@ protected:
 
 private:
     friend class MemoryManager;
-    friend struct ExecutionContext;
     friend struct Identifiers;
 };
 
index 26c26c0..0281aec 100644 (file)
@@ -128,7 +128,8 @@ bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::C
 } } // namespace QQmlJS::VM
 
 MemoryManager::MemoryManager()
-    : m_d(new Data(true))
+    : m_d(new Data(true))\
+    , m_contextList(0)
 {
     setEnableGC(true);
 #ifdef V4_USE_VALGRIND
@@ -280,6 +281,20 @@ std::size_t MemoryManager::sweep()
     for (QVector<Data::Chunk>::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i)
         freedCount += sweep(reinterpret_cast<char*>(i->memory.base()), i->memory.size(), i->chunkSize);
 
+    ExecutionContext *ctx = m_contextList;
+    ExecutionContext **n = &m_contextList;
+    while (ctx) {
+        ExecutionContext *next = ctx->next;
+        if (!ctx->marked) {
+            free(ctx);
+            *n = next;
+        } else {
+            ctx->marked = false;
+            n = &ctx->next;
+        }
+        ctx = next;
+    }
+
     return freedCount;
 }
 
index fdde761..a41df83 100644 (file)
@@ -43,6 +43,7 @@
 #define QV4GC_H
 
 #include "qv4global.h"
+#include "qv4context.h"
 
 #include <QScopedPointer>
 
@@ -54,6 +55,7 @@ namespace QQmlJS {
 namespace VM {
 
 struct ExecutionEngine;
+struct ExecutionContext;
 struct Managed;
 
 class Q_V4_EXPORT MemoryManager
@@ -103,6 +105,8 @@ public:
         return o;
     }
 
+    ExecutionContext *allocContext(uint size);
+
     bool isGCBlocked() const;
     void setGCBlocked(bool blockGC);
     void runGC();
@@ -131,8 +135,18 @@ private:
 
 protected:
     QScopedPointer<Data> m_d;
+    ExecutionContext *m_contextList;
 };
 
+inline ExecutionContext *MemoryManager::allocContext(uint size)
+{
+    ExecutionContext *newContext = (ExecutionContext *)malloc(size);
+    newContext->next = m_contextList;
+    m_contextList = newContext;
+    return newContext;
+}
+
+
 } // namespace VM
 } // namespace QQmlJS