diff options
Diffstat (limited to 'src/backend/optimizer/path')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 94 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 38 |
2 files changed, 56 insertions, 76 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 0c564be7f66..0cfa3a1d6cd 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -3207,70 +3207,52 @@ generate_useful_gather_paths(PlannerInfo *root, RelOptInfo *rel, bool override_r continue; /* - * Consider regular sort for the cheapest partial path (for each - * useful pathkeys). We know the path is not sorted, because we'd - * not get here otherwise. + * Try at least sorting the cheapest path and also try + * incrementally sorting any path which is partially sorted + * already (no need to deal with paths which have presorted keys + * when incremental sort is disabled unless it's the cheapest + * input path). + */ + if (subpath != cheapest_partial_path && + (presorted_keys == 0 || !enable_incremental_sort)) + continue; + + /* + * Consider regular sort for any path that's not presorted or if + * incremental sort is disabled. We've no need to consider both + * sort and incremental sort on the same path. We assume that + * incremental sort is always faster when there are presorted + * keys. * * This is not redundant with the gather paths created in * generate_gather_paths, because that doesn't generate ordered * output. Here we add an explicit sort to match the useful * ordering. */ - if (cheapest_partial_path == subpath) + if (presorted_keys == 0 || !enable_incremental_sort) { - Path *tmp; - - tmp = (Path *) create_sort_path(root, - rel, - subpath, - useful_pathkeys, - -1.0); - - rows = tmp->rows * tmp->parallel_workers; - - path = create_gather_merge_path(root, rel, - tmp, - rel->reltarget, - tmp->pathkeys, - NULL, - rowsp); - - add_path(rel, &path->path); - - /* Fall through */ - } - - /* - * Consider incremental sort, but only when the subpath is already - * partially sorted on a pathkey prefix. - */ - if (enable_incremental_sort && presorted_keys > 0) - { - Path *tmp; - - /* - * We should have already excluded pathkeys of length 1 - * because then presorted_keys > 0 would imply is_sorted was - * true. - */ - Assert(list_length(useful_pathkeys) != 1); - - tmp = (Path *) create_incremental_sort_path(root, - rel, - subpath, - useful_pathkeys, - presorted_keys, - -1); - - path = create_gather_merge_path(root, rel, - tmp, - rel->reltarget, - tmp->pathkeys, - NULL, - rowsp); - - add_path(rel, &path->path); + subpath = (Path *) create_sort_path(root, + rel, + subpath, + useful_pathkeys, + -1.0); + rows = subpath->rows * subpath->parallel_workers; } + else + subpath = (Path *) create_incremental_sort_path(root, + rel, + subpath, + useful_pathkeys, + presorted_keys, + -1); + path = create_gather_merge_path(root, rel, + subpath, + rel->reltarget, + subpath->pathkeys, + NULL, + rowsp); + + add_path(rel, &path->path); } } } diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 897309d7ec4..0f0bcfb7e50 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -1959,8 +1959,8 @@ cost_incremental_sort(Path *path, double input_tuples, int width, Cost comparison_cost, int sort_mem, double limit_tuples) { - Cost startup_cost = 0, - run_cost = 0, + Cost startup_cost, + run_cost, input_run_cost = input_total_cost - input_startup_cost; double group_tuples, input_groups; @@ -1969,10 +1969,9 @@ cost_incremental_sort(Path *path, group_input_run_cost; List *presortedExprs = NIL; ListCell *l; - int i = 0; bool unknown_varno = false; - Assert(presorted_keys != 0); + Assert(presorted_keys > 0 && presorted_keys < list_length(pathkeys)); /* * We want to be sure the cost of a sort is never estimated as zero, even @@ -2025,12 +2024,11 @@ cost_incremental_sort(Path *path, /* expression not containing any Vars with "varno 0" */ presortedExprs = lappend(presortedExprs, member->em_expr); - i++; - if (i >= presorted_keys) + if (foreach_current_index(l) + 1 >= presorted_keys) break; } - /* Estimate number of groups with equal presorted keys. */ + /* Estimate the number of groups with equal presorted keys. */ if (!unknown_varno) input_groups = estimate_num_groups(root, presortedExprs, input_tuples, NULL, NULL); @@ -2039,22 +2037,19 @@ cost_incremental_sort(Path *path, group_input_run_cost = input_run_cost / input_groups; /* - * Estimate average cost of sorting of one group where presorted keys are - * equal. Incremental sort is sensitive to distribution of tuples to the - * groups, where we're relying on quite rough assumptions. Thus, we're - * pessimistic about incremental sort performance and increase its average - * group size by half. + * Estimate the average cost of sorting of one group where presorted keys + * are equal. */ cost_tuplesort(&group_startup_cost, &group_run_cost, - 1.5 * group_tuples, width, comparison_cost, sort_mem, + group_tuples, width, comparison_cost, sort_mem, limit_tuples); /* * Startup cost of incremental sort is the startup cost of its first group * plus the cost of its input. */ - startup_cost += group_startup_cost - + input_startup_cost + group_input_run_cost; + startup_cost = group_startup_cost + input_startup_cost + + group_input_run_cost; /* * After we started producing tuples from the first group, the cost of @@ -2062,17 +2057,20 @@ cost_incremental_sort(Path *path, * group, plus the total cost to process the remaining groups, plus the * remaining cost of input. */ - run_cost += group_run_cost - + (group_run_cost + group_startup_cost) * (input_groups - 1) - + group_input_run_cost * (input_groups - 1); + run_cost = group_run_cost + (group_run_cost + group_startup_cost) * + (input_groups - 1) + group_input_run_cost * (input_groups - 1); /* * Incremental sort adds some overhead by itself. Firstly, it has to * detect the sort groups. This is roughly equal to one extra copy and - * comparison per tuple. Secondly, it has to reset the tuplesort context - * for every group. + * comparison per tuple. */ run_cost += (cpu_tuple_cost + comparison_cost) * input_tuples; + + /* + * Additionally, we charge double cpu_tuple_cost for each input group to + * account for the tuplesort_reset that's performed after each group. + */ run_cost += 2.0 * cpu_tuple_cost * input_groups; path->rows = input_tuples; |