Commit e3a33a9a authored by Tom Lane's avatar Tom Lane

Marginal hack to avoid spending a lot of time in find_join_rel during

large planning problems: when the list of join rels gets too long, make
an auxiliary hash table that hashes on the identifying Bitmapset.
parent 77c168a8
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* Copyright (c) 2003-2005, PostgreSQL Global Development Group * Copyright (c) 2003-2005, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/bitmapset.c,v 1.7 2005/01/01 20:44:15 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/bitmapset.c,v 1.8 2005/06/08 23:02:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -763,3 +763,28 @@ bms_first_member(Bitmapset *a) ...@@ -763,3 +763,28 @@ bms_first_member(Bitmapset *a)
} }
return -1; return -1;
} }
/*
* bms_hash_value - compute a hash key for a Bitmapset
*
* Note: we must ensure that any two bitmapsets that are bms_equal() will
* hash to the same value; in practice this means that trailing all-zero
* words cannot affect the result. Longitudinal XOR provides a reasonable
* hash value that has this property.
*/
uint32
bms_hash_value(const Bitmapset *a)
{
bitmapword result = 0;
int nwords;
int wordnum;
if (a == NULL)
return 0; /* All empty sets hash to 0 */
nwords = a->nwords;
for (wordnum = 0; wordnum < nwords; wordnum++)
{
result ^= a->words[wordnum];
}
return (uint32) result;
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.74 2005/06/05 22:32:55 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.75 2005/06/08 23:02:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,7 +47,8 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata) ...@@ -47,7 +47,8 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata)
MemoryContext oldcxt; MemoryContext oldcxt;
RelOptInfo *joinrel; RelOptInfo *joinrel;
Cost fitness; Cost fitness;
List *savelist; int savelength;
struct HTAB *savehash;
/* /*
* Because gimme_tree considers both left- and right-sided trees, * Because gimme_tree considers both left- and right-sided trees,
...@@ -83,13 +84,19 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata) ...@@ -83,13 +84,19 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata)
* gimme_tree will add entries to root->join_rel_list, which may or may * gimme_tree will add entries to root->join_rel_list, which may or may
* not already contain some entries. The newly added entries will be * not already contain some entries. The newly added entries will be
* recycled by the MemoryContextDelete below, so we must ensure that * recycled by the MemoryContextDelete below, so we must ensure that
* the list is restored to its former state before exiting. With the * the list is restored to its former state before exiting. We can
* new List implementation, the easiest way is to make a duplicate list * do this by truncating the list to its original length. NOTE this
* that gimme_tree can modify. * assumes that any added entries are appended at the end!
*
* We also must take care not to mess up the outer join_rel_hash,
* if there is one. We can do this by just temporarily setting the
* link to NULL. (If we are dealing with enough join rels, which we
* very likely are, a new hash table will get built and used locally.)
*/ */
savelist = evaldata->root->join_rel_list; savelength = list_length(evaldata->root->join_rel_list);
savehash = evaldata->root->join_rel_hash;
evaldata->root->join_rel_list = list_copy(savelist); evaldata->root->join_rel_hash = NULL;
/* construct the best path for the given combination of relations */ /* construct the best path for the given combination of relations */
joinrel = gimme_tree(tour, num_gene, evaldata); joinrel = gimme_tree(tour, num_gene, evaldata);
...@@ -105,8 +112,13 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata) ...@@ -105,8 +112,13 @@ geqo_eval(Gene *tour, int num_gene, GeqoEvalData *evaldata)
else else
fitness = DBL_MAX; fitness = DBL_MAX;
/* restore join_rel_list */ /*
evaldata->root->join_rel_list = savelist; * Restore join_rel_list to its former state, and put back original
* hashtable if any.
*/
evaldata->root->join_rel_list = list_truncate(evaldata->root->join_rel_list,
savelength);
evaldata->root->join_rel_hash = savehash;
/* release all the memory acquired within gimme_tree */ /* release all the memory acquired within gimme_tree */
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_main.c,v 1.49 2005/06/05 22:32:55 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/geqo/geqo_main.c,v 1.50 2005/06/08 23:02:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -252,7 +252,6 @@ geqo(PlannerInfo *root, int number_of_rels, List *initial_rels) ...@@ -252,7 +252,6 @@ geqo(PlannerInfo *root, int number_of_rels, List *initial_rels)
*/ */
best_tour = (Gene *) pool->data[0].string; best_tour = (Gene *) pool->data[0].string;
/* root->join_rel_list will be modified during this ! */
best_rel = gimme_tree(best_tour, pool->string_length, &evaldata); best_rel = gimme_tree(best_tour, pool->string_length, &evaldata);
if (best_rel == NULL) if (best_rel == NULL)
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.83 2005/06/06 04:13:35 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.84 2005/06/08 23:02:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -116,6 +116,7 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction, ...@@ -116,6 +116,7 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
root->base_rel_array = (RelOptInfo **) root->base_rel_array = (RelOptInfo **)
palloc0(root->base_rel_array_size * sizeof(RelOptInfo *)); palloc0(root->base_rel_array_size * sizeof(RelOptInfo *));
root->join_rel_list = NIL; root->join_rel_list = NIL;
root->join_rel_hash = NULL;
root->equi_key_list = NIL; root->equi_key_list = NIL;
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.68 2005/06/06 04:13:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.69 2005/06/08 23:02:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,8 +21,15 @@ ...@@ -21,8 +21,15 @@
#include "optimizer/restrictinfo.h" #include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/hsearch.h"
typedef struct JoinHashEntry
{
Relids join_relids; /* hash key --- MUST BE FIRST */
RelOptInfo *join_rel;
} JoinHashEntry;
static RelOptInfo *make_reloptinfo(PlannerInfo *root, int relid, static RelOptInfo *make_reloptinfo(PlannerInfo *root, int relid,
RelOptKind reloptkind); RelOptKind reloptkind);
static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel,
...@@ -197,6 +204,47 @@ find_base_rel(PlannerInfo *root, int relid) ...@@ -197,6 +204,47 @@ find_base_rel(PlannerInfo *root, int relid)
return NULL; /* keep compiler quiet */ return NULL; /* keep compiler quiet */
} }
/*
* build_join_rel_hash
* Construct the auxiliary hash table for join relations.
*/
static void
build_join_rel_hash(PlannerInfo *root)
{
HTAB *hashtab;
HASHCTL hash_ctl;
ListCell *l;
/* Create the hash table */
MemSet(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Relids);
hash_ctl.entrysize = sizeof(JoinHashEntry);
hash_ctl.hash = bitmap_hash;
hash_ctl.match = bitmap_match;
hash_ctl.hcxt = CurrentMemoryContext;
hashtab = hash_create("JoinRelHashTable",
256L,
&hash_ctl,
HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);
/* Insert all the already-existing joinrels */
foreach(l, root->join_rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(l);
JoinHashEntry *hentry;
bool found;
hentry = (JoinHashEntry *) hash_search(hashtab,
&(rel->relids),
HASH_ENTER,
&found);
Assert(!found);
hentry->join_rel = rel;
}
root->join_rel_hash = hashtab;
}
/* /*
* find_join_rel * find_join_rel
* Returns relation entry corresponding to 'relids' (a set of RT indexes), * Returns relation entry corresponding to 'relids' (a set of RT indexes),
...@@ -205,6 +253,35 @@ find_base_rel(PlannerInfo *root, int relid) ...@@ -205,6 +253,35 @@ find_base_rel(PlannerInfo *root, int relid)
RelOptInfo * RelOptInfo *
find_join_rel(PlannerInfo *root, Relids relids) find_join_rel(PlannerInfo *root, Relids relids)
{ {
/*
* Switch to using hash lookup when list grows "too long". The threshold
* is arbitrary and is known only here.
*/
if (!root->join_rel_hash && list_length(root->join_rel_list) > 32)
build_join_rel_hash(root);
/*
* Use either hashtable lookup or linear search, as appropriate.
*
* Note: the seemingly redundant hashkey variable is used to avoid
* taking the address of relids; unless the compiler is exceedingly
* smart, doing so would force relids out of a register and thus
* probably slow down the list-search case.
*/
if (root->join_rel_hash)
{
Relids hashkey = relids;
JoinHashEntry *hentry;
hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
&hashkey,
HASH_FIND,
NULL);
if (hentry)
return hentry->join_rel;
}
else
{
ListCell *l; ListCell *l;
foreach(l, root->join_rel_list) foreach(l, root->join_rel_list)
...@@ -214,6 +291,7 @@ find_join_rel(PlannerInfo *root, Relids relids) ...@@ -214,6 +291,7 @@ find_join_rel(PlannerInfo *root, Relids relids)
if (bms_equal(rel->relids, relids)) if (bms_equal(rel->relids, relids))
return rel; return rel;
} }
}
return NULL; return NULL;
} }
...@@ -329,9 +407,24 @@ build_join_rel(PlannerInfo *root, ...@@ -329,9 +407,24 @@ build_join_rel(PlannerInfo *root,
jointype, restrictlist); jointype, restrictlist);
/* /*
* Add the joinrel to the query's joinrel list. * Add the joinrel to the query's joinrel list, and store it into
* the auxiliary hashtable if there is one. NB: GEQO requires us
* to append the new joinrel to the end of the list!
*/ */
root->join_rel_list = lcons(joinrel, root->join_rel_list); root->join_rel_list = lappend(root->join_rel_list, joinrel);
if (root->join_rel_hash)
{
JoinHashEntry *hentry;
bool found;
hentry = (JoinHashEntry *) hash_search(root->join_rel_hash,
&(joinrel->relids),
HASH_ENTER,
&found);
Assert(!found);
hentry->join_rel = joinrel;
}
return joinrel; return joinrel;
} }
......
...@@ -9,13 +9,14 @@ ...@@ -9,13 +9,14 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/hash/hashfn.c,v 1.23 2005/04/14 20:32:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/hash/hashfn.c,v 1.24 2005/06/08 23:02:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "access/hash.h" #include "access/hash.h"
#include "nodes/bitmapset.h"
#include "utils/hsearch.h" #include "utils/hsearch.h"
...@@ -53,3 +54,26 @@ oid_hash(const void *key, Size keysize) ...@@ -53,3 +54,26 @@ oid_hash(const void *key, Size keysize)
/* We don't actually bother to do anything to the OID value ... */ /* We don't actually bother to do anything to the OID value ... */
return (uint32) *((const Oid *) key); return (uint32) *((const Oid *) key);
} }
/*
* bitmap_hash: hash function for keys that are (pointers to) Bitmapsets
*
* Note: don't forget to specify bitmap_match as the match function!
*/
uint32
bitmap_hash(const void *key, Size keysize)
{
Assert(keysize == sizeof(Bitmapset *));
return bms_hash_value(*((const Bitmapset * const *) key));
}
/*
* bitmap_match: match function to use with bitmap_hash
*/
int
bitmap_match(const void *key1, const void *key2, Size keysize)
{
Assert(keysize == sizeof(Bitmapset *));
return !bms_equal(*((const Bitmapset * const *) key1),
*((const Bitmapset * const *) key2));
}
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* Copyright (c) 2003-2005, PostgreSQL Global Development Group * Copyright (c) 2003-2005, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/include/nodes/bitmapset.h,v 1.6 2005/01/01 20:44:28 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/bitmapset.h,v 1.7 2005/06/08 23:02:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -80,4 +80,7 @@ extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b); ...@@ -80,4 +80,7 @@ extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b);
/* support for iterating through the integer elements of a set: */ /* support for iterating through the integer elements of a set: */
extern int bms_first_member(Bitmapset *a); extern int bms_first_member(Bitmapset *a);
/* support for hashtables using Bitmapsets as keys: */
extern uint32 bms_hash_value(const Bitmapset *a);
#endif /* BITMAPSET_H */ #endif /* BITMAPSET_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.111 2005/06/06 04:13:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.112 2005/06/08 23:02:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -72,7 +72,17 @@ typedef struct PlannerInfo ...@@ -72,7 +72,17 @@ typedef struct PlannerInfo
struct RelOptInfo **base_rel_array; /* All one-relation RelOptInfos */ struct RelOptInfo **base_rel_array; /* All one-relation RelOptInfos */
int base_rel_array_size; /* current allocated array len */ int base_rel_array_size; /* current allocated array len */
/*
* join_rel_list is a list of all join-relation RelOptInfos we have
* considered in this planning run. For small problems we just scan
* the list to do lookups, but when there are many join relations we
* build a hash table for faster lookups. The hash table is present
* and valid when join_rel_hash is not NULL. Note that we still maintain
* the list even when using the hash table for lookups; this simplifies
* life for GEQO.
*/
List *join_rel_list; /* list of join-relation RelOptInfos */ List *join_rel_list; /* list of join-relation RelOptInfos */
struct HTAB *join_rel_hash; /* optional hashtable for join relations */
List *equi_key_list; /* list of lists of equijoined List *equi_key_list; /* list of lists of equijoined
* PathKeyItems */ * PathKeyItems */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/hsearch.h,v 1.36 2005/05/29 04:23:06 tgl Exp $ * $PostgreSQL: pgsql/src/include/utils/hsearch.h,v 1.37 2005/06/08 23:02:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -184,5 +184,7 @@ extern long hash_select_dirsize(long num_entries); ...@@ -184,5 +184,7 @@ extern long hash_select_dirsize(long num_entries);
extern uint32 string_hash(const void *key, Size keysize); extern uint32 string_hash(const void *key, Size keysize);
extern uint32 tag_hash(const void *key, Size keysize); extern uint32 tag_hash(const void *key, Size keysize);
extern uint32 oid_hash(const void *key, Size keysize); extern uint32 oid_hash(const void *key, Size keysize);
extern uint32 bitmap_hash(const void *key, Size keysize);
extern int bitmap_match(const void *key1, const void *key2, Size keysize);
#endif /* HSEARCH_H */ #endif /* HSEARCH_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment