|
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 | return0;
|
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 | +staticint |
| 98 | +compare_fuzzy_path_costs(Path*path1,Path*path2,CostSelectorcriterion) |
| 99 | +{ |
| 100 | +Costfuzz; |
| 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 | +return0; |
| 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 | boolremove_old= false;/* unless new proves superior */
|
216 | 286 | intcostcmp;
|
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 thatcompare_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 thatcompare_fuzzy_path_costs will only return 0 if |
| 301 | + *bothcosts areeffectivelyequal (and, therefore, there's no need |
| 302 | + *to call ittwice 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 | casePATHKEYS_EQUAL:
|
236 | 311 | if (costcmp<0)
|
237 | 312 | remove_old= true;/* new dominates old */
|
| 313 | +elseif (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 | casePATHKEYS_BETTER1:
|
243 | 331 | if (costcmp <=0)
|
|