From 040a610072e772fdb38476fa70b6a9f1672aae68 Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Tue, 16 Oct 2001 05:40:27 +0000 Subject: [PATCH] Add SetAlphaBounds, TABLE_SIZE, and MIN_ALPHA APIs (103990, r=dbaron, sr=waterson). git-svn-id: svn://10.0.0.236/trunk@105470 18797224-902f-48f8-a5cc-f745e15eee43 --- .../xul/templates/src/nsTemplateMatchSet.cpp | 6 +- .../commandhandler/src/nsCommandParams.cpp | 8 +- mozilla/js/src/Makefile.in | 4 + mozilla/js/src/jsdhash.c | 99 +++++++++++++++---- mozilla/js/src/jsdhash.h | 40 +++++++- mozilla/js/src/jsobj.c | 12 +-- mozilla/xpcom/ds/pldhash.c | 99 +++++++++++++++---- mozilla/xpcom/ds/pldhash.h | 40 +++++++- mozilla/xpcom/io/nsFastLoadFile.cpp | 2 +- mozilla/xpcom/io/nsFastLoadService.cpp | 2 +- 10 files changed, 253 insertions(+), 59 deletions(-) diff --git a/mozilla/content/xul/templates/src/nsTemplateMatchSet.cpp b/mozilla/content/xul/templates/src/nsTemplateMatchSet.cpp index e11f8999aa7..d368cbdf0b3 100644 --- a/mozilla/content/xul/templates/src/nsTemplateMatchSet.cpp +++ b/mozilla/content/xul/templates/src/nsTemplateMatchSet.cpp @@ -298,7 +298,7 @@ nsTemplateMatchRefSet::First() const return ConstIterator(this, (nsTemplateMatch**) mStorageElements.mInlineMatches.mEntries); Entry* entry = NS_REINTERPRET_CAST(Entry*, mStorageElements.mTable.entryStore); - Entry* limit = entry + PR_BIT(mStorageElements.mTable.sizeLog2); + Entry* limit = entry + PL_DHASH_TABLE_SIZE(&mStorageElements.mTable); for ( ; entry < limit; ++entry) { if (ENTRY_IS_LIVE(entry)) break; @@ -319,7 +319,7 @@ nsTemplateMatchRefSet::Last() const } Entry* limit = NS_REINTERPRET_CAST(Entry*, mStorageElements.mTable.entryStore); - limit += PR_BIT(mStorageElements.mTable.sizeLog2); + limit += PL_DHASH_TABLE_SIZE(&mStorageElements.mTable); return ConstIterator(this, limit); } @@ -331,7 +331,7 @@ nsTemplateMatchRefSet::ConstIterator::Next() else { const PLDHashTable& table = mSet->mStorageElements.mTable; Entry* limit = NS_REINTERPRET_CAST(Entry*, table.entryStore); - limit += PR_BIT(table.sizeLog2); + limit += PL_DHASH_TABLE_SIZE(&table); while (++mTableEntry < limit) { if (ENTRY_IS_LIVE(mTableEntry)) break; diff --git a/mozilla/embedding/components/commandhandler/src/nsCommandParams.cpp b/mozilla/embedding/components/commandhandler/src/nsCommandParams.cpp index 2b735956790..b23f07f9c23 100644 --- a/mozilla/embedding/components/commandhandler/src/nsCommandParams.cpp +++ b/mozilla/embedding/components/commandhandler/src/nsCommandParams.cpp @@ -43,10 +43,6 @@ #include "nsCommandParams.h" -// will come from pldhash.h soon -#define PL_DHASH_ENTRY_IS_LIVE(entry) ((entry)->keyHash >= 2) - - PLDHashTableOps nsCommandParams::sHashOps = { PL_DHashAllocTable, @@ -277,7 +273,7 @@ nsCommandParams::HashEntry* nsCommandParams::GetIndexedEntry(PRInt32 index) { HashEntry* entry = NS_REINTERPRET_CAST(HashEntry*, mValuesHash.entryStore); - HashEntry* limit = entry + PR_BIT(mValuesHash.sizeLog2); + HashEntry* limit = entry + PL_DHASH_TABLE_SIZE(&mValuesHash); PRUint32 entryCount = 0; do @@ -299,7 +295,7 @@ PRUint32 nsCommandParams::GetNumEntries() { HashEntry* entry = NS_REINTERPRET_CAST(HashEntry*, mValuesHash.entryStore); - HashEntry* limit = entry + PR_BIT(mValuesHash.sizeLog2); + HashEntry* limit = entry + PL_DHASH_TABLE_SIZE(&mValuesHash); PRUint32 entryCount = 0; do diff --git a/mozilla/js/src/Makefile.in b/mozilla/js/src/Makefile.in index ab8104c9b30..4c2b2928531 100644 --- a/mozilla/js/src/Makefile.in +++ b/mozilla/js/src/Makefile.in @@ -43,6 +43,10 @@ MODULE = js LIBRARY_NAME = mozjs EXPORT_LIBRARY = 1 +ifdef NS_TRACE_MALLOC +REQUIRES = xpcom +endif + CSRCS = \ jsapi.c \ jsarena.c \ diff --git a/mozilla/js/src/jsdhash.c b/mozilla/js/src/jsdhash.c index 87f3b5b034c..d82c8a830cc 100644 --- a/mozilla/js/src/jsdhash.c +++ b/mozilla/js/src/jsdhash.c @@ -188,8 +188,11 @@ JS_DHashTableInit(JSDHashTable *table, JSDHashTableOps *ops, void *data, capacity = JS_DHASH_MIN_SIZE; log2 = JS_CeilingLog2(capacity); capacity = JS_BIT(log2); + if (capacity >= JS_DHASH_MAX_SIZE) + return JS_FALSE; table->hashShift = JS_DHASH_BITS - log2; - table->sizeLog2 = log2; + table->maxAlphaFrac = 0xC0; /* 12/16 or .75 */ + table->minAlphaFrac = 0x40; /* 1/4 or .25 */ table->entrySize = entrySize; table->entryCount = table->removedCount = 0; table->generation = 0; @@ -203,6 +206,54 @@ JS_DHashTableInit(JSDHashTable *table, JSDHashTableOps *ops, void *data, return JS_TRUE; } +/* + * Compute max and min load numbers (entry counts) from table params. + */ +#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8) +#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8) + +JS_PUBLIC_API(void) +JS_DHashTableSetAlphaBounds(JSDHashTable *table, + float maxAlpha, + float minAlpha) +{ + uint32 size; + + /* + * Reject obviously insane bounds, rather than trying to guess what the + * buggy caller intended. + */ + JS_ASSERT(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha); + if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0) + return; + + /* + * Ensure that at least one entry will always be free. If maxAlpha at + * minimum size leaves no entries free, reduce maxAlpha based on minimum + * size and the precision limit of maxAlphaFrac's fixed point format. + */ + JS_ASSERT(JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) >= 1); + if (JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) < 1) { + maxAlpha = (float) + (JS_DHASH_MIN_SIZE - JS_MAX(JS_DHASH_MIN_SIZE / 256, 1)) + / JS_DHASH_MIN_SIZE; + } + + /* + * Ensure that minAlpha is strictly less than half maxAlpha. Take care + * not to truncate an entry's worth of alpha when storing in minAlphaFrac + * (8-bit fixed point format). + */ + JS_ASSERT(minAlpha < maxAlpha / 2); + if (minAlpha >= maxAlpha / 2) { + size = JS_DHASH_TABLE_SIZE(table); + minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size); + } + + table->maxAlphaFrac = (uint8)(maxAlpha * 256); + table->minAlphaFrac = (uint8)(minAlpha * 256); +} + /* * Double hashing needs the second hash code to be relatively prime to table * size, so we simply make hash2 odd. @@ -262,7 +313,7 @@ JS_DHashTableFinish(JSDHashTable *table) /* Clear any remaining live entries. */ entryAddr = table->entryStore; entrySize = table->entrySize; - entryLimit = entryAddr + JS_BIT(table->sizeLog2) * entrySize; + entryLimit = entryAddr + JS_DHASH_TABLE_SIZE(table) * entrySize; while (entryAddr < entryLimit) { entry = (JSDHashEntryHdr *)entryAddr; if (ENTRY_IS_LIVE(entry)) { @@ -308,7 +359,7 @@ SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash, } /* Collision: double hash. */ - sizeLog2 = table->sizeLog2; + sizeLog2 = JS_DHASH_BITS - table->hashShift; hash2 = HASH2(keyHash, sizeLog2, hashShift); sizeMask = JS_BITMASK(sizeLog2); @@ -373,10 +424,12 @@ ChangeTable(JSDHashTable *table, int deltaLog2) JSDHashMoveEntry moveEntry; /* Look, but don't touch, until we succeed in getting new entry store. */ - oldLog2 = table->sizeLog2; + oldLog2 = JS_DHASH_BITS - table->hashShift; newLog2 = oldLog2 + deltaLog2; oldCapacity = JS_BIT(oldLog2); newCapacity = JS_BIT(newLog2); + if (newCapacity >= JS_DHASH_MAX_SIZE) + return JS_FALSE; entrySize = table->entrySize; nbytes = newCapacity * entrySize; @@ -386,7 +439,6 @@ ChangeTable(JSDHashTable *table, int deltaLog2) /* We can't fail from here on, so update table parameters. */ table->hashShift = JS_DHASH_BITS - newLog2; - table->sizeLog2 = newLog2; table->removedCount = 0; table->generation++; @@ -442,8 +494,9 @@ JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) * in the table, we may grow once more than necessary, but only if we * are on the edge of being overloaded. */ - size = JS_BIT(table->sizeLog2); - if (table->entryCount + table->removedCount >= size - (size >> 2)) { + size = JS_DHASH_TABLE_SIZE(table); + if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) { + /* Compress if a quarter or more of all entries are removed. */ if (table->removedCount >= size >> 2) { METER(table->stats.compresses++); deltaLog2 = 0; @@ -492,8 +545,9 @@ JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) JS_DHashTableRawRemove(table, entry); /* Shrink if alpha is <= .25 and table isn't too small already. */ - size = JS_BIT(table->sizeLog2); - if (size > JS_DHASH_MIN_SIZE && table->entryCount <= size >> 2) { + size = JS_DHASH_TABLE_SIZE(table); + if (size > JS_DHASH_MIN_SIZE && + table->entryCount <= MIN_LOAD(table, size)) { METER(table->stats.shrinks++); (void) ChangeTable(table, -1); } @@ -537,7 +591,7 @@ JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) entryAddr = table->entryStore; entrySize = table->entrySize; - capacity = JS_BIT(table->sizeLog2); + capacity = JS_DHASH_TABLE_SIZE(table); entryLimit = entryAddr + capacity * entrySize; i = 0; while (entryAddr < entryLimit) { @@ -554,14 +608,16 @@ JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) entryAddr += entrySize; } - /* Shrink or compress if enough entries were removed that alpha < .5. */ + /* Shrink or compress if a quarter or more of all entries are removed. */ if (table->removedCount >= capacity >> 2) { METER(table->stats.enumShrinks++); capacity = table->entryCount; capacity += capacity >> 1; if (capacity < JS_DHASH_MIN_SIZE) capacity = JS_DHASH_MIN_SIZE; - (void) ChangeTable(table, JS_CeilingLog2(capacity) - table->sizeLog2); + (void) ChangeTable(table, + JS_CeilingLog2(capacity) + - (JS_DHASH_BITS - table->hashShift)); } return i; } @@ -574,6 +630,7 @@ JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) { char *entryAddr; uint32 entrySize, entryCount; + int hashShift, sizeLog2; uint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount; JSDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2; double sqsum, mean, variance, sigma; @@ -581,8 +638,10 @@ JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) entryAddr = table->entryStore; entrySize = table->entrySize; - tableSize = JS_BIT(table->sizeLog2); - sizeMask = JS_BITMASK(table->sizeLog2); + hashShift = table->hashShift; + sizeLog2 = JS_DHASH_BITS - hashShift; + tableSize = JS_DHASH_TABLE_SIZE(table); + sizeMask = JS_BITMASK(sizeLog2); chainCount = maxChainLen = 0; hash2 = 0; sqsum = 0; @@ -592,7 +651,7 @@ JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) entryAddr += entrySize; if (!ENTRY_IS_LIVE(entry)) continue; - hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, table->hashShift); + hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift); saveHash1 = hash1; probe = ADDRESS_ENTRY(table, hash1); chainLen = 1; @@ -600,8 +659,8 @@ JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) /* Start of a (possibly unit-length) chain. */ chainCount++; } else { - hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, table->sizeLog2, - table->hashShift); + hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2, + hashShift); do { chainLen++; hash1 -= hash2; @@ -637,8 +696,10 @@ JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) fprintf(fp, " number of searches: %u\n", table->stats.searches); fprintf(fp, " number of hits: %u\n", table->stats.hits); fprintf(fp, " number of misses: %u\n", table->stats.misses); - fprintf(fp, " mean steps per search: %g\n", (double)table->stats.steps - / table->stats.searches); + fprintf(fp, " mean steps per search: %g\n", table->stats.searches ? + (double)table->stats.steps + / table->stats.searches : + 0.); fprintf(fp, " mean hash chain length: %g\n", mean); fprintf(fp, " standard deviation: %g\n", sigma); fprintf(fp, " maximum hash chain length: %u\n", maxChainLen); diff --git a/mozilla/js/src/jsdhash.h b/mozilla/js/src/jsdhash.h index b3371bea303..c18bcddc722 100644 --- a/mozilla/js/src/jsdhash.h +++ b/mozilla/js/src/jsdhash.h @@ -49,9 +49,15 @@ JS_BEGIN_EXTERN_C #define JS_DHASHMETER 1 #endif +/* Maximum table size, do not equal or exceed (see min&maxAlphaFrac, below). */ +#undef JS_DHASH_MAX_SIZE +#define JS_DHASH_MAX_SIZE JS_BIT(24) + /* Minimum table size, or gross entry count (net is at most .75 loaded). */ #ifndef JS_DHASH_MIN_SIZE #define JS_DHASH_MIN_SIZE 16 +#elif (JS_DHASH_MIN_SIZE & (JS_DHASH_MIN_SIZE - 1)) != 0 +#error "JS_DHASH_MIN_SIZE must be a power of two!" #endif /* @@ -153,7 +159,7 @@ struct JSDHashEntryHdr { * assuming esize is not too large (in which case, chaining should probably be * used for any alpha). For esize=2 and k=3, we want alpha >= .2; for esize=3 * and k=2, we want alpha >= .4. For k=4, esize could be 6, and alpha >= .5 - * would still obtain. + * would still obtain. See the JS_DHASH_MIN_ALPHA macro further below. * * The current implementation uses a constant .25 as alpha's lower bound when * deciding to shrink the table (while respecting JS_DHASH_MIN_SIZE). @@ -173,7 +179,8 @@ struct JSDHashTable { JSDHashTableOps *ops; /* virtual operations, see below */ void *data; /* ops- and instance-specific data */ int16 hashShift; /* multiplicative hash shift */ - int16 sizeLog2; /* log2(table size) */ + uint8 maxAlphaFrac; /* 8-bit fixed point max alpha */ + uint8 minAlphaFrac; /* 8-bit fixed point min alpha */ uint32 entrySize; /* number of bytes in an entry */ uint32 entryCount; /* number of entries in table */ uint32 removedCount; /* removed entry sentinels in table */ @@ -202,6 +209,13 @@ struct JSDHashTable { #endif }; +/* + * Size in entries (gross, not net of free and removed sentinels) for table. + * We store hashShift rather than sizeLog2 to optimize the collision-free case + * in SearchTable. + */ +#define JS_DHASH_TABLE_SIZE(table) JS_BIT(JS_DHASH_BITS - (table)->hashShift) + /* * Table space at entryStore is allocated and freed using these callbacks. * The allocator should return null on error only (not if called with nbytes @@ -392,6 +406,28 @@ extern JS_PUBLIC_API(JSBool) JS_DHashTableInit(JSDHashTable *table, JSDHashTableOps *ops, void *data, uint32 entrySize, uint32 capacity); +/* + * Set maximum and minimum alpha for table. The defaults are 0.75 and .25. + * maxAlpha must be in [0.5, 0.9375] for the default JS_DHASH_MIN_SIZE; or if + * MinSize=JS_DHASH_MIN_SIZE <= 256, in [0.5, (float)(MinSize-1)/MinSize]; or + * else in [0.5, 255.0/256]. minAlpha must be in [0, maxAlpha / 2), so that + * we don't shrink on next remove after growing a table upon adding an entry + * that brings entryCount past maxAlpha * tableSize. + */ +JS_PUBLIC_API(void) +JS_DHashTableSetAlphaBounds(JSDHashTable *table, + float maxAlpha, + float minAlpha); + +/* + * Call this macro with k, the number of pointer-sized words wasted per entry + * under chaining, to compute the minimum alpha at which double hashing still + * beats chaining. + */ +#define JS_DHASH_MIN_ALPHA(table, k) \ + ((float)((table)->entrySize / sizeof(void *) - 1) \ + / ((table)->entrySize / sizeof(void *) + (k))) + /* * Finalize table's data, free its entry storage using table->ops->freeTable, * and leave its members unchanged from their last live values (which leaves diff --git a/mozilla/js/src/jsobj.c b/mozilla/js/src/jsobj.c index dc95d8b86cf..e5e8ca418f4 100644 --- a/mozilla/js/src/jsobj.c +++ b/mozilla/js/src/jsobj.c @@ -2218,7 +2218,7 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, * entry, compressing or shrinking the table as needed. */ if (table->generation == generation && - table->removedCount < JS_BIT(table->sizeLog2) >> 2) { + table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2) { JS_DHashTableRawRemove(table, entry); } else { JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); @@ -3590,7 +3590,7 @@ js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) /* Routines to print out values during debugging. */ void printChar(jschar *cp) { - fprintf(stderr, "jschar* (0x%p) \"", cp); + fprintf(stderr, "jschar* (0x%p) \"", (void *)cp); while (*cp) fputc(*cp++, stderr); fputc('"', stderr); @@ -3599,7 +3599,7 @@ void printChar(jschar *cp) { void printString(JSString *str) { jsuint i; - fprintf(stderr, "string (0x%p) \"", str); + fprintf(stderr, "string (0x%p) \"", (void *)str); for (i=0; i < str->length; i++) fputc(str->chars[i], stderr); fputc('"', stderr); @@ -3613,14 +3613,14 @@ void printObj(JSContext *cx, JSObject *jsobj) { jsval val; JSClass *clasp; - fprintf(stderr, "object 0x%p\n", jsobj); + fprintf(stderr, "object 0x%p\n", (void *)jsobj); clasp = OBJ_GET_CLASS(cx, jsobj); - fprintf(stderr, "class 0x%p %s\n", clasp, clasp->name); + fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name); for (i=0; i < jsobj->map->nslots; i++) { fprintf(stderr, "slot %3d ", i); val = jsobj->slots[i]; if (JSVAL_IS_OBJECT(val)) - fprintf(stderr, "object 0x%p\n", JSVAL_TO_OBJECT(val)); + fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val)); else printVal(cx, val); } diff --git a/mozilla/xpcom/ds/pldhash.c b/mozilla/xpcom/ds/pldhash.c index a23cd2228d5..48eb5338899 100644 --- a/mozilla/xpcom/ds/pldhash.c +++ b/mozilla/xpcom/ds/pldhash.c @@ -189,8 +189,11 @@ PL_DHashTableInit(PLDHashTable *table, PLDHashTableOps *ops, void *data, capacity = PL_DHASH_MIN_SIZE; log2 = PR_CeilingLog2(capacity); capacity = PR_BIT(log2); + if (capacity >= PL_DHASH_MAX_SIZE) + return PR_FALSE; table->hashShift = PL_DHASH_BITS - log2; - table->sizeLog2 = log2; + table->maxAlphaFrac = 0xC0; /* 12/16 or .75 */ + table->minAlphaFrac = 0x40; /* 1/4 or .25 */ table->entrySize = entrySize; table->entryCount = table->removedCount = 0; table->generation = 0; @@ -204,6 +207,54 @@ PL_DHashTableInit(PLDHashTable *table, PLDHashTableOps *ops, void *data, return PR_TRUE; } +/* + * Compute max and min load numbers (entry counts) from table params. + */ +#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8) +#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8) + +PR_IMPLEMENT(void) +PL_DHashTableSetAlphaBounds(PLDHashTable *table, + float maxAlpha, + float minAlpha) +{ + PRUint32 size; + + /* + * Reject obviously insane bounds, rather than trying to guess what the + * buggy caller intended. + */ + PR_ASSERT(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha); + if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0) + return; + + /* + * Ensure that at least one entry will always be free. If maxAlpha at + * minimum size leaves no entries free, reduce maxAlpha based on minimum + * size and the precision limit of maxAlphaFrac's fixed point format. + */ + PR_ASSERT(PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) >= 1); + if (PL_DHASH_MIN_SIZE - (maxAlpha * PL_DHASH_MIN_SIZE) < 1) { + maxAlpha = (float) + (PL_DHASH_MIN_SIZE - PR_MAX(PL_DHASH_MIN_SIZE / 256, 1)) + / PL_DHASH_MIN_SIZE; + } + + /* + * Ensure that minAlpha is strictly less than half maxAlpha. Take care + * not to truncate an entry's worth of alpha when storing in minAlphaFrac + * (8-bit fixed point format). + */ + PR_ASSERT(minAlpha < maxAlpha / 2); + if (minAlpha >= maxAlpha / 2) { + size = PL_DHASH_TABLE_SIZE(table); + minAlpha = (size * maxAlpha - PR_MAX(size / 256, 1)) / (2 * size); + } + + table->maxAlphaFrac = (uint8)(maxAlpha * 256); + table->minAlphaFrac = (uint8)(minAlpha * 256); +} + /* * Double hashing needs the second hash code to be relatively prime to table * size, so we simply make hash2 odd. @@ -263,7 +314,7 @@ PL_DHashTableFinish(PLDHashTable *table) /* Clear any remaining live entries. */ entryAddr = table->entryStore; entrySize = table->entrySize; - entryLimit = entryAddr + PR_BIT(table->sizeLog2) * entrySize; + entryLimit = entryAddr + PL_DHASH_TABLE_SIZE(table) * entrySize; while (entryAddr < entryLimit) { entry = (PLDHashEntryHdr *)entryAddr; if (ENTRY_IS_LIVE(entry)) { @@ -309,7 +360,7 @@ SearchTable(PLDHashTable *table, const void *key, PLDHashNumber keyHash, } /* Collision: double hash. */ - sizeLog2 = table->sizeLog2; + sizeLog2 = PL_DHASH_BITS - table->hashShift; hash2 = HASH2(keyHash, sizeLog2, hashShift); sizeMask = PR_BITMASK(sizeLog2); @@ -374,10 +425,12 @@ ChangeTable(PLDHashTable *table, int deltaLog2) PLDHashMoveEntry moveEntry; /* Look, but don't touch, until we succeed in getting new entry store. */ - oldLog2 = table->sizeLog2; + oldLog2 = PL_DHASH_BITS - table->hashShift; newLog2 = oldLog2 + deltaLog2; oldCapacity = PR_BIT(oldLog2); newCapacity = PR_BIT(newLog2); + if (newCapacity >= PL_DHASH_MAX_SIZE) + return PR_FALSE; entrySize = table->entrySize; nbytes = newCapacity * entrySize; @@ -387,7 +440,6 @@ ChangeTable(PLDHashTable *table, int deltaLog2) /* We can't fail from here on, so update table parameters. */ table->hashShift = PL_DHASH_BITS - newLog2; - table->sizeLog2 = newLog2; table->removedCount = 0; table->generation++; @@ -443,8 +495,9 @@ PL_DHashTableOperate(PLDHashTable *table, const void *key, PLDHashOperator op) * in the table, we may grow once more than necessary, but only if we * are on the edge of being overloaded. */ - size = PR_BIT(table->sizeLog2); - if (table->entryCount + table->removedCount >= size - (size >> 2)) { + size = PL_DHASH_TABLE_SIZE(table); + if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) { + /* Compress if a quarter or more of all entries are removed. */ if (table->removedCount >= size >> 2) { METER(table->stats.compresses++); deltaLog2 = 0; @@ -493,8 +546,9 @@ PL_DHashTableOperate(PLDHashTable *table, const void *key, PLDHashOperator op) PL_DHashTableRawRemove(table, entry); /* Shrink if alpha is <= .25 and table isn't too small already. */ - size = PR_BIT(table->sizeLog2); - if (size > PL_DHASH_MIN_SIZE && table->entryCount <= size >> 2) { + size = PL_DHASH_TABLE_SIZE(table); + if (size > PL_DHASH_MIN_SIZE && + table->entryCount <= MIN_LOAD(table, size)) { METER(table->stats.shrinks++); (void) ChangeTable(table, -1); } @@ -538,7 +592,7 @@ PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg) entryAddr = table->entryStore; entrySize = table->entrySize; - capacity = PR_BIT(table->sizeLog2); + capacity = PL_DHASH_TABLE_SIZE(table); entryLimit = entryAddr + capacity * entrySize; i = 0; while (entryAddr < entryLimit) { @@ -555,14 +609,16 @@ PL_DHashTableEnumerate(PLDHashTable *table, PLDHashEnumerator etor, void *arg) entryAddr += entrySize; } - /* Shrink or compress if enough entries were removed that alpha < .5. */ + /* Shrink or compress if a quarter or more of all entries are removed. */ if (table->removedCount >= capacity >> 2) { METER(table->stats.enumShrinks++); capacity = table->entryCount; capacity += capacity >> 1; if (capacity < PL_DHASH_MIN_SIZE) capacity = PL_DHASH_MIN_SIZE; - (void) ChangeTable(table, PR_CeilingLog2(capacity) - table->sizeLog2); + (void) ChangeTable(table, + PR_CeilingLog2(capacity) + - (PL_DHASH_BITS - table->hashShift)); } return i; } @@ -575,6 +631,7 @@ PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp) { char *entryAddr; PRUint32 entrySize, entryCount; + int hashShift, sizeLog2; PRUint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount; PLDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2; double sqsum, mean, variance, sigma; @@ -582,8 +639,10 @@ PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp) entryAddr = table->entryStore; entrySize = table->entrySize; - tableSize = PR_BIT(table->sizeLog2); - sizeMask = PR_BITMASK(table->sizeLog2); + hashShift = table->hashShift; + sizeLog2 = PL_DHASH_BITS - hashShift; + tableSize = PL_DHASH_TABLE_SIZE(table); + sizeMask = PR_BITMASK(sizeLog2); chainCount = maxChainLen = 0; hash2 = 0; sqsum = 0; @@ -593,7 +652,7 @@ PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp) entryAddr += entrySize; if (!ENTRY_IS_LIVE(entry)) continue; - hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, table->hashShift); + hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift); saveHash1 = hash1; probe = ADDRESS_ENTRY(table, hash1); chainLen = 1; @@ -601,8 +660,8 @@ PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp) /* Start of a (possibly unit-length) chain. */ chainCount++; } else { - hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, table->sizeLog2, - table->hashShift); + hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2, + hashShift); do { chainLen++; hash1 -= hash2; @@ -638,8 +697,10 @@ PL_DHashTableDumpMeter(PLDHashTable *table, PLDHashEnumerator dump, FILE *fp) fprintf(fp, " number of searches: %u\n", table->stats.searches); fprintf(fp, " number of hits: %u\n", table->stats.hits); fprintf(fp, " number of misses: %u\n", table->stats.misses); - fprintf(fp, " mean steps per search: %g\n", (double)table->stats.steps - / table->stats.searches); + fprintf(fp, " mean steps per search: %g\n", table->stats.searches ? + (double)table->stats.steps + / table->stats.searches : + 0.); fprintf(fp, " mean hash chain length: %g\n", mean); fprintf(fp, " standard deviation: %g\n", sigma); fprintf(fp, " maximum hash chain length: %u\n", maxChainLen); diff --git a/mozilla/xpcom/ds/pldhash.h b/mozilla/xpcom/ds/pldhash.h index 706456e687b..1dbbc82e595 100644 --- a/mozilla/xpcom/ds/pldhash.h +++ b/mozilla/xpcom/ds/pldhash.h @@ -50,9 +50,15 @@ PR_BEGIN_EXTERN_C #define PL_DHASHMETER 1 #endif +/* Maximum table size, do not equal or exceed (see min&maxAlphaFrac, below). */ +#undef PL_DHASH_MAX_SIZE +#define PL_DHASH_MAX_SIZE PR_BIT(24) + /* Minimum table size, or gross entry count (net is at most .75 loaded). */ #ifndef PL_DHASH_MIN_SIZE #define PL_DHASH_MIN_SIZE 16 +#elif (PL_DHASH_MIN_SIZE & (PL_DHASH_MIN_SIZE - 1)) != 0 +#error "PL_DHASH_MIN_SIZE must be a power of two!" #endif /* @@ -154,7 +160,7 @@ struct PLDHashEntryHdr { * assuming esize is not too large (in which case, chaining should probably be * used for any alpha). For esize=2 and k=3, we want alpha >= .2; for esize=3 * and k=2, we want alpha >= .4. For k=4, esize could be 6, and alpha >= .5 - * would still obtain. + * would still obtain. See the PL_DHASH_MIN_ALPHA macro further below. * * The current implementation uses a constant .25 as alpha's lower bound when * deciding to shrink the table (while respecting PL_DHASH_MIN_SIZE). @@ -174,7 +180,8 @@ struct PLDHashTable { PLDHashTableOps *ops; /* virtual operations, see below */ void *data; /* ops- and instance-specific data */ PRInt16 hashShift; /* multiplicative hash shift */ - PRInt16 sizeLog2; /* log2(table size) */ + uint8 maxAlphaFrac; /* 8-bit fixed point max alpha */ + uint8 minAlphaFrac; /* 8-bit fixed point min alpha */ PRUint32 entrySize; /* number of bytes in an entry */ PRUint32 entryCount; /* number of entries in table */ PRUint32 removedCount; /* removed entry sentinels in table */ @@ -203,6 +210,13 @@ struct PLDHashTable { #endif }; +/* + * Size in entries (gross, not net of free and removed sentinels) for table. + * We store hashShift rather than sizeLog2 to optimize the collision-free case + * in SearchTable. + */ +#define PL_DHASH_TABLE_SIZE(table) PR_BIT(PL_DHASH_BITS - (table)->hashShift) + /* * Table space at entryStore is allocated and freed using these callbacks. * The allocator should return null on error only (not if called with nbytes @@ -393,6 +407,28 @@ PR_EXTERN(PRBool) PL_DHashTableInit(PLDHashTable *table, PLDHashTableOps *ops, void *data, PRUint32 entrySize, PRUint32 capacity); +/* + * Set maximum and minimum alpha for table. The defaults are 0.75 and .25. + * maxAlpha must be in [0.5, 0.9375] for the default PL_DHASH_MIN_SIZE; or if + * MinSize=PL_DHASH_MIN_SIZE <= 256, in [0.5, (float)(MinSize-1)/MinSize]; or + * else in [0.5, 255.0/256]. minAlpha must be in [0, maxAlpha / 2), so that + * we don't shrink on next remove after growing a table upon adding an entry + * that brings entryCount past maxAlpha * tableSize. + */ +PR_IMPLEMENT(void) +PL_DHashTableSetAlphaBounds(PLDHashTable *table, + float maxAlpha, + float minAlpha); + +/* + * Call this macro with k, the number of pointer-sized words wasted per entry + * under chaining, to compute the minimum alpha at which double hashing still + * beats chaining. + */ +#define PL_DHASH_MIN_ALPHA(table, k) \ + ((float)((table)->entrySize / sizeof(void *) - 1) \ + / ((table)->entrySize / sizeof(void *) + (k))) + /* * Finalize table's data, free its entry storage using table->ops->freeTable, * and leave its members unchanged from their last live values (which leaves diff --git a/mozilla/xpcom/io/nsFastLoadFile.cpp b/mozilla/xpcom/io/nsFastLoadFile.cpp index c7d25c801e8..18b4c9e9214 100644 --- a/mozilla/xpcom/io/nsFastLoadFile.cpp +++ b/mozilla/xpcom/io/nsFastLoadFile.cpp @@ -497,7 +497,7 @@ nsFastLoadFileReader::EndMuxedDocument(nsISupports* aURI) return NS_ERROR_NOT_AVAILABLE; // Shrink the table if half the entries are removed sentinels. - PRUint32 size = PR_BIT(mFooter.mURIMap.sizeLog2); + PRUint32 size = PL_DHASH_TABLE_SIZE(&mFooter.mURIMap); if (mFooter.mURIMap.removedCount >= (size >> 2)) PL_DHashTableOperate(&mFooter.mURIMap, key, PL_DHASH_REMOVE); else diff --git a/mozilla/xpcom/io/nsFastLoadService.cpp b/mozilla/xpcom/io/nsFastLoadService.cpp index 09cbd088dd0..26fd40bf3a3 100644 --- a/mozilla/xpcom/io/nsFastLoadService.cpp +++ b/mozilla/xpcom/io/nsFastLoadService.cpp @@ -465,7 +465,7 @@ nsFastLoadService::GetFastLoadReferent(nsISupports* *aPtrAddr) return rv; // Shrink the table if half the entries are removed sentinels. - PRUint32 size = PR_BIT(mFastLoadPtrMap->sizeLog2); + PRUint32 size = PL_DHASH_TABLE_SIZE(mFastLoadPtrMap); if (mFastLoadPtrMap->removedCount >= (size >> 2)) PL_DHashTableOperate(mFastLoadPtrMap, entry, PL_DHASH_REMOVE); else