Bug 697646 - Don't create tiny property tables. r=bhackett.

This commit is contained in:
Nicholas Nethercote 2011-10-27 17:58:44 -07:00
Родитель 0b7b324d43
Коммит 4c0b47bc7e
1 изменённых файлов: 35 добавлений и 8 удалений

Просмотреть файл

@ -207,6 +207,8 @@
* were non-null and minimal-length. Until a scope is searched * were non-null and minimal-length. Until a scope is searched
* MAX_LINEAR_SEARCHES times, we use linear search from obj->lastProp to find a * MAX_LINEAR_SEARCHES times, we use linear search from obj->lastProp to find a
* given id, and save on the time and space overhead of creating a hash table. * given id, and save on the time and space overhead of creating a hash table.
* Also, we don't create tables for property tree Shapes that have shape
* lineages smaller than MIN_ENTRIES.
*/ */
#define SHAPE_INVALID_SLOT 0xffffffff #define SHAPE_INVALID_SLOT 0xffffffff
@ -215,12 +217,11 @@ namespace js {
/* /*
* Shapes use multiplicative hashing, _a la_ jsdhash.[ch], but specialized to * Shapes use multiplicative hashing, _a la_ jsdhash.[ch], but specialized to
* minimize footprint. But if a Shape lineage has been searched fewer than * minimize footprint.
* MAX_LINEAR_SEARCHES times, we use linear search and avoid allocating
* scope->table.
*/ */
struct PropertyTable { struct PropertyTable {
static const uint32 MAX_LINEAR_SEARCHES = 7; static const uint32 MAX_LINEAR_SEARCHES = 7;
static const uint32 MIN_ENTRIES = 7;
static const uint32 MIN_SIZE_LOG2 = 4; static const uint32 MIN_SIZE_LOG2 = 4;
static const uint32 MIN_SIZE = JS_BIT(MIN_SIZE_LOG2); static const uint32 MIN_SIZE = JS_BIT(MIN_SIZE_LOG2);
@ -625,6 +626,18 @@ struct Shape : public js::gc::Cell
return count; return count;
} }
bool isBigEnoughForAPropertyTable() const {
JS_ASSERT(!hasTable());
const js::Shape *shape = this;
uint32 count = 0;
for (js::Shape::Range r = shape->all(); !r.empty(); r.popFront()) {
++count;
if (count >= PropertyTable::MIN_ENTRIES)
return true;
}
return false;
}
#ifdef DEBUG #ifdef DEBUG
void dump(JSContext *cx, FILE *fp) const; void dump(JSContext *cx, FILE *fp) const;
void dumpSubtree(JSContext *cx, int level, FILE *fp) const; void dumpSubtree(JSContext *cx, int level, FILE *fp) const;
@ -704,6 +717,17 @@ js_GenerateShape(JSContext *cx);
namespace js { namespace js {
/*
* The search succeeds if it finds a Shape with the given id. There are two
* success cases:
* - If the Shape is the last in its shape lineage, we return |startp|, which
* is &obj->lastProp or something similar.
* - Otherwise, we return &shape->parent, where |shape| is the successor to the
* found Shape.
*
* There is one failure case: we return &emptyShape->parent, where
* |emptyShape| is the EmptyShape at the start of the shape lineage.
*/
JS_ALWAYS_INLINE js::Shape ** JS_ALWAYS_INLINE js::Shape **
Shape::search(JSContext *cx, js::Shape **startp, jsid id, bool adding) Shape::search(JSContext *cx, js::Shape **startp, jsid id, bool adding)
{ {
@ -712,9 +736,12 @@ Shape::search(JSContext *cx, js::Shape **startp, jsid id, bool adding)
return start->getTable()->search(id, adding); return start->getTable()->search(id, adding);
if (start->numLinearSearches == PropertyTable::MAX_LINEAR_SEARCHES) { if (start->numLinearSearches == PropertyTable::MAX_LINEAR_SEARCHES) {
if (start->hashify(cx)) if (start->isBigEnoughForAPropertyTable() && start->hashify(cx))
return start->getTable()->search(id, adding); return start->getTable()->search(id, adding);
/* OOM! Don't increment numLinearSearches, to keep hasTable() false. */ /*
* No table built -- there weren't enough entries, or OOM occurred.
* Don't increment numLinearSearches, to keep hasTable() false.
*/
JS_ASSERT(!start->hasTable()); JS_ASSERT(!start->hasTable());
} else { } else {
JS_ASSERT(start->numLinearSearches < PropertyTable::MAX_LINEAR_SEARCHES); JS_ASSERT(start->numLinearSearches < PropertyTable::MAX_LINEAR_SEARCHES);
@ -725,9 +752,9 @@ Shape::search(JSContext *cx, js::Shape **startp, jsid id, bool adding)
* Not enough searches done so far to justify hashing: search linearly * Not enough searches done so far to justify hashing: search linearly
* from *startp. * from *startp.
* *
* We don't use a Range here, or stop at null parent (the empty shape * We don't use a Range here, or stop at null parent (the empty shape at
* at the end), to avoid an extra load per iteration just to save a * the end). This avoids an extra load per iteration at the cost (if the
* load and id test at the end (when missing). * search fails) of an extra load and id test at the end.
*/ */
js::Shape **spp; js::Shape **spp;
for (spp = startp; js::Shape *shape = *spp; spp = &shape->parent) { for (spp = startp; js::Shape *shape = *spp; spp = &shape->parent) {