|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * 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 $ |
12 | 12 | *
|
13 | 13 | *-------------------------------------------------------------------------
|
14 | 14 | */
|
@@ -84,6 +84,76 @@ compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
|
84 | 84 | return 0;
|
85 | 85 | }
|
86 | 86 |
|
| 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 | + |
87 | 157 | /*
|
88 | 158 | * compare_path_fractional_costs
|
89 | 159 | * 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)
|
215 | 285 | bool remove_old = false; /* unless new proves superior */
|
216 | 286 | int costcmp;
|
217 | 287 |
|
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); |
219 | 294 |
|
220 | 295 | /*
|
221 | 296 | * If the two paths compare differently for startup and total
|
222 | 297 | * cost, then we want to keep both, and we can skip the (much
|
223 | 298 | * slower) comparison of pathkeys. If they compare the same,
|
224 | 299 | * 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). |
228 | 303 | */
|
229 | 304 | 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)) |
232 | 307 | {
|
233 | 308 | switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys))
|
234 | 309 | {
|
235 | 310 | case PATHKEYS_EQUAL:
|
236 | 311 | if (costcmp < 0)
|
237 | 312 | remove_old = true; /* new dominates old */
|
| 313 | + else if (costcmp > 0) |
| 314 | + accept_new = false; /* old dominates new */ |
238 | 315 | 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 |
240 | 327 | * new */
|
| 328 | + } |
241 | 329 | break;
|
242 | 330 | case PATHKEYS_BETTER1:
|
243 | 331 | if (costcmp <= 0)
|
|
0 commit comments