Fix O(n^2) complexity in {Get,Inset}PropertyTreeChild (335700, r=mrbkap).

git-svn-id: svn://10.0.0.236/trunk@218379 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
brendan%mozilla.org
2007-01-15 05:40:45 +00:00
parent 8bfeddf98a
commit c69ac01ef4

View File

@@ -505,11 +505,13 @@ NewScopeProperty(JSRuntime *rt)
#define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \
((jsuword)(chunk) | CHUNKY_KIDS_TAG))
#define MAX_KIDS_PER_CHUNK 10
#define CHUNK_HASH_THRESHOLD 30
typedef struct PropTreeKidsChunk PropTreeKidsChunk;
struct PropTreeKidsChunk {
JSScopeProperty *kids[MAX_KIDS_PER_CHUNK];
JSDHashTable *table;
PropTreeKidsChunk *next;
};
@@ -530,6 +532,8 @@ static void
DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk)
{
JS_RUNTIME_UNMETER(rt, propTreeKidsChunks);
if (chunk->table)
JS_DHashTableDestroy(chunk->table);
free(chunk);
}
@@ -538,6 +542,7 @@ static JSBool
InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
JSScopeProperty *child, PropTreeKidsChunk *sweptChunk)
{
JSDHashTable *table;
JSPropertyTreeEntry *entry;
JSScopeProperty **childp, *kids, *sprop;
PropTreeKidsChunk *chunk, **chunkp;
@@ -546,8 +551,9 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
JS_ASSERT(!parent || child->parent != parent);
if (!parent) {
table = &rt->propertyTreeHash;
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD);
JS_DHashTableOperate(table, child, JS_DHASH_ADD);
if (!entry)
return JS_FALSE;
childp = &entry->child;
@@ -580,6 +586,28 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
if (kids) {
if (KIDS_IS_CHUNKY(kids)) {
chunk = KIDS_TO_CHUNK(kids);
table = chunk->table;
if (table) {
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(table, child, JS_DHASH_ADD);
if (!entry)
return JS_FALSE;
if (!entry->child) {
entry->child = child;
while (chunk->next)
chunk = chunk->next;
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
childp = &chunk->kids[i];
sprop = *childp;
if (!sprop)
goto insert;
}
chunkp = &chunk->next;
goto new_chunk;
}
}
do {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
childp = &chunk->kids[i];
@@ -602,6 +630,7 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
chunkp = &chunk->next;
} while ((chunk = *chunkp) != NULL);
new_chunk:
if (sweptChunk) {
chunk = sweptChunk;
} else {
@@ -647,11 +676,14 @@ InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
static PropTreeKidsChunk *
RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child)
{
JSPropertyTreeEntry *entry;
PropTreeKidsChunk *freeChunk;
JSScopeProperty *parent, *kids, *kid;
JSDHashTable *table;
PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk;
uintN i, j;
JSPropertyTreeEntry *entry;
freeChunk = NULL;
parent = child->parent;
if (!parent) {
/*
@@ -659,16 +691,13 @@ RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child)
* matches a root child in the table that has compatible members. See
* the "Duplicate child" comments in InsertPropertyTreeChild, above.
*/
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_LOOKUP);
if (entry->child == child)
JS_DHashTableRawRemove(&rt->propertyTreeHash, &entry->hdr);
table = &rt->propertyTreeHash;
} else {
kids = parent->kids;
if (KIDS_IS_CHUNKY(kids)) {
list = chunk = KIDS_TO_CHUNK(kids);
chunkp = &list;
table = chunk->table;
do {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
@@ -695,21 +724,58 @@ RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child)
*chunkp = NULL;
if (!list)
parent->kids = NULL;
return lastChunk;
freeChunk = lastChunk;
goto out;
}
return NULL;
goto out;
}
}
chunkp = &chunk->next;
} while ((chunk = *chunkp) != NULL);
} else {
table = NULL;
kid = kids;
if (kid == child)
parent->kids = NULL;
}
}
return NULL;
out:
if (table) {
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(table, child, JS_DHASH_LOOKUP);
if (entry->child == child)
JS_DHashTableRawRemove(table, &entry->hdr);
}
return freeChunk;
}
static JSDHashTable *
HashChunks(PropTreeKidsChunk *chunk, uintN n)
{
JSDHashTable *table;
uintN i;
JSScopeProperty *sprop;
JSPropertyTreeEntry *entry;
table = JS_NewDHashTable(&PropertyTreeHashOps, NULL,
sizeof(JSPropertyTreeEntry),
JS_DHASH_DEFAULT_CAPACITY(n + 1));
if (!table)
return NULL;
do {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
sprop = chunk->kids[i];
if (!sprop)
break;
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(table, sprop, JS_DHASH_ADD);
entry->child = sprop;
}
} while ((chunk = chunk->next) != NULL);
return table;
}
/*
@@ -722,17 +788,19 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,
JSScopeProperty *child)
{
JSRuntime *rt;
JSDHashTable *table;
JSPropertyTreeEntry *entry;
JSScopeProperty *sprop;
PropTreeKidsChunk *chunk;
uintN i;
uintN i, n;
rt = cx->runtime;
if (!parent) {
JS_LOCK_RUNTIME(rt);
table = &rt->propertyTreeHash;
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD);
JS_DHashTableOperate(table, child, JS_DHASH_ADD);
if (!entry)
goto out_of_memory;
@@ -748,24 +816,57 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,
* has extremely low fan-out below its root in popular embeddings with
* real-world workloads.
*
* If workload changes so as to increase fan-out significantly below
* the property tree root, we'll want to add another tag bit stored in
* parent->kids that indicates a JSDHashTable pointer.
* Patterns such as defining closures that capture a constructor's
* environment as getters or setters on the new object that is passed
* in as |this| can significantly increase fan-out below the property
* tree root -- see bug 335700 for details.
*/
entry = NULL;
sprop = parent->kids;
if (sprop) {
if (KIDS_IS_CHUNKY(sprop)) {
chunk = KIDS_TO_CHUNK(sprop);
table = chunk->table;
if (table) {
JS_LOCK_RUNTIME(rt);
entry = (JSPropertyTreeEntry *)
JS_DHashTableOperate(table, child, JS_DHASH_LOOKUP);
sprop = entry->child;
if (sprop) {
JS_UNLOCK_RUNTIME(rt);
return sprop;
}
goto locked_not_found;
}
n = 0;
do {
for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) {
sprop = chunk->kids[i];
if (!sprop)
if (!sprop) {
n += i;
if (n >= CHUNK_HASH_THRESHOLD) {
chunk = KIDS_TO_CHUNK(parent->kids);
if (!chunk->table) {
table = HashChunks(chunk, n);
JS_LOCK_RUNTIME(rt);
if (!table)
goto out_of_memory;
if (chunk->table)
JS_DHashTableDestroy(table);
else
chunk->table = table;
goto locked_not_found;
}
}
goto not_found;
}
if (SPROP_MATCH(sprop, child))
return sprop;
}
n += MAX_KIDS_PER_CHUNK;
} while ((chunk = chunk->next) != NULL);
} else {
if (SPROP_MATCH(sprop, child))
@@ -777,6 +878,7 @@ GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent,
JS_LOCK_RUNTIME(rt);
}
locked_not_found:
sprop = NewScopeProperty(rt);
if (!sprop)
goto out_of_memory;
@@ -1654,7 +1756,8 @@ js_SweepScopeProperties(JSRuntime *rt)
if (kids) {
sprop->kids = NULL;
parent = sprop->parent;
/* Validate that grandparent has no kids or chunky kids. */
/* Assert that grandparent has no kids or chunky kids. */
JS_ASSERT(!parent || !parent->kids ||
KIDS_IS_CHUNKY(parent->kids));
if (KIDS_IS_CHUNKY(kids)) {