Bug 547327 - Estimate optimal number of JSObject slots (r=lw)
☠☠ backed out by 8bb016a281c0 ☠ ☠
authorBill McCloskey <wmccloskey@mozilla.com>
Tue, 21 Dec 2010 15:54:25 -0800
changeset 59910 d75da3b1209855955b1081854fb469f728a422a4
parent 59909 8f24dc55e1696c83de795ed31ffbe43ac664e25e
child 59911 8bb016a281c0911c309aba008e10c58b5c3a9289
push id17820
push usercleary@mozilla.com
push dateTue, 04 Jan 2011 21:40:57 +0000
treeherdermozilla-central@969691cfe40e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslw
bugs547327
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 547327 - Estimate optimal number of JSObject slots (r=lw)
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -73,16 +73,17 @@
 #include "jsscript.h"
 #include "jsstaticcheck.h"
 #include "jsstdint.h"
 #include "jsstr.h"
 #include "jstracer.h"
 #include "jsdbgapi.h"
 #include "json.h"
 #include "jswrapper.h"
+#include "jstl.h"
 
 #include "jsinterpinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 #include "jsobjinlines.h"
 
 #if JS_HAS_GENERATORS
 #include "jsiter.h"
@@ -2813,20 +2814,61 @@ js_CreateThis(JSContext *cx, JSObject *c
     JSObject *parent = callee->getParent();
     gc::FinalizeKind kind = NewObjectGCKind(cx, newclasp);
     JSObject *obj = NewObject<WithProto::Class>(cx, newclasp, proto, parent, kind);
     if (obj)
         obj->syncSpecialEquality();
     return obj;
 }
 
+static uintN
+GuessObjectSizeFromConstructor(JSObject *callee)
+{
+    JSFunction *fun = callee->getFunctionPrivate();
+    JSScript *script = fun->script();
+    jsbytecode *pc = script->code;
+    jsbytecode *end = script->code + script->length;
+    uintN count = 0;
+    
+    while (pc < end) {
+        if (JSOp(*pc) == JSOP_SETPROP)
+            count++;
+
+        const JSCodeSpec *cs = &js_CodeSpec[JSOp(*pc)];
+        ptrdiff_t oplen = cs->length;
+        if (oplen < 0)
+            oplen = js_GetVariableBytecodeLength(pc);
+        pc += oplen;
+    }
+
+    return count;
+}
+
+static gc::FinalizeKind
+GuessObjectGCKindByFlags(JSContext *cx, JSObject *callee, JSObject *proto, Class *clasp)
+{
+    gc::FinalizeKind kind;
+    if (proto && clasp == &js_ObjectClass) {
+        if (proto->hasStoredGCKind()) {
+            kind = gc::FinalizeKind(proto->getStoredGCKind());
+        } else {
+            uintN count = GuessObjectSizeFromConstructor(callee);
+            kind = gc::GetGCObjectKind(count);
+            proto->setStoredGCKind(kind);
+        }
+    } else {
+        kind = NewObjectGCKind(cx, clasp);
+    }
+    return kind;
+}
+
 JSObject *
 js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
 {
-    gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
+    gc::FinalizeKind kind = GuessObjectGCKindByFlags(cx, callee, proto, &js_ObjectClass);
     return NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, proto, callee->getParent(), kind);
 }
 
 JSObject *
 js_CreateThisForFunction(JSContext *cx, JSObject *callee)
 {
     Value protov;
     if (!callee->getProperty(cx,
@@ -2934,17 +2976,17 @@ js_CreateThisFromTrace(JSContext *cx, Cl
              */
         }
     }
 
     /*
      * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
      * from easily or unconditionally calling NewNativeClassInstance here.
      */
-    gc::FinalizeKind kind = NewObjectGCKind(cx, clasp);
+    gc::FinalizeKind kind = GuessObjectGCKindByFlags(cx, ctor, proto, &js_ObjectClass);
     return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, kind);
 }
 
 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0,
                      nanojit::ACCSET_STORE_ANY)
 
 #else  /* !JS_TRACER */
 
@@ -3966,16 +4008,27 @@ JSObject::growSlots(JSContext *cx, size_
         actualCapacity = SLOT_CAPACITY_MIN;
 
     /* Don't let nslots get close to wrapping around uint32. */
     if (actualCapacity >= NSLOTS_LIMIT) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
 
+    /* This is a heuristic to make later constructor invocations use a larger size. */
+    if (clasp == &js_ObjectClass && proto) {
+        uint32 stored;
+        if (proto->hasStoredGCKind())
+            stored = GetGCKindSlots(gc::FinalizeKind(proto->getStoredGCKind()));
+        else
+            stored = actualCapacity;
+        uint32 slots = Max(actualCapacity, stored);
+        proto->setStoredGCKind(GetGCObjectKind(slots));
+    }
+
     /* If nothing was allocated yet, treat it as initial allocation. */
     if (!hasSlotsArray())
         return allocSlots(cx, actualCapacity);
 
     Value *tmpslots = (Value*) cx->realloc(slots, oldcap * sizeof(Value), actualCapacity * sizeof(Value));
     if (!tmpslots)
         return false;    /* Leave dslots as its old size. */
     slots = tmpslots;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -344,19 +344,32 @@ struct JSObject : js::gc::Cell {
         GENERIC                   =  0x10,
         METHOD_BARRIER            =  0x20,
         INDEXED                   =  0x40,
         OWN_SHAPE                 =  0x80,
         BOUND_FUNCTION            = 0x100,
         HAS_EQUALITY              = 0x200,
         METHOD_THRASH_COUNT_MASK  = 0xc00,
         METHOD_THRASH_COUNT_SHIFT =    10,
-        METHOD_THRASH_COUNT_MAX   = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT
+        METHOD_THRASH_COUNT_MAX   = METHOD_THRASH_COUNT_MASK >> METHOD_THRASH_COUNT_SHIFT,
+
+        GCKIND_BIT0     = 0x20000000,
+        GCKIND_BIT1     = 0x40000000,
+        GCKIND_BIT2     = 0x80000000
     };
 
+    const static int32 FLAGS_GCKIND_SHIFT = 29;
+    const static uint32 FLAGS_GCKIND_MASK = 0xe0000000;
+    const static uint32 FLAGS_GCKIND_INITIAL = FLAGS_GCKIND_MASK;
+
+    const static uint32 INITIAL_FLAGS = FLAGS_GCKIND_INITIAL;
+    
+    JS_STATIC_ASSERT(GCKIND_BIT0 == 1 << FLAGS_GCKIND_SHIFT);
+    JS_STATIC_ASSERT(FLAGS_GCKIND_MASK == (GCKIND_BIT0 | GCKIND_BIT1 | GCKIND_BIT2));
+
     /*
      * Impose a sane upper bound, originally checked only for dense arrays, on
      * number of slots in an object.
      */
     enum {
         NSLOTS_BITS     = 29,
         NSLOTS_LIMIT    = JS_BIT(NSLOTS_BITS)
     };
@@ -449,16 +462,23 @@ struct JSObject : js::gc::Cell {
     bool hasSpecialEquality() const { return !!(flags & HAS_EQUALITY); }
     void assertSpecialEqualitySynced() const {
         JS_ASSERT(!!clasp->ext.equality == hasSpecialEquality());
     }
 
     /* Sets an object's HAS_EQUALITY flag based on its clasp. */
     inline void syncSpecialEquality();
 
+    bool hasStoredGCKind() { return (flags & FLAGS_GCKIND_MASK) != FLAGS_GCKIND_INITIAL; }
+    unsigned /* gc::FinalizeKind */ getStoredGCKind() { return flags >> FLAGS_GCKIND_SHIFT; }
+    void setStoredGCKind(unsigned /* gc::FinalizeKind */ kind) {
+        flags = (flags & ~FLAGS_GCKIND_MASK) | (kind << FLAGS_GCKIND_SHIFT);
+        JS_ASSERT(getStoredGCKind() == kind);
+    }
+
   private:
     void generateOwnShape(JSContext *cx);
 
     void setOwnShape(uint32 s)  { flags |= OWN_SHAPE; objShape = s; }
     void clearOwnShape()        { flags &= ~OWN_SHAPE; objShape = map->shape; }
 
   public:
     inline bool nativeEmpty() const;
@@ -578,17 +598,17 @@ struct JSObject : js::gc::Cell {
 
     inline js::Value* fixedSlots() const;
     inline size_t numFixedSlots() const;
 
     static inline size_t getFixedSlotOffset(size_t slot);
 
   public:
     /* Minimum size for dynamically allocated slots. */
-    static const uint32 SLOT_CAPACITY_MIN = 8;
+    static const uint32 SLOT_CAPACITY_MIN = 2;
 
     bool allocSlots(JSContext *cx, size_t nslots);
     bool growSlots(JSContext *cx, size_t nslots);
     void shrinkSlots(JSContext *cx, size_t nslots);
 
     bool ensureSlots(JSContext *cx, size_t nslots) {
         if (numSlots() < nslots)
             return growSlots(cx, nslots);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -629,17 +629,17 @@ JSObject::setWithThis(JSObject *thisp)
     getSlotRef(JSSLOT_WITH_THIS).setObject(*thisp);
 }
 
 inline void
 JSObject::init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent,
                void *priv, bool useHoles)
 {
     clasp = aclasp;
-    flags = 0;
+    flags = INITIAL_FLAGS;
 
 #ifdef DEBUG
     /*
      * NB: objShape must not be set here; rather, the caller must call setMap
      * or setSharedNonNativeMap after calling init. To defend this requirement
      * we set map to null in DEBUG builds, and set objShape to a value we then
      * assert obj->shape() never returns.
      */