Skip to content

Commit 3dd4f3a

Browse files
danolivoclaude
authored andcommitted
Make the optimiser aware of the parallel temp scan
Consider the extra cost of flushing temporary tables in partial path comparisons. With this commit, the optimiser gains a rationale for cost-based decision on enabling the parallel scan of subtrees that include temporary tables. It is achieved by adding to the path comparison routine an extra 'flush buffers' weighting factor. It is trivial to calculate the cost by tracking the number of dirtied temporary buffers and multiplying it by the write_page_cost parameter. The functions compare_path_costs and compare_fractional_path_costs were modified to account for this additional factor.
1 parent 4259f9e commit 3dd4f3a

File tree

6 files changed

+66
-8
lines changed

6 files changed

+66
-8
lines changed

src/backend/optimizer/path/costsize.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
#include "optimizer/plancat.h"
105105
#include "optimizer/restrictinfo.h"
106106
#include "parser/parsetree.h"
107+
#include "storage/bufmgr.h"
107108
#include "utils/lsyscache.h"
108109
#include "utils/selfuncs.h"
109110
#include "utils/spccache.h"
@@ -129,6 +130,7 @@
129130

130131
double seq_page_cost = DEFAULT_SEQ_PAGE_COST;
131132
double random_page_cost = DEFAULT_RANDOM_PAGE_COST;
133+
double write_page_cost = DEFAULT_WRITE_PAGE_COST;
132134
double cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
133135
double cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST;
134136
double cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST;
@@ -6655,3 +6657,25 @@ compute_gather_rows(Path *path)
66556657

66566658
return clamp_row_est(path->rows * get_parallel_divisor(path));
66576659
}
6660+
6661+
/*
6662+
* Before the launch parallel workers in a SELECT query, the leader process must
6663+
* flush all dirty pages in temp buffers to guarantee equal access to the data
6664+
* in each parallel worker.
6665+
* It seems difficult to calculate specific set of tables, indexes and toasts
6666+
* that may be touched inside the subtree. Moreover, stored procedures may also
6667+
* scan temporary tables. So, it makes sense to flush all temporary buffers.
6668+
* Here we calculate the cost of such operation to allow small queries do not
6669+
* activate expensive parallel scan over temp resources.
6670+
*/
6671+
Cost
6672+
tempbuf_flush_extra_cost()
6673+
{
6674+
if (!extended_parallel_processing)
6675+
/* Fast exit if feature is disabled */
6676+
return 0.0;
6677+
6678+
/* Hopefully, we have an statistics on the number of dirtied buffers */
6679+
Assert(dirtied_localbufs >= 0);
6680+
return write_page_cost * dirtied_localbufs;
6681+
}

src/backend/optimizer/util/pathnode.c

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ static bool pathlist_is_reparameterizable_by_child(List *pathlist,
6969
int
7070
compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
7171
{
72+
Cost startup_cost1 = path1->startup_cost;
73+
Cost startup_cost2 = path2->startup_cost;
74+
Cost total_cost1 = path1->total_cost;
75+
Cost total_cost2 = path2->total_cost;
76+
Cost extra_cost = tempbuf_flush_extra_cost();
77+
7278
/* Number of disabled nodes, if different, trumps all else. */
7379
if (unlikely(path1->disabled_nodes != path2->disabled_nodes))
7480
{
@@ -78,35 +84,50 @@ compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
7884
return +1;
7985
}
8086

87+
/*
88+
* Add an extra cost of temporary buffers flushing fofr the time
89+
* of comparison only.
90+
*/
91+
if (path1->parallel_safe == NEEDS_TEMP_FLUSH)
92+
{
93+
startup_cost1 += extra_cost;
94+
total_cost1 += extra_cost;
95+
}
96+
if (path2->parallel_safe == NEEDS_TEMP_FLUSH)
97+
{
98+
startup_cost2 += extra_cost;
99+
total_cost2 += extra_cost;
100+
}
101+
81102
if (criterion == STARTUP_COST)
82103
{
83-
if (path1->startup_cost < path2->startup_cost)
104+
if (startup_cost1 < startup_cost2)
84105
return -1;
85-
if (path1->startup_cost > path2->startup_cost)
106+
if (startup_cost1 > startup_cost2)
86107
return +1;
87108

88109
/*
89110
* If paths have the same startup cost (not at all unlikely), order
90111
* them by total cost.
91112
*/
92-
if (path1->total_cost < path2->total_cost)
113+
if (total_cost1 < total_cost2)
93114
return -1;
94-
if (path1->total_cost > path2->total_cost)
115+
if (total_cost1 > total_cost2)
95116
return +1;
96117
}
97118
else
98119
{
99-
if (path1->total_cost < path2->total_cost)
120+
if (total_cost1 < total_cost2)
100121
return -1;
101-
if (path1->total_cost > path2->total_cost)
122+
if (total_cost1 > total_cost2)
102123
return +1;
103124

104125
/*
105126
* If paths have the same total cost, order them by startup cost.
106127
*/
107-
if (path1->startup_cost < path2->startup_cost)
128+
if (startup_cost1 < startup_cost2)
108129
return -1;
109-
if (path1->startup_cost > path2->startup_cost)
130+
if (startup_cost1 > startup_cost2)
110131
return +1;
111132
}
112133
return 0;

src/backend/utils/misc/guc_parameters.dat

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3502,6 +3502,15 @@
35023502
max => 'MAX_KILOBYTES',
35033503
},
35043504

3505+
3506+
{ name => 'write_page_cost', type => 'real', context => 'PGC_USERSET', group => 'QUERY_TUNING_COST',
3507+
short_desc => 'Sets the planner\'s estimate of the cost of a disk page flushing.',
3508+
flags => 'GUC_EXPLAIN',
3509+
variable => 'write_page_cost',
3510+
boot_val => 'DEFAULT_WRITE_PAGE_COST',
3511+
min => '0',
3512+
max => 'DBL_MAX',
3513+
},
35053514
{ name => 'xmlbinary', type => 'enum', context => 'PGC_USERSET', group => 'CLIENT_CONN_STATEMENT',
35063515
short_desc => 'Sets how binary values are to be encoded in XML.',
35073516
variable => 'xmlbinary',

src/backend/utils/misc/postgresql.conf.sample

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@
435435

436436
#seq_page_cost = 1.0 # measured on an arbitrary scale
437437
#random_page_cost = 4.0 # same scale as above
438+
#write_page_cost = 5.0 # same scale as above
438439
#cpu_tuple_cost = 0.01 # same scale as above
439440
#cpu_index_tuple_cost = 0.005 # same scale as above
440441
#cpu_operator_cost = 0.0025 # same scale as above

src/include/optimizer/cost.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
/* If you change these, update backend/utils/misc/postgresql.conf.sample */
2424
#define DEFAULT_SEQ_PAGE_COST 1.0
2525
#define DEFAULT_RANDOM_PAGE_COST 4.0
26+
#define DEFAULT_WRITE_PAGE_COST 5.0 /* Make it a little more than random read. */
2627
#define DEFAULT_CPU_TUPLE_COST 0.01
2728
#define DEFAULT_CPU_INDEX_TUPLE_COST 0.005
2829
#define DEFAULT_CPU_OPERATOR_COST 0.0025
@@ -222,5 +223,6 @@ extern double compute_bitmap_pages(PlannerInfo *root, RelOptInfo *baserel,
222223
Path *bitmapqual, double loop_count,
223224
Cost *cost_p, double *tuples_p);
224225
extern double compute_gather_rows(Path *path);
226+
extern Cost tempbuf_flush_extra_cost(void);
225227

226228
#endif /* COST_H */

src/include/optimizer/optimizer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ extern Selectivity clauselist_selectivity_ext(PlannerInfo *root,
7272
/* widely used cost parameters */
7373
extern PGDLLIMPORT double seq_page_cost;
7474
extern PGDLLIMPORT double random_page_cost;
75+
extern PGDLLIMPORT double write_page_cost;
7576
extern PGDLLIMPORT double cpu_tuple_cost;
7677
extern PGDLLIMPORT double cpu_index_tuple_cost;
7778
extern PGDLLIMPORT double cpu_operator_cost;

0 commit comments

Comments
 (0)