Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d4ce5a4

Browse files
committed
Revise cost_qual_eval() to compute both startup (one-time) and per-tuple
costs for expression evaluation, not only per-tuple cost as before. This extension is needed in order to deal realistically with hashed or materialized sub-selects.
1 parent d51260a commit d4ce5a4

File tree

8 files changed

+164
-71
lines changed

8 files changed

+164
-71
lines changed

doc/src/sgml/indexcost.sgml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.12 2002/08/22 00:01:40 tgl Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/Attic/indexcost.sgml,v 2.13 2003/01/12 22:35:29 tgl Exp $
33
-->
44

55
<chapter id="indexcost">
@@ -237,9 +237,10 @@ amcostestimate (Query *root,
237237
* Also, we charge for evaluation of the indexquals at each index tuple.
238238
* All the costs are assumed to be paid incrementally during the scan.
239239
*/
240-
*indexStartupCost = 0;
240+
cost_qual_eval(&index_qual_cost, indexQuals);
241+
*indexStartupCost = index_qual_cost.startup;
241242
*indexTotalCost = numIndexPages +
242-
(cpu_index_tuple_cost + cost_qual_eval(indexQuals)) * numIndexTuples;
243+
(cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;
243244
</programlisting>
244245
</para>
245246
</step>

src/backend/optimizer/path/costsize.c

Lines changed: 131 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
* Portions Copyright (c) 1994, Regents of the University of California
4343
*
4444
* IDENTIFICATION
45-
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.98 2002/12/30 15:21:21 tgl Exp $
45+
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.99 2003/01/12 22:35:29 tgl Exp $
4646
*
4747
*-------------------------------------------------------------------------
4848
*/
@@ -87,7 +87,7 @@ bool enable_hashjoin = true;
8787

8888
static Selectivity estimate_hash_bucketsize(Query *root, Var *var,
8989
int nbuckets);
90-
static bool cost_qual_eval_walker(Node *node, Cost *total);
90+
static bool cost_qual_eval_walker(Node *node, QualCost *total);
9191
static Selectivity approx_selectivity(Query *root, List *quals);
9292
static void set_rel_width(Query *root, RelOptInfo *rel);
9393
static double relation_byte_size(double tuples, int width);
@@ -131,7 +131,8 @@ cost_seqscan(Path *path, Query *root,
131131
run_cost += baserel->pages; /* sequential fetches with cost 1.0 */
132132

133133
/* CPU costs */
134-
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
134+
startup_cost += baserel->baserestrictcost.startup;
135+
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
135136
run_cost += cpu_per_tuple * baserel->tuples;
136137

137138
path->startup_cost = startup_cost;
@@ -344,17 +345,25 @@ cost_index(Path *path, Query *root,
344345
*
345346
* Normally the indexquals will be removed from the list of restriction
346347
* clauses that we have to evaluate as qpquals, so we should subtract
347-
* their costs from baserestrictcost. XXX For a lossy index, not all
348-
* the quals will be removed and so we really shouldn't subtract their
349-
* costs; but detecting that seems more expensive than it's worth.
350-
* Also, if we are doing a join then some of the indexquals are join
351-
* clauses and shouldn't be subtracted. Rather than work out exactly
352-
* how much to subtract, we don't subtract anything.
348+
* their costs from baserestrictcost. But if we are doing a join then
349+
* some of the indexquals are join clauses and shouldn't be subtracted.
350+
* Rather than work out exactly how much to subtract, we don't subtract
351+
* anything.
352+
*
353+
* XXX For a lossy index, not all the quals will be removed and so we
354+
* really shouldn't subtract their costs; but detecting that seems more
355+
* expensive than it's worth.
353356
*/
354-
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
357+
startup_cost += baserel->baserestrictcost.startup;
358+
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
355359

356360
if (!is_injoin)
357-
cpu_per_tuple -= cost_qual_eval(indexQuals);
361+
{
362+
QualCost index_qual_cost;
363+
364+
cost_qual_eval(&index_qual_cost, indexQuals);
365+
cpu_per_tuple -= index_qual_cost.per_tuple;
366+
}
358367

359368
run_cost += cpu_per_tuple * tuples_fetched;
360369

@@ -386,7 +395,8 @@ cost_tidscan(Path *path, Query *root,
386395
run_cost += random_page_cost * ntuples;
387396

388397
/* CPU costs */
389-
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
398+
startup_cost += baserel->baserestrictcost.startup;
399+
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
390400
run_cost += cpu_per_tuple * ntuples;
391401

392402
path->startup_cost = startup_cost;
@@ -416,7 +426,8 @@ cost_functionscan(Path *path, Query *root, RelOptInfo *baserel)
416426
cpu_per_tuple = cpu_operator_cost;
417427

418428
/* Add scanning CPU costs */
419-
cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost;
429+
startup_cost += baserel->baserestrictcost.startup;
430+
cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
420431
run_cost += cpu_per_tuple * baserel->tuples;
421432

422433
path->startup_cost = startup_cost;
@@ -656,6 +667,7 @@ cost_nestloop(Path *path, Query *root,
656667
Cost startup_cost = 0;
657668
Cost run_cost = 0;
658669
Cost cpu_per_tuple;
670+
QualCost restrict_qual_cost;
659671
double ntuples;
660672

661673
if (!enable_nestloop)
@@ -703,7 +715,9 @@ cost_nestloop(Path *path, Query *root,
703715
ntuples *= outer_path->parent->rows;
704716

705717
/* CPU costs */
706-
cpu_per_tuple = cpu_tuple_cost + cost_qual_eval(restrictlist);
718+
cost_qual_eval(&restrict_qual_cost, restrictlist);
719+
startup_cost += restrict_qual_cost.startup;
720+
cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
707721
run_cost += cpu_per_tuple * ntuples;
708722

709723
path->startup_cost = startup_cost;
@@ -736,6 +750,7 @@ cost_mergejoin(Path *path, Query *root,
736750
Cost startup_cost = 0;
737751
Cost run_cost = 0;
738752
Cost cpu_per_tuple;
753+
QualCost restrict_qual_cost;
739754
RestrictInfo *firstclause;
740755
Var *leftvar;
741756
double outer_rows,
@@ -850,7 +865,9 @@ cost_mergejoin(Path *path, Query *root,
850865
outer_path->parent->rows * inner_path->parent->rows;
851866

852867
/* CPU costs */
853-
cpu_per_tuple = cpu_tuple_cost + cost_qual_eval(restrictlist);
868+
cost_qual_eval(&restrict_qual_cost, restrictlist);
869+
startup_cost += restrict_qual_cost.startup;
870+
cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
854871
run_cost += cpu_per_tuple * ntuples;
855872

856873
path->startup_cost = startup_cost;
@@ -878,6 +895,7 @@ cost_hashjoin(Path *path, Query *root,
878895
Cost startup_cost = 0;
879896
Cost run_cost = 0;
880897
Cost cpu_per_tuple;
898+
QualCost restrict_qual_cost;
881899
double ntuples;
882900
double outerbytes = relation_byte_size(outer_path->parent->rows,
883901
outer_path->parent->width);
@@ -984,7 +1002,9 @@ cost_hashjoin(Path *path, Query *root,
9841002
outer_path->parent->rows * inner_path->parent->rows;
9851003

9861004
/* CPU costs */
987-
cpu_per_tuple = cpu_tuple_cost + cost_qual_eval(restrictlist);
1005+
cost_qual_eval(&restrict_qual_cost, restrictlist);
1006+
startup_cost += restrict_qual_cost.startup;
1007+
cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple;
9881008
run_cost += cpu_per_tuple * ntuples;
9891009

9901010
/*
@@ -1185,16 +1205,20 @@ estimate_hash_bucketsize(Query *root, Var *var, int nbuckets)
11851205

11861206
/*
11871207
* cost_qual_eval
1188-
* Estimate the CPU cost of evaluating a WHERE clause (once).
1208+
* Estimate the CPU costs of evaluating a WHERE clause.
11891209
* The input can be either an implicitly-ANDed list of boolean
11901210
* expressions, or a list of RestrictInfo nodes.
1211+
* The result includes both a one-time (startup) component,
1212+
* and a per-evaluation component.
11911213
*/
1192-
Cost
1193-
cost_qual_eval(List *quals)
1214+
void
1215+
cost_qual_eval(QualCost *cost, List *quals)
11941216
{
1195-
Cost total = 0;
11961217
List *l;
11971218

1219+
cost->startup = 0;
1220+
cost->per_tuple = 0;
1221+
11981222
/* We don't charge any cost for the implicit ANDing at top level ... */
11991223

12001224
foreach(l, quals)
@@ -1205,31 +1229,32 @@ cost_qual_eval(List *quals)
12051229
* RestrictInfo nodes contain an eval_cost field reserved for this
12061230
* routine's use, so that it's not necessary to evaluate the qual
12071231
* clause's cost more than once. If the clause's cost hasn't been
1208-
* computed yet, the field will contain -1.
1232+
* computed yet, the field's startup value will contain -1.
12091233
*/
12101234
if (qual && IsA(qual, RestrictInfo))
12111235
{
12121236
RestrictInfo *restrictinfo = (RestrictInfo *) qual;
12131237

1214-
if (restrictinfo->eval_cost < 0)
1238+
if (restrictinfo->eval_cost.startup < 0)
12151239
{
1216-
restrictinfo->eval_cost = 0;
1240+
restrictinfo->eval_cost.startup = 0;
1241+
restrictinfo->eval_cost.per_tuple = 0;
12171242
cost_qual_eval_walker((Node *) restrictinfo->clause,
12181243
&restrictinfo->eval_cost);
12191244
}
1220-
total += restrictinfo->eval_cost;
1245+
cost->startup += restrictinfo->eval_cost.startup;
1246+
cost->per_tuple += restrictinfo->eval_cost.per_tuple;
12211247
}
12221248
else
12231249
{
12241250
/* If it's a bare expression, must always do it the hard way */
1225-
cost_qual_eval_walker(qual, &total);
1251+
cost_qual_eval_walker(qual, cost);
12261252
}
12271253
}
1228-
return total;
12291254
}
12301255

12311256
static bool
1232-
cost_qual_eval_walker(Node *node, Cost *total)
1257+
cost_qual_eval_walker(Node *node, QualCost *total)
12331258
{
12341259
if (node == NULL)
12351260
return false;
@@ -1246,43 +1271,96 @@ cost_qual_eval_walker(Node *node, Cost *total)
12461271
if (IsA(node, FuncExpr) ||
12471272
IsA(node, OpExpr) ||
12481273
IsA(node, DistinctExpr))
1249-
*total += cpu_operator_cost;
1274+
{
1275+
total->per_tuple += cpu_operator_cost;
1276+
}
1277+
else if (IsA(node, SubLink))
1278+
{
1279+
/* This routine should not be applied to un-planned expressions */
1280+
elog(ERROR, "cost_qual_eval: can't handle unplanned sub-select");
1281+
}
12501282
else if (IsA(node, SubPlan))
12511283
{
12521284
/*
1253-
* A subplan node in an expression indicates that the
1254-
* subplan will be executed on each evaluation, so charge
1255-
* accordingly. (We assume that sub-selects that can be
1256-
* executed as InitPlans have already been removed from
1257-
* the expression.)
1285+
* A subplan node in an expression typically indicates that the
1286+
* subplan will be executed on each evaluation, so charge accordingly.
1287+
* (Sub-selects that can be executed as InitPlans have already been
1288+
* removed from the expression.)
1289+
*
1290+
* An exception occurs when we have decided we can implement the
1291+
* subplan by hashing.
12581292
*
1259-
* NOTE: this logic should agree with the estimates used by
1260-
* make_subplan() in plan/subselect.c.
12611293
*/
12621294
SubPlan *subplan = (SubPlan *) node;
12631295
Plan *plan = subplan->plan;
1264-
Cost subcost;
12651296

1266-
if (subplan->subLinkType == EXISTS_SUBLINK)
1297+
if (subplan->useHashTable)
12671298
{
1268-
/* we only need to fetch 1 tuple */
1269-
subcost = plan->startup_cost +
1270-
(plan->total_cost - plan->startup_cost) / plan->plan_rows;
1271-
}
1272-
else if (subplan->subLinkType == ALL_SUBLINK ||
1273-
subplan->subLinkType == ANY_SUBLINK)
1274-
{
1275-
/* assume we need 50% of the tuples */
1276-
subcost = plan->startup_cost +
1277-
0.50 * (plan->total_cost - plan->startup_cost);
1278-
/* XXX what if subplan has been materialized? */
1299+
/*
1300+
* If we are using a hash table for the subquery outputs, then
1301+
* the cost of evaluating the query is a one-time cost.
1302+
* We charge one cpu_operator_cost per tuple for the work of
1303+
* loading the hashtable, too.
1304+
*/
1305+
total->startup += plan->total_cost +
1306+
cpu_operator_cost * plan->plan_rows;
1307+
/*
1308+
* The per-tuple costs include the cost of evaluating the
1309+
* lefthand expressions, plus the cost of probing the hashtable.
1310+
* Recursion into the exprs list will handle the lefthand
1311+
* expressions properly, and will count one cpu_operator_cost
1312+
* for each comparison operator. That is probably too low for
1313+
* the probing cost, but it's hard to make a better estimate,
1314+
* so live with it for now.
1315+
*/
12791316
}
12801317
else
12811318
{
1282-
/* assume we need all tuples */
1283-
subcost = plan->total_cost;
1319+
/*
1320+
* Otherwise we will be rescanning the subplan output on each
1321+
* evaluation. We need to estimate how much of the output
1322+
* we will actually need to scan. NOTE: this logic should
1323+
* agree with the estimates used by make_subplan() in
1324+
* plan/subselect.c.
1325+
*/
1326+
Cost plan_run_cost = plan->total_cost - plan->startup_cost;
1327+
1328+
if (subplan->subLinkType == EXISTS_SUBLINK)
1329+
{
1330+
/* we only need to fetch 1 tuple */
1331+
total->per_tuple += plan_run_cost / plan->plan_rows;
1332+
}
1333+
else if (subplan->subLinkType == ALL_SUBLINK ||
1334+
subplan->subLinkType == ANY_SUBLINK)
1335+
{
1336+
/* assume we need 50% of the tuples */
1337+
total->per_tuple += 0.50 * plan_run_cost;
1338+
/* also charge a cpu_operator_cost per row examined */
1339+
total->per_tuple += 0.50 * plan->plan_rows * cpu_operator_cost;
1340+
}
1341+
else
1342+
{
1343+
/* assume we need all tuples */
1344+
total->per_tuple += plan_run_cost;
1345+
}
1346+
/*
1347+
* Also account for subplan's startup cost.
1348+
* If the subplan is uncorrelated or undirect correlated,
1349+
* AND its topmost node is a Sort or Material node, assume
1350+
* that we'll only need to pay its startup cost once;
1351+
* otherwise assume we pay the startup cost every time.
1352+
*/
1353+
if (subplan->parParam == NIL &&
1354+
(IsA(plan, Sort) ||
1355+
IsA(plan, Material)))
1356+
{
1357+
total->startup += plan->startup_cost;
1358+
}
1359+
else
1360+
{
1361+
total->per_tuple += plan->startup_cost;
1362+
}
12841363
}
1285-
*total += subcost;
12861364
}
12871365

12881366
return expression_tree_walker(node, cost_qual_eval_walker,
@@ -1388,7 +1466,7 @@ set_baserel_size_estimates(Query *root, RelOptInfo *rel)
13881466

13891467
rel->rows = temp;
13901468

1391-
rel->baserestrictcost = cost_qual_eval(rel->baserestrictinfo);
1469+
cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo);
13921470

13931471
set_rel_width(root, rel);
13941472
}
@@ -1533,7 +1611,7 @@ set_function_size_estimates(Query *root, RelOptInfo *rel)
15331611

15341612
rel->rows = temp;
15351613

1536-
rel->baserestrictcost = cost_qual_eval(rel->baserestrictinfo);
1614+
cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo);
15371615

15381616
set_rel_width(root, rel);
15391617
}

src/backend/optimizer/plan/initsplan.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.79 2002/12/17 01:18:25 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.80 2003/01/12 22:35:29 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -396,7 +396,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
396396

397397
restrictinfo->clause = (Expr *) clause;
398398
restrictinfo->subclauseindices = NIL;
399-
restrictinfo->eval_cost = -1; /* not computed until needed */
399+
restrictinfo->eval_cost.startup = -1; /* not computed until needed */
400400
restrictinfo->this_selec = -1; /* not computed until needed */
401401
restrictinfo->mergejoinoperator = InvalidOid;
402402
restrictinfo->left_sortop = InvalidOid;

src/backend/optimizer/prep/prepunion.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*
1515
*
1616
* IDENTIFICATION
17-
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.84 2003/01/05 00:56:40 tgl Exp $
17+
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.85 2003/01/12 22:35:29 tgl Exp $
1818
*
1919
*-------------------------------------------------------------------------
2020
*/
@@ -853,7 +853,7 @@ adjust_inherited_attrs_mutator(Node *node,
853853
adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
854854

855855
newinfo->subclauseindices = NIL;
856-
newinfo->eval_cost = -1; /* reset these too */
856+
newinfo->eval_cost.startup = -1; /* reset these too */
857857
newinfo->this_selec = -1;
858858
newinfo->left_pathkey = NIL; /* and these */
859859
newinfo->right_pathkey = NIL;

0 commit comments

Comments
 (0)