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

Commit 8d9a28e

Browse files
committed
Use fuzzy comparison of path costs in add_path(), so that paths with the
same path keys and nearly equivalent costs will be considered redundant. The exact nature of the fuzziness may get adjusted later based on current discussions, but no one has shot a hole in the basic idea yet ...
1 parent 047a2ce commit 8d9a28e

File tree

1 file changed

+96
-8
lines changed

1 file changed

+96
-8
lines changed

src/backend/optimizer/util/pathnode.c

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.102 2004/03/02 16:42:20 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.103 2004/03/29 19:58:04 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -84,6 +84,76 @@ compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
8484
return 0;
8585
}
8686

87+
/*
88+
* compare_fuzzy_path_costs
89+
* Return -1, 0, or +1 according as path1 is cheaper, the same cost,
90+
* or more expensive than path2 for the specified criterion.
91+
*
92+
* This differs from compare_path_costs in that we consider the costs the
93+
* same if they agree to within a "fuzz factor". This is used by add_path
94+
* to avoid keeping both of a pair of paths that really have insignificantly
95+
* different cost.
96+
*/
97+
static int
98+
compare_fuzzy_path_costs(Path *path1, Path *path2, CostSelector criterion)
99+
{
100+
Cost fuzz;
101+
102+
/*
103+
* The fuzz factor is set at one percent of the smaller total_cost, but
104+
* not less than 0.01 cost units (just in case total cost is zero).
105+
*
106+
* XXX does this percentage need to be user-configurable?
107+
*/
108+
fuzz = Min(path1->total_cost, path2->total_cost) * 0.01;
109+
fuzz = Max(fuzz, 0.01);
110+
111+
if (criterion == STARTUP_COST)
112+
{
113+
if (Abs(path1->startup_cost - path2->startup_cost) > fuzz)
114+
{
115+
if (path1->startup_cost < path2->startup_cost)
116+
return -1;
117+
else
118+
return +1;
119+
}
120+
121+
/*
122+
* If paths have the same startup cost (not at all unlikely),
123+
* order them by total cost.
124+
*/
125+
if (Abs(path1->total_cost - path2->total_cost) > fuzz)
126+
{
127+
if (path1->total_cost < path2->total_cost)
128+
return -1;
129+
else
130+
return +1;
131+
}
132+
}
133+
else
134+
{
135+
if (Abs(path1->total_cost - path2->total_cost) > fuzz)
136+
{
137+
if (path1->total_cost < path2->total_cost)
138+
return -1;
139+
else
140+
return +1;
141+
}
142+
143+
/*
144+
* If paths have the same total cost, order them by startup cost.
145+
*/
146+
if (Abs(path1->startup_cost - path2->startup_cost) > fuzz)
147+
{
148+
if (path1->startup_cost < path2->startup_cost)
149+
return -1;
150+
else
151+
return +1;
152+
}
153+
}
154+
return 0;
155+
}
156+
87157
/*
88158
* compare_path_fractional_costs
89159
* Return -1, 0, or +1 according as path1 is cheaper, the same cost,
@@ -215,29 +285,47 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
215285
bool remove_old = false; /* unless new proves superior */
216286
int costcmp;
217287

218-
costcmp = compare_path_costs(new_path, old_path, TOTAL_COST);
288+
/*
289+
* As of Postgres 7.5, we use fuzzy cost comparison to avoid wasting
290+
* cycles keeping paths that are really not significantly different
291+
* in cost.
292+
*/
293+
costcmp = compare_fuzzy_path_costs(new_path, old_path, TOTAL_COST);
219294

220295
/*
221296
* If the two paths compare differently for startup and total
222297
* cost, then we want to keep both, and we can skip the (much
223298
* slower) comparison of pathkeys. If they compare the same,
224299
* proceed with the pathkeys comparison. Note: this test relies
225-
* on the fact that compare_path_costs will only return 0 if both
226-
* costs are equal (and, therefore, there's no need to call it
227-
* twice in that case).
300+
* on the fact that compare_fuzzy_path_costs will only return 0 if
301+
* both costs are effectively equal (and, therefore, there's no need
302+
* to call it twice in that case).
228303
*/
229304
if (costcmp == 0 ||
230-
costcmp == compare_path_costs(new_path, old_path,
231-
STARTUP_COST))
305+
costcmp == compare_fuzzy_path_costs(new_path, old_path,
306+
STARTUP_COST))
232307
{
233308
switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys))
234309
{
235310
case PATHKEYS_EQUAL:
236311
if (costcmp < 0)
237312
remove_old = true; /* new dominates old */
313+
else if (costcmp > 0)
314+
accept_new = false; /* old dominates new */
238315
else
239-
accept_new = false; /* old equals or dominates
316+
{
317+
/*
318+
* Same pathkeys, and fuzzily the same cost, so
319+
* keep just one --- but we'll do an exact cost
320+
* comparison to decide which.
321+
*/
322+
if (compare_path_costs(new_path, old_path,
323+
TOTAL_COST) < 0)
324+
remove_old = true; /* new dominates old */
325+
else
326+
accept_new = false; /* old equals or dominates
240327
* new */
328+
}
241329
break;
242330
case PATHKEYS_BETTER1:
243331
if (costcmp <= 0)

0 commit comments

Comments
 (0)