costsize.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,021 行 · 第 1/5 页

C
2,021
字号
		((outer_rows - outer_skip_rows) +		 (inner_rows - inner_skip_rows) * rescanratio);	/*	 * For each tuple that gets through the mergejoin proper, we charge	 * cpu_tuple_cost plus the cost of evaluating additional restriction	 * clauses that are to be applied at the join.	(This is pessimistic since	 * not all of the quals may get evaluated at each tuple.)  This work is	 * skipped in JOIN_IN mode, so apply the factor.	 */	startup_cost += qp_qual_cost.startup;	cpu_per_tuple = cpu_tuple_cost + qp_qual_cost.per_tuple;	run_cost += cpu_per_tuple * mergejointuples * joininfactor;	path->jpath.path.startup_cost = startup_cost;	path->jpath.path.total_cost = startup_cost + run_cost;}/* * run mergejoinscansel() with caching */static MergeScanSelCache *cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey){	MergeScanSelCache *cache;	ListCell   *lc;	Selectivity leftstartsel,				leftendsel,				rightstartsel,				rightendsel;	MemoryContext oldcontext;	/* Do we have this result already? */	foreach(lc, rinfo->scansel_cache)	{		cache = (MergeScanSelCache *) lfirst(lc);		if (cache->opfamily == pathkey->pk_opfamily &&			cache->strategy == pathkey->pk_strategy &&			cache->nulls_first == pathkey->pk_nulls_first)			return cache;	}	/* Nope, do the computation */	mergejoinscansel(root,					 (Node *) rinfo->clause,					 pathkey->pk_opfamily,					 pathkey->pk_strategy,					 pathkey->pk_nulls_first,					 &leftstartsel,					 &leftendsel,					 &rightstartsel,					 &rightendsel);	/* Cache the result in suitably long-lived workspace */	oldcontext = MemoryContextSwitchTo(root->planner_cxt);	cache = (MergeScanSelCache *) palloc(sizeof(MergeScanSelCache));	cache->opfamily = pathkey->pk_opfamily;	cache->strategy = pathkey->pk_strategy;	cache->nulls_first = pathkey->pk_nulls_first;	cache->leftstartsel = leftstartsel;	cache->leftendsel = leftendsel;	cache->rightstartsel = rightstartsel;	cache->rightendsel = rightendsel;	rinfo->scansel_cache = lappend(rinfo->scansel_cache, cache);	MemoryContextSwitchTo(oldcontext);	return cache;}/* * cost_hashjoin *	  Determines and returns the cost of joining two relations using the *	  hash join algorithm. * * 'path' is already filled in except for the cost fields * * Note: path's hashclauses should be a subset of the joinrestrictinfo list */voidcost_hashjoin(HashPath *path, PlannerInfo *root){	Path	   *outer_path = path->jpath.outerjoinpath;	Path	   *inner_path = path->jpath.innerjoinpath;	List	   *hashclauses = path->path_hashclauses;	Cost		startup_cost = 0;	Cost		run_cost = 0;	Cost		cpu_per_tuple;	Selectivity hash_selec;	QualCost	hash_qual_cost;	QualCost	qp_qual_cost;	double		hashjointuples;	double		outer_path_rows = PATH_ROWS(outer_path);	double		inner_path_rows = PATH_ROWS(inner_path);	int			num_hashclauses = list_length(hashclauses);	int			numbuckets;	int			numbatches;	double		virtualbuckets;	Selectivity innerbucketsize;	Selectivity joininfactor;	ListCell   *hcl;	if (!enable_hashjoin)		startup_cost += disable_cost;	/*	 * Compute cost and selectivity of the hashquals and qpquals (other	 * restriction clauses) separately.  We use approx_selectivity here for	 * speed --- in most cases, any errors won't affect the result much.	 *	 * Note: it's probably bogus to use the normal selectivity calculation	 * here when either the outer or inner path is a UniquePath.	 */	hash_selec = approx_selectivity(root, hashclauses,									path->jpath.jointype);	cost_qual_eval(&hash_qual_cost, hashclauses, root);	cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root);	qp_qual_cost.startup -= hash_qual_cost.startup;	qp_qual_cost.per_tuple -= hash_qual_cost.per_tuple;	/* approx # tuples passing the hash quals */	hashjointuples = clamp_row_est(hash_selec * outer_path_rows * inner_path_rows);	/* cost of source data */	startup_cost += outer_path->startup_cost;	run_cost += outer_path->total_cost - outer_path->startup_cost;	startup_cost += inner_path->total_cost;	/*	 * Cost of computing hash function: must do it once per input tuple. We	 * charge one cpu_operator_cost for each column's hash function.  Also,	 * tack on one cpu_tuple_cost per inner row, to model the costs of	 * inserting the row into the hashtable.	 *	 * XXX when a hashclause is more complex than a single operator, we really	 * should charge the extra eval costs of the left or right side, as	 * appropriate, here.  This seems more work than it's worth at the moment.	 */	startup_cost += (cpu_operator_cost * num_hashclauses + cpu_tuple_cost)		* inner_path_rows;	run_cost += cpu_operator_cost * num_hashclauses * outer_path_rows;	/* Get hash table size that executor would use for inner relation */	ExecChooseHashTableSize(inner_path_rows,							inner_path->parent->width,							&numbuckets,							&numbatches);	virtualbuckets = (double) numbuckets *(double) numbatches;	/*	 * Determine bucketsize fraction for inner relation.  We use the smallest	 * bucketsize estimated for any individual hashclause; this is undoubtedly	 * conservative.	 *	 * BUT: if inner relation has been unique-ified, we can assume it's good	 * for hashing.  This is important both because it's the right answer, and	 * because we avoid contaminating the cache with a value that's wrong for	 * non-unique-ified paths.	 */	if (IsA(inner_path, UniquePath))		innerbucketsize = 1.0 / virtualbuckets;	else	{		innerbucketsize = 1.0;		foreach(hcl, hashclauses)		{			RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(hcl);			Selectivity thisbucketsize;			Assert(IsA(restrictinfo, RestrictInfo));			/*			 * First we have to figure out which side of the hashjoin clause			 * is the inner side.			 *			 * Since we tend to visit the same clauses over and over when			 * planning a large query, we cache the bucketsize estimate in the			 * RestrictInfo node to avoid repeated lookups of statistics.			 */			if (bms_is_subset(restrictinfo->right_relids,							  inner_path->parent->relids))			{				/* righthand side is inner */				thisbucketsize = restrictinfo->right_bucketsize;				if (thisbucketsize < 0)				{					/* not cached yet */					thisbucketsize =						estimate_hash_bucketsize(root,										   get_rightop(restrictinfo->clause),												 virtualbuckets);					restrictinfo->right_bucketsize = thisbucketsize;				}			}			else			{				Assert(bms_is_subset(restrictinfo->left_relids,									 inner_path->parent->relids));				/* lefthand side is inner */				thisbucketsize = restrictinfo->left_bucketsize;				if (thisbucketsize < 0)				{					/* not cached yet */					thisbucketsize =						estimate_hash_bucketsize(root,											get_leftop(restrictinfo->clause),												 virtualbuckets);					restrictinfo->left_bucketsize = thisbucketsize;				}			}			if (innerbucketsize > thisbucketsize)				innerbucketsize = thisbucketsize;		}	}	/*	 * If inner relation is too big then we will need to "batch" the join,	 * which implies writing and reading most of the tuples to disk an extra	 * time.  Charge seq_page_cost per page, since the I/O should be nice and	 * sequential.	Writing the inner rel counts as startup cost, all the rest	 * as run cost.	 */	if (numbatches > 1)	{		double		outerpages = page_size(outer_path_rows,										   outer_path->parent->width);		double		innerpages = page_size(inner_path_rows,										   inner_path->parent->width);		startup_cost += seq_page_cost * innerpages;		run_cost += seq_page_cost * (innerpages + 2 * outerpages);	}	/* CPU costs */	/*	 * If we're doing JOIN_IN then we will stop comparing inner tuples to an	 * outer tuple as soon as we have one match.  Account for the effects of	 * this by scaling down the cost estimates in proportion to the expected	 * output size.  (This assumes that all the quals attached to the join are	 * IN quals, which should be true.)	 */	joininfactor = join_in_selectivity(&path->jpath, root);	/*	 * The number of tuple comparisons needed is the number of outer tuples	 * times the typical number of tuples in a hash bucket, which is the inner	 * relation size times its bucketsize fraction.  At each one, we need to	 * evaluate the hashjoin quals.  But actually, charging the full qual eval	 * cost at each tuple is pessimistic, since we don't evaluate the quals	 * unless the hash values match exactly.  For lack of a better idea, halve	 * the cost estimate to allow for that.	 */	startup_cost += hash_qual_cost.startup;	run_cost += hash_qual_cost.per_tuple *		outer_path_rows * clamp_row_est(inner_path_rows * innerbucketsize) *		joininfactor * 0.5;	/*	 * For each tuple that gets through the hashjoin proper, we charge	 * cpu_tuple_cost plus the cost of evaluating additional restriction	 * clauses that are to be applied at the join.	(This is pessimistic since	 * not all of the quals may get evaluated at each tuple.)	 */	startup_cost += qp_qual_cost.startup;	cpu_per_tuple = cpu_tuple_cost + qp_qual_cost.per_tuple;	run_cost += cpu_per_tuple * hashjointuples * joininfactor;	path->jpath.path.startup_cost = startup_cost;	path->jpath.path.total_cost = startup_cost + run_cost;}/* * cost_qual_eval *		Estimate the CPU costs of evaluating a WHERE clause. *		The input can be either an implicitly-ANDed list of boolean *		expressions, or a list of RestrictInfo nodes.  (The latter is *		preferred since it allows caching of the results.) *		The result includes both a one-time (startup) component, *		and a per-evaluation component. */voidcost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root){	cost_qual_eval_context context;	ListCell   *l;	context.root = root;	context.total.startup = 0;	context.total.per_tuple = 0;	/* We don't charge any cost for the implicit ANDing at top level ... */	foreach(l, quals)	{		Node	   *qual = (Node *) lfirst(l);		cost_qual_eval_walker(qual, &context);	}	*cost = context.total;}/* * cost_qual_eval_node *		As above, for a single RestrictInfo or expression. */voidcost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root){	cost_qual_eval_context context;	context.root = root;	context.total.startup = 0;	context.total.per_tuple = 0;	cost_qual_eval_walker(qual, &context);	*cost = context.total;}static boolcost_qual_eval_walker(Node *node, cost_qual_eval_context *context){	if (node == NULL)		return false;	/*	 * RestrictInfo nodes contain an eval_cost field reserved for this	 * routine's use, so that it's not necessary to evaluate the qual clause's	 * cost more than once.  If the clause's cost hasn't been computed yet,	 * the field's startup value will contain -1.	 */	if (IsA(node, RestrictInfo))	{		RestrictInfo *rinfo = (RestrictInfo *) node;		if (rinfo->eval_cost.startup < 0)		{			cost_qual_eval_context locContext;			locContext.root = context->root;			locContext.total.startup = 0;			locContext.total.per_tuple = 0;			/*			 * For an OR clause, recurse into the marked-up tree so that we			 * set the eval_cost for contained RestrictInfos too.			 */			if (rinfo->orclause)				cost_qual_eval_walker((Node *) rinfo->orclause, &locContext);			else				cost_qual_eval_walker((Node *) rinfo->clause, &locContext);			/*			 * If the RestrictInfo is marked pseudoconstant, it will be tested			 * only once, so treat its cost as all startup cost.			 */			if (rinfo->pseudoconstant)			{				/* count one execution during startup */				locContext.total.startup += locContext.total.per_tuple;				locContext.total.per_tuple = 0;			}			rinfo->eval_cost = locContext.total;		}		context->total.startup += rinfo->eval_cost.startup;		context->total.per_tuple += rinfo->eval_cost.per_tuple;		/* do NOT recurse into children */		return false;	}	/*	 * For each operator or function node in the given tree, we charge the	 * estimated execution cost given by pg_proc.procost (remember to multiply	 * this by cpu_operator_cost).	 *	 * Vars and Consts are charged zero, and so are boolean operators (AND,	 * OR, NOT). Simplistic, but a lot better than no model at all.	 *	 * Should we try to account for the possibility of short-circuit	 * evaluation of AND/OR?  Probably *not*, because that would make the	 * results depend on the clause ordering, and we are not in any position	 * to expect that the current ordering of the clauses is the one that's	 * going to end up being used.	(Is it worth applying order_qual_clauses	 * much earlier in the planning process to fix this?)	 */	if (IsA(node, FuncExpr))	{		context->total.per_tuple +=			get_func_cost(((FuncExpr *) node)->funcid) * cpu_operator_cost;	}	else if (IsA(node, OpExpr) ||			 IsA(node, DistinctExpr) ||			 IsA(node, NullIfExpr))	{		/* rely on struct e

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?