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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy