Bug 948445 - use different caching schemes for ProtoAndIfaceArray depending on the global kind; r=bz

This commit is contained in:
Nathan Froyd 2013-12-10 16:04:55 -05:00
parent dfd53166ec
commit bfa7382195
4 changed files with 184 additions and 22 deletions

View File

@ -1134,7 +1134,8 @@ ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper,
{
JSAutoCompartment ac(cx, global);
ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(global);
JSObject* protoOrIface = protoAndIfaceArray[protoAndIfaceArrayIndex];
JSObject* protoOrIface =
protoAndIfaceArray.EntrySlotIfExists(protoAndIfaceArrayIndex);
if (!protoOrIface) {
return false;
}

View File

@ -293,29 +293,190 @@ static_assert((size_t)constructors::id::_ID_Start ==
"Overlapping or discontiguous indexes.");
const size_t kProtoAndIfaceCacheCount = constructors::id::_ID_Count;
class ProtoAndIfaceArray : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount>
class ProtoAndIfaceArray
{
// The caching strategy we use depends on what sort of global we're dealing
// with. For a window-like global, we want everything to be as fast as
// possible, so we use a flat array, indexed by prototype/constructor ID.
// For everything else (e.g. globals for JSMs), space is more important than
// speed, so we use a two-level lookup table.
class ArrayCache : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount>
{
public:
JSObject* EntrySlotIfExists(size_t i) {
return (*this)[i];
}
JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
return (*this)[i];
}
JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
MOZ_ASSERT((*this)[i]);
return (*this)[i];
}
void Trace(JSTracer* aTracer) {
for (size_t i = 0; i < ArrayLength(*this); ++i) {
if ((*this)[i]) {
JS_CallHeapObjectTracer(aTracer, &(*this)[i], "protoAndIfaceArray[i]");
}
}
}
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
return aMallocSizeOf(this);
}
};
class PageTableCache
{
public:
PageTableCache() {
memset(&mPages, 0, sizeof(mPages));
}
~PageTableCache() {
for (size_t i = 0; i < ArrayLength(mPages); ++i) {
delete mPages[i];
}
}
JSObject* EntrySlotIfExists(size_t i) {
MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
size_t pageIndex = i / kPageSize;
size_t leafIndex = i % kPageSize;
Page* p = mPages[pageIndex];
if (!p) {
return nullptr;
}
return (*p)[leafIndex];
}
JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
size_t pageIndex = i / kPageSize;
size_t leafIndex = i % kPageSize;
Page* p = mPages[pageIndex];
if (!p) {
p = new Page;
mPages[pageIndex] = p;
}
return (*p)[leafIndex];
}
JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
size_t pageIndex = i / kPageSize;
size_t leafIndex = i % kPageSize;
Page* p = mPages[pageIndex];
MOZ_ASSERT(p);
return (*p)[leafIndex];
}
void Trace(JSTracer* trc) {
for (size_t i = 0; i < ArrayLength(mPages); ++i) {
Page* p = mPages[i];
if (p) {
for (size_t j = 0; j < ArrayLength(*p); ++j) {
if ((*p)[j]) {
JS_CallHeapObjectTracer(trc, &(*p)[j], "protoAndIfaceArray[i]");
}
}
}
}
}
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
size_t n = aMallocSizeOf(this);
for (size_t i = 0; i < ArrayLength(mPages); ++i) {
n += aMallocSizeOf(mPages[i]);
}
return n;
}
private:
static const size_t kPageSize = 16;
typedef Array<JS::Heap<JSObject*>, kPageSize> Page;
static const size_t kNPages = kProtoAndIfaceCacheCount / kPageSize +
size_t(bool(kProtoAndIfaceCacheCount % kPageSize));
Array<Page*, kNPages> mPages;
};
public:
ProtoAndIfaceArray() {
enum Kind {
WindowLike,
NonWindowLike
};
ProtoAndIfaceArray(Kind aKind) : mKind(aKind) {
MOZ_COUNT_CTOR(ProtoAndIfaceArray);
if (aKind == WindowLike) {
mArrayCache = new ArrayCache();
} else {
mPageTableCache = new PageTableCache();
}
}
~ProtoAndIfaceArray() {
if (mKind == WindowLike) {
delete mArrayCache;
} else {
delete mPageTableCache;
}
MOZ_COUNT_DTOR(ProtoAndIfaceArray);
}
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
return aMallocSizeOf(this);
#define FORWARD_OPERATION(opName, args) \
do { \
if (mKind == WindowLike) { \
return mArrayCache->opName args; \
} else { \
return mPageTableCache->opName args; \
} \
} while(0)
JSObject* EntrySlotIfExists(size_t i) {
FORWARD_OPERATION(EntrySlotIfExists, (i));
}
JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
FORWARD_OPERATION(EntrySlotOrCreate, (i));
}
JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
FORWARD_OPERATION(EntrySlotMustExist, (i));
}
void Trace(JSTracer *aTracer) {
FORWARD_OPERATION(Trace, (aTracer));
}
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
size_t n = aMallocSizeOf(this);
n += (mKind == WindowLike
? mArrayCache->SizeOfIncludingThis(aMallocSizeOf)
: mPageTableCache->SizeOfIncludingThis(aMallocSizeOf));
return n;
}
#undef FORWARD_OPERATION
private:
union {
ArrayCache *mArrayCache;
PageTableCache *mPageTableCache;
};
Kind mKind;
};
inline void
AllocateProtoAndIfaceCache(JSObject* obj)
AllocateProtoAndIfaceCache(JSObject* obj, ProtoAndIfaceArray::Kind aKind)
{
MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
ProtoAndIfaceArray* protoAndIfaceArray = new ProtoAndIfaceArray();
ProtoAndIfaceArray* protoAndIfaceArray = new ProtoAndIfaceArray(aKind);
js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
JS::PrivateValue(protoAndIfaceArray));
@ -328,12 +489,8 @@ TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj)
if (!HasProtoAndIfaceArray(obj))
return;
ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(obj);
for (size_t i = 0; i < ArrayLength(protoAndIfaceArray); ++i) {
if (protoAndIfaceArray[i]) {
JS_CallHeapObjectTracer(trc, &protoAndIfaceArray[i], "protoAndIfaceArray[i]");
}
}
ProtoAndIfaceArray* protoAndIfaceArray = GetProtoAndIfaceArray(obj);
protoAndIfaceArray->Trace(trc);
}
inline void
@ -2124,7 +2281,7 @@ inline JSObject*
GetUnforgeableHolder(JSObject* aGlobal, prototypes::ID aId)
{
ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(aGlobal);
JSObject* interfaceProto = protoAndIfaceArray[aId];
JSObject* interfaceProto = protoAndIfaceArray.EntrySlotMustExist(aId);
return &js::GetReservedSlot(interfaceProto,
DOM_INTERFACE_PROTO_SLOTS_BASE).toObject();
}
@ -2382,7 +2539,7 @@ CreateGlobal(JSContext* aCx, T* aObject, nsWrapperCache* aCache,
JSAutoCompartment ac(aCx, global);
dom::AllocateProtoAndIfaceCache(global);
dom::AllocateProtoAndIfaceCache(global, ProtoAndIfaceArray::WindowLike);
js::SetReservedSlot(global, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
NS_ADDREF(aObject);

View File

@ -2074,13 +2074,13 @@ if (!unforgeableHolder) {
if needInterfacePrototypeObject:
protoClass = "&PrototypeClass.mBase"
protoCache = "&aProtoAndIfaceArray[prototypes::id::%s]" % self.descriptor.name
protoCache = "&aProtoAndIfaceArray.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name
else:
protoClass = "nullptr"
protoCache = "nullptr"
if needInterfaceObject:
interfaceClass = "&InterfaceObjectClass.mBase"
interfaceCache = "&aProtoAndIfaceArray[constructors::id::%s]" % self.descriptor.name
interfaceCache = "&aProtoAndIfaceArray.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
else:
# We don't have slots to store the named constructors.
assert len(self.descriptor.interface.namedConstructors) == 0
@ -2120,7 +2120,7 @@ if (!unforgeableHolder) {
if UseHolderForUnforgeable(self.descriptor):
assert needInterfacePrototypeObject
setUnforgeableHolder = CGGeneric(
"JSObject* proto = aProtoAndIfaceArray[prototypes::id::%s];\n"
"JSObject* proto = aProtoAndIfaceArray.EntrySlotOrCreate(prototypes::id::%s);\n"
"if (proto) {\n"
" js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE,\n"
" JS::ObjectValue(*unforgeableHolder));\n"
@ -2153,7 +2153,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
}
/* Check to see whether the interface objects are already installed */
ProtoAndIfaceArray& protoAndIfaceArray = *GetProtoAndIfaceArray(aGlobal);
if (!protoAndIfaceArray[%s]) {
if (!protoAndIfaceArray.EntrySlotIfExists(%s)) {
CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceArray, aDefineOnGlobal);
}
@ -2164,8 +2164,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
* traced by TraceProtoAndIfaceCache() and its contents are never
* changed after they have been set.
*/
return JS::Handle<JSObject*>::fromMarkedLocation(protoAndIfaceArray[%s].address());""" %
(self.id, self.id))
return JS::Handle<JSObject*>::fromMarkedLocation(protoAndIfaceArray.EntrySlotMustExist(%s).address());""" % (self.id, self.id))
class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
"""

View File

@ -389,7 +389,12 @@ CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal,
#endif
if (clasp->flags & JSCLASS_DOM_GLOBAL) {
AllocateProtoAndIfaceCache(global);
const char* className = clasp->name;
AllocateProtoAndIfaceCache(global,
(strcmp(className, "Window") == 0 ||
strcmp(className, "ChromeWindow") == 0)
? ProtoAndIfaceArray::WindowLike
: ProtoAndIfaceArray::NonWindowLike);
}
return global;