Marginal performance improvements in dynahash: make sure that everything

associated with a hashtable is allocated in that hashtable's private
context, so that hash_destroy only has to destroy the context and not
do any retail pfree's; and tighten the inner loop of hash_seq_search.
This commit is contained in:
Tom Lane 2005-05-06 00:19:14 +00:00
parent 6f1ca7e457
commit fba2a104c6
1 changed files with 122 additions and 94 deletions

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/hash/dynahash.c,v 1.58 2004/12/31 22:01:37 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/utils/hash/dynahash.c,v 1.59 2005/05/06 00:19:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -72,9 +72,8 @@ static void hash_corrupted(HTAB *hashp);
/*
* memory allocation routines
* memory allocation support
*/
static MemoryContext DynaHashCxt = NULL;
static MemoryContext CurrentDynaHashCxt = NULL;
static void *
@ -84,10 +83,6 @@ DynaHashAlloc(Size size)
return MemoryContextAlloc(CurrentDynaHashCxt, size);
}
#define MEM_ALLOC DynaHashAlloc
#undef MEM_FREE /* already in windows header files */
#define MEM_FREE pfree
#if HASH_STATISTICS
static long hash_accesses,
@ -98,31 +93,60 @@ static long hash_accesses,
/************************** CREATE ROUTINES **********************/
/*
* hash_create -- create a new dynamic hash table
*
* tabname: a name for the table (for debugging purposes)
* nelem: maximum number of elements expected
* *info: additional table parameters, as indicated by flags
* flags: bitmask indicating which parameters to take from *info
*
* Note: for a shared-memory hashtable, nelem needs to be a pretty good
* estimate, since we can't expand the table on the fly. But an unshared
* hashtable can be expanded on-the-fly, so it's better for nelem to be
* on the small side and let the table grow if it's exceeded. An overly
* large nelem will penalize hash_seq_search speed without buying much.
*/
HTAB *
hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
{
HTAB *hashp;
HASHHDR *hctl;
/* First time through, create a memory context for hash tables */
if (!DynaHashCxt)
DynaHashCxt = AllocSetContextCreate(TopMemoryContext,
"DynaHash",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
/* Select allocation context for this hash table */
if (flags & HASH_CONTEXT)
CurrentDynaHashCxt = info->hcxt;
/*
* For shared hash tables, we have a local hash header (HTAB struct)
* that we allocate in TopMemoryContext; all else is in shared memory.
*
* For non-shared hash tables, everything including the hash header
* is in a memory context created specially for the hash table ---
* this makes hash_destroy very simple. The memory context is made
* a child of either a context specified by the caller, or
* TopMemoryContext if nothing is specified.
*/
if (flags & HASH_SHARED_MEM)
{
/* Set up to allocate the hash header */
CurrentDynaHashCxt = TopMemoryContext;
}
else
CurrentDynaHashCxt = DynaHashCxt;
{
/* Create the hash table's private memory context */
if (flags & HASH_CONTEXT)
CurrentDynaHashCxt = info->hcxt;
else
CurrentDynaHashCxt = TopMemoryContext;
CurrentDynaHashCxt = AllocSetContextCreate(CurrentDynaHashCxt,
tabname,
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
}
/* Initialize the hash header */
hashp = (HTAB *) MEM_ALLOC(sizeof(HTAB));
/* Initialize the hash header, plus a copy of the table name */
hashp = (HTAB *) DynaHashAlloc(sizeof(HTAB) + strlen(tabname) + 1);
MemSet(hashp, 0, sizeof(HTAB));
hashp->tabname = (char *) MEM_ALLOC(strlen(tabname) + 1);
hashp->tabname = (char *) (hashp + 1);
strcpy(hashp->tabname, tabname);
if (flags & HASH_FUNCTION)
@ -143,6 +167,11 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
else
hashp->match = memcmp;
if (flags & HASH_ALLOC)
hashp->alloc = info->alloc;
else
hashp->alloc = DynaHashAlloc;
if (flags & HASH_SHARED_MEM)
{
/*
@ -151,7 +180,6 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
*/
hashp->hctl = info->hctl;
hashp->dir = info->dir;
hashp->alloc = info->alloc;
hashp->hcxt = NULL;
hashp->isshared = true;
@ -164,7 +192,6 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
/* setup hash table defaults */
hashp->hctl = NULL;
hashp->dir = NULL;
hashp->alloc = MEM_ALLOC;
hashp->hcxt = CurrentDynaHashCxt;
hashp->isshared = false;
}
@ -210,23 +237,11 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
*/
if (flags & HASH_ELEM)
{
Assert(info->entrysize >= info->keysize);
hctl->keysize = info->keysize;
hctl->entrysize = info->entrysize;
}
if (flags & HASH_ALLOC)
hashp->alloc = info->alloc;
else
{
/* remaining hash table structures live in child of given context */
hashp->hcxt = AllocSetContextCreate(CurrentDynaHashCxt,
tabname,
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
CurrentDynaHashCxt = hashp->hcxt;
}
/* Build the hash directory structure */
if (!init_htab(hashp, nelem))
{
@ -431,26 +446,16 @@ hash_destroy(HTAB *hashp)
if (hashp != NULL)
{
/* allocation method must be one we know how to free, too */
Assert(hashp->alloc == MEM_ALLOC);
Assert(hashp->alloc == DynaHashAlloc);
/* so this hashtable must have it's own context */
Assert(hashp->hcxt != NULL);
hash_stats("destroy", hashp);
/*
* Free buckets, dir etc. by destroying the hash table's memory
* context.
* Free everything by destroying the hash table's memory context.
*/
MemoryContextDelete(hashp->hcxt);
/*
* Free the HTAB and control structure, which are allocated in the
* parent context (DynaHashCxt or the context given by the caller
* of hash_create()).
*/
MEM_FREE(hashp->hctl);
MEM_FREE(hashp->tabname);
MEM_FREE(hashp);
}
}
@ -702,55 +707,74 @@ hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
void *
hash_seq_search(HASH_SEQ_STATUS *status)
{
HTAB *hashp = status->hashp;
HASHHDR *hctl = hashp->hctl;
HTAB *hashp;
HASHHDR *hctl;
uint32 max_bucket;
long ssize;
long segment_num;
long segment_ndx;
HASHSEGMENT segp;
uint32 curBucket;
HASHELEMENT *curElem;
while (status->curBucket <= hctl->max_bucket)
if ((curElem = status->curEntry) != NULL)
{
long segment_num;
long segment_ndx;
HASHSEGMENT segp;
if (status->curEntry != NULL)
{
/* Continuing scan of curBucket... */
HASHELEMENT *curElem;
curElem = status->curEntry;
status->curEntry = curElem->link;
if (status->curEntry == NULL) /* end of this bucket */
++status->curBucket;
return (void *) ELEMENTKEY(curElem);
}
/*
* initialize the search within this bucket.
*/
segment_num = status->curBucket >> hctl->sshift;
segment_ndx = MOD(status->curBucket, hctl->ssize);
/*
* first find the right segment in the table directory.
*/
segp = hashp->dir[segment_num];
if (segp == NULL)
hash_corrupted(hashp);
/*
* now find the right index into the segment for the first item in
* this bucket's chain. if the bucket is not empty (its entry in
* the dir is valid), we know this must correspond to a valid
* element and not a freed element because it came out of the
* directory of valid stuff. if there are elements in the bucket
* chains that point to the freelist we're in big trouble.
*/
status->curEntry = segp[segment_ndx];
if (status->curEntry == NULL) /* empty bucket */
/* Continuing scan of curBucket... */
status->curEntry = curElem->link;
if (status->curEntry == NULL) /* end of this bucket */
++status->curBucket;
return (void *) ELEMENTKEY(curElem);
}
return NULL; /* out of buckets */
/*
* Search for next nonempty bucket starting at curBucket.
*/
curBucket = status->curBucket;
hashp = status->hashp;
hctl = hashp->hctl;
ssize = hctl->ssize;
max_bucket = hctl->max_bucket;
if (curBucket > max_bucket)
return NULL; /* search is done */
/*
* first find the right segment in the table directory.
*/
segment_num = curBucket >> hctl->sshift;
segment_ndx = MOD(curBucket, ssize);
segp = hashp->dir[segment_num];
/*
* Pick up the first item in this bucket's chain. If chain is
* not empty we can go back around the outer loop to search it.
* Otherwise we have to advance to find the next nonempty bucket.
* We try to optimize that case since searching a near-empty
* hashtable has to iterate this loop a lot.
*/
while ((curElem = segp[segment_ndx]) == NULL)
{
/* empty bucket, advance to next */
if (++curBucket > max_bucket)
{
status->curBucket = curBucket;
return NULL; /* search is done */
}
if (++segment_ndx >= ssize)
{
segment_num++;
segment_ndx = 0;
segp = hashp->dir[segment_num];
}
}
/* Begin scan of curBucket... */
status->curEntry = curElem->link;
if (status->curEntry == NULL) /* end of this bucket */
++curBucket;
status->curBucket = curBucket;
return (void *) ELEMENTKEY(curElem);
}
@ -880,9 +904,13 @@ dir_realloc(HTAB *hashp)
{
memcpy(p, old_p, old_dirsize);
MemSet(((char *) p) + old_dirsize, 0, new_dirsize - old_dirsize);
MEM_FREE((char *) old_p);
hashp->dir = p;
hashp->hctl->dsize = new_dsize;
/* XXX assume the allocator is palloc, so we know how to free */
Assert(hashp->alloc == DynaHashAlloc);
pfree(old_p);
return true;
}