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 + -
显示快捷键?