Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitda1c916

Browse files
committed
Speed up planner's scanning for parallel-query hazards.
We need to scan the whole parse tree for parallel-unsafe functions.If there are none, we'll later need to determine whether particularsubtrees contain any parallel-restricted functions. The previous codingretained no knowledge from the first scan, even though this is verywasteful in the common case where the query contains only parallel-safefunctions. We can bypass all of the later scans by remembering that fact.This provides a small but measurable speed improvement when the caseapplies, and shouldn't cost anything when it doesn't.Patch by me, reviewed by Robert HaasDiscussion: <3740.1471538387@sss.pgh.pa.us>
1 parent6f79ae7 commitda1c916

File tree

9 files changed

+133
-80
lines changed

9 files changed

+133
-80
lines changed

‎src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,6 +2029,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node)
20292029
WRITE_BOOL_FIELD(dependsOnRole);
20302030
WRITE_BOOL_FIELD(parallelModeOK);
20312031
WRITE_BOOL_FIELD(parallelModeNeeded);
2032+
WRITE_CHAR_FIELD(maxParallelHazard);
20322033
}
20332034

20342035
staticvoid

‎src/backend/optimizer/path/allpaths.c

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ static void set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel,
7878
staticvoidcreate_plain_partial_paths(PlannerInfo*root,RelOptInfo*rel);
7979
staticvoidset_rel_consider_parallel(PlannerInfo*root,RelOptInfo*rel,
8080
RangeTblEntry*rte);
81-
staticboolfunction_rte_parallel_ok(RangeTblEntry*rte);
8281
staticvoidset_plain_rel_pathlist(PlannerInfo*root,RelOptInfo*rel,
8382
RangeTblEntry*rte);
8483
staticvoidset_tablesample_rel_size(PlannerInfo*root,RelOptInfo*rel,
@@ -542,8 +541,7 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
542541

543542
if (proparallel!=PROPARALLEL_SAFE)
544543
return;
545-
if (has_parallel_hazard((Node*)rte->tablesample->args,
546-
false))
544+
if (!is_parallel_safe(root, (Node*)rte->tablesample->args))
547545
return;
548546
}
549547

@@ -596,7 +594,7 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
596594

597595
caseRTE_FUNCTION:
598596
/* Check for parallel-restricted functions. */
599-
if (!function_rte_parallel_ok(rte))
597+
if (!is_parallel_safe(root, (Node*)rte->functions))
600598
return;
601599
break;
602600

@@ -629,40 +627,20 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
629627
* outer join clauses work correctly. It would likely break equivalence
630628
* classes, too.
631629
*/
632-
if (has_parallel_hazard((Node*)rel->baserestrictinfo, false))
630+
if (!is_parallel_safe(root,(Node*)rel->baserestrictinfo))
633631
return;
634632

635633
/*
636634
* Likewise, if the relation's outputs are not parallel-safe, give up.
637635
* (Usually, they're just Vars, but sometimes they're not.)
638636
*/
639-
if (has_parallel_hazard((Node*)rel->reltarget->exprs, false))
637+
if (!is_parallel_safe(root,(Node*)rel->reltarget->exprs))
640638
return;
641639

642640
/* We have a winner. */
643641
rel->consider_parallel= true;
644642
}
645643

646-
/*
647-
* Check whether a function RTE is scanning something parallel-restricted.
648-
*/
649-
staticbool
650-
function_rte_parallel_ok(RangeTblEntry*rte)
651-
{
652-
ListCell*lc;
653-
654-
foreach(lc,rte->functions)
655-
{
656-
RangeTblFunction*rtfunc= (RangeTblFunction*)lfirst(lc);
657-
658-
Assert(IsA(rtfunc,RangeTblFunction));
659-
if (has_parallel_hazard(rtfunc->funcexpr, false))
660-
return false;
661-
}
662-
663-
return true;
664-
}
665-
666644
/*
667645
* set_plain_rel_pathlist
668646
* Build access paths for a plain relation (no subquery, no inheritance)

‎src/backend/optimizer/plan/planmain.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,13 @@ query_planner(PlannerInfo *root, List *tlist,
7171

7272
/*
7373
* If query allows parallelism in general, check whether the quals are
74-
* parallel-restricted. There's currently no real benefit to setting
75-
* this flag correctly because we can't yet reference subplans from
76-
* parallel workers. But that might change someday, so set this
77-
* correctly anyway.
74+
* parallel-restricted. (We need not check final_rel->reltarget
75+
* because it's empty at this point. Anything parallel-restricted in
76+
* the query tlist will be dealt with later.)
7877
*/
7978
if (root->glob->parallelModeOK)
8079
final_rel->consider_parallel=
81-
!has_parallel_hazard(parse->jointree->quals, false);
80+
is_parallel_safe(root,parse->jointree->quals);
8281

8382
/* The only path for it is a trivial Result path */
8483
add_path(final_rel, (Path*)

‎src/backend/optimizer/plan/planner.c

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include"access/sysattr.h"
2424
#include"access/xact.h"
2525
#include"catalog/pg_constraint_fn.h"
26+
#include"catalog/pg_proc.h"
2627
#include"catalog/pg_type.h"
2728
#include"executor/executor.h"
2829
#include"executor/nodeAgg.h"
@@ -241,12 +242,26 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
241242
* time and execution time, so don't generate a parallel plan if we're in
242243
* serializable mode.
243244
*/
244-
glob->parallelModeOK= (cursorOptions&CURSOR_OPT_PARALLEL_OK)!=0&&
245-
IsUnderPostmaster&&dynamic_shared_memory_type!=DSM_IMPL_NONE&&
246-
parse->commandType==CMD_SELECT&& !parse->hasModifyingCTE&&
247-
parse->utilityStmt==NULL&&max_parallel_workers_per_gather>0&&
248-
!IsParallelWorker()&& !IsolationIsSerializable()&&
249-
!has_parallel_hazard((Node*)parse, true);
245+
if ((cursorOptions&CURSOR_OPT_PARALLEL_OK)!=0&&
246+
IsUnderPostmaster&&
247+
dynamic_shared_memory_type!=DSM_IMPL_NONE&&
248+
parse->commandType==CMD_SELECT&&
249+
parse->utilityStmt==NULL&&
250+
!parse->hasModifyingCTE&&
251+
max_parallel_workers_per_gather>0&&
252+
!IsParallelWorker()&&
253+
!IsolationIsSerializable())
254+
{
255+
/* all the cheap tests pass, so scan the query tree */
256+
glob->maxParallelHazard=max_parallel_hazard(parse);
257+
glob->parallelModeOK= (glob->maxParallelHazard!=PROPARALLEL_UNSAFE);
258+
}
259+
else
260+
{
261+
/* skip the query tree scan, just assume it's unsafe */
262+
glob->maxParallelHazard=PROPARALLEL_UNSAFE;
263+
glob->parallelModeOK= false;
264+
}
250265

251266
/*
252267
* glob->parallelModeNeeded should tell us whether it's necessary to
@@ -1802,7 +1817,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
18021817
* computed by partial paths.
18031818
*/
18041819
if (current_rel->partial_pathlist&&
1805-
!has_parallel_hazard((Node*)scanjoin_target->exprs, false))
1820+
is_parallel_safe(root,(Node*)scanjoin_target->exprs))
18061821
{
18071822
/* Apply the scan/join target to each partial path */
18081823
foreach(lc,current_rel->partial_pathlist)
@@ -1948,8 +1963,8 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
19481963
* query.
19491964
*/
19501965
if (current_rel->consider_parallel&&
1951-
!has_parallel_hazard(parse->limitOffset, false)&&
1952-
!has_parallel_hazard(parse->limitCount, false))
1966+
is_parallel_safe(root,parse->limitOffset)&&
1967+
is_parallel_safe(root,parse->limitCount))
19531968
final_rel->consider_parallel= true;
19541969

19551970
/*
@@ -3326,8 +3341,8 @@ create_grouping_paths(PlannerInfo *root,
33263341
* target list and HAVING quals are parallel-safe.
33273342
*/
33283343
if (input_rel->consider_parallel&&
3329-
!has_parallel_hazard((Node*)target->exprs, false)&&
3330-
!has_parallel_hazard((Node*)parse->havingQual, false))
3344+
is_parallel_safe(root,(Node*)target->exprs)&&
3345+
is_parallel_safe(root,(Node*)parse->havingQual))
33313346
grouped_rel->consider_parallel= true;
33323347

33333348
/*
@@ -3881,8 +3896,8 @@ create_window_paths(PlannerInfo *root,
38813896
* target list and active windows for non-parallel-safe constructs.
38823897
*/
38833898
if (input_rel->consider_parallel&&
3884-
!has_parallel_hazard((Node*)output_target->exprs, false)&&
3885-
!has_parallel_hazard((Node*)activeWindows, false))
3899+
is_parallel_safe(root,(Node*)output_target->exprs)&&
3900+
is_parallel_safe(root,(Node*)activeWindows))
38863901
window_rel->consider_parallel= true;
38873902

38883903
/*
@@ -4272,7 +4287,7 @@ create_ordered_paths(PlannerInfo *root,
42724287
* target list is parallel-safe.
42734288
*/
42744289
if (input_rel->consider_parallel&&
4275-
!has_parallel_hazard((Node*)target->exprs, false))
4290+
is_parallel_safe(root,(Node*)target->exprs))
42764291
ordered_rel->consider_parallel= true;
42774292

42784293
/*

‎src/backend/optimizer/util/clauses.c

Lines changed: 86 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,9 @@ typedef struct
9191

9292
typedefstruct
9393
{
94-
boolallow_restricted;
95-
}has_parallel_hazard_arg;
94+
charmax_hazard;/* worst proparallel hazard found so far */
95+
charmax_interesting;/* worst proparallel hazard of interest */
96+
}max_parallel_hazard_context;
9697

9798
staticboolcontain_agg_clause_walker(Node*node,void*context);
9899
staticboolget_agg_clause_costs_walker(Node*node,
@@ -103,8 +104,8 @@ static bool contain_subplans_walker(Node *node, void *context);
103104
staticboolcontain_mutable_functions_walker(Node*node,void*context);
104105
staticboolcontain_volatile_functions_walker(Node*node,void*context);
105106
staticboolcontain_volatile_functions_not_nextval_walker(Node*node,void*context);
106-
staticboolhas_parallel_hazard_walker(Node*node,
107-
has_parallel_hazard_arg*context);
107+
staticboolmax_parallel_hazard_walker(Node*node,
108+
max_parallel_hazard_context*context);
108109
staticboolcontain_nonstrict_functions_walker(Node*node,void*context);
109110
staticboolcontain_context_dependent_node(Node*clause);
110111
staticboolcontain_context_dependent_node_walker(Node*node,int*flags);
@@ -1100,46 +1101,98 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context)
11001101
context);
11011102
}
11021103

1104+
11031105
/*****************************************************************************
11041106
*Check queries for parallel unsafe and/or restricted constructs
11051107
*****************************************************************************/
11061108

11071109
/*
1108-
* Check whether a node tree contains parallel hazards. This is used both on
1109-
* the entire query tree, to see whether the query can be parallelized at all
1110-
* (with allow_restricted = true), and also to evaluate whether a particular
1111-
* expression is safe to run within a parallel worker (with allow_restricted =
1112-
* false). We could separate these concerns into two different functions, but
1113-
* there's enough overlap that it doesn't seem worthwhile.
1110+
* max_parallel_hazard
1111+
*Find the worst parallel-hazard level in the given query
1112+
*
1113+
* Returns the worst function hazard property (the earliest in this list:
1114+
* PROPARALLEL_UNSAFE, PROPARALLEL_RESTRICTED, PROPARALLEL_SAFE) that can
1115+
* be found in the given parsetree. We use this to find out whether the query
1116+
* can be parallelized at all. The caller will also save the result in
1117+
* PlannerGlobal so as to short-circuit checks of portions of the querytree
1118+
* later, in the common case where everything is SAFE.
1119+
*/
1120+
char
1121+
max_parallel_hazard(Query*parse)
1122+
{
1123+
max_parallel_hazard_contextcontext;
1124+
1125+
context.max_hazard=PROPARALLEL_SAFE;
1126+
context.max_interesting=PROPARALLEL_UNSAFE;
1127+
(void)max_parallel_hazard_walker((Node*)parse,&context);
1128+
returncontext.max_hazard;
1129+
}
1130+
1131+
/*
1132+
* is_parallel_safe
1133+
*Detect whether the given expr contains only parallel-safe functions
1134+
*
1135+
* root->glob->maxParallelHazard must previously have been set to the
1136+
* result of max_parallel_hazard() on the whole query.
11141137
*/
11151138
bool
1116-
has_parallel_hazard(Node*node,boolallow_restricted)
1139+
is_parallel_safe(PlannerInfo*root,Node*node)
11171140
{
1118-
has_parallel_hazard_argcontext;
1141+
max_parallel_hazard_contextcontext;
11191142

1120-
context.allow_restricted=allow_restricted;
1121-
returnhas_parallel_hazard_walker(node,&context);
1143+
/* If max_parallel_hazard found nothing unsafe, we don't need to look */
1144+
if (root->glob->maxParallelHazard==PROPARALLEL_SAFE)
1145+
return true;
1146+
/* Else use max_parallel_hazard's search logic, but stop on RESTRICTED */
1147+
context.max_hazard=PROPARALLEL_SAFE;
1148+
context.max_interesting=PROPARALLEL_RESTRICTED;
1149+
return !max_parallel_hazard_walker(node,&context);
11221150
}
11231151

1152+
/* core logic for all parallel-hazard checks */
11241153
staticbool
1125-
has_parallel_hazard_checker(Oidfunc_id,void*context)
1154+
max_parallel_hazard_test(charproparallel,max_parallel_hazard_context*context)
11261155
{
1127-
charproparallel=func_parallel(func_id);
1156+
switch (proparallel)
1157+
{
1158+
casePROPARALLEL_SAFE:
1159+
/* nothing to see here, move along */
1160+
break;
1161+
casePROPARALLEL_RESTRICTED:
1162+
/* increase max_hazard to RESTRICTED */
1163+
Assert(context->max_hazard!=PROPARALLEL_UNSAFE);
1164+
context->max_hazard=proparallel;
1165+
/* done if we are not expecting any unsafe functions */
1166+
if (context->max_interesting==proparallel)
1167+
return true;
1168+
break;
1169+
casePROPARALLEL_UNSAFE:
1170+
context->max_hazard=proparallel;
1171+
/* we're always done at the first unsafe construct */
1172+
return true;
1173+
default:
1174+
elog(ERROR,"unrecognized proparallel value \"%c\"",proparallel);
1175+
break;
1176+
}
1177+
return false;
1178+
}
11281179

1129-
if (((has_parallel_hazard_arg*)context)->allow_restricted)
1130-
return (proparallel==PROPARALLEL_UNSAFE);
1131-
else
1132-
return (proparallel!=PROPARALLEL_SAFE);
1180+
/* check_functions_in_node callback */
1181+
staticbool
1182+
max_parallel_hazard_checker(Oidfunc_id,void*context)
1183+
{
1184+
returnmax_parallel_hazard_test(func_parallel(func_id),
1185+
(max_parallel_hazard_context*)context);
11331186
}
11341187

11351188
staticbool
1136-
has_parallel_hazard_walker(Node*node,has_parallel_hazard_arg*context)
1189+
max_parallel_hazard_walker(Node*node,max_parallel_hazard_context*context)
11371190
{
11381191
if (node==NULL)
11391192
return false;
11401193

11411194
/* Check for hazardous functions in node itself */
1142-
if (check_functions_in_node(node,has_parallel_hazard_checker,
1195+
if (check_functions_in_node(node,max_parallel_hazard_checker,
11431196
context))
11441197
return true;
11451198

@@ -1156,7 +1209,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
11561209
*/
11571210
if (IsA(node,CoerceToDomain))
11581211
{
1159-
if (!context->allow_restricted)
1212+
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED,context))
11601213
return true;
11611214
}
11621215

@@ -1167,7 +1220,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
11671220
{
11681221
RestrictInfo*rinfo= (RestrictInfo*)node;
11691222

1170-
returnhas_parallel_hazard_walker((Node*)rinfo->clause,context);
1223+
returnmax_parallel_hazard_walker((Node*)rinfo->clause,context);
11711224
}
11721225

11731226
/*
@@ -1176,13 +1229,13 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
11761229
* not worry about examining their contents; if they are unsafe, we would
11771230
* have found that out while examining the whole tree before reduction of
11781231
* sublinks to subplans. (Really we should not see SubLink during a
1179-
*not-allow_restricted scan, but if we do, return true.)
1232+
*max_interesting == restricted scan, but if we do, return true.)
11801233
*/
11811234
elseif (IsA(node,SubLink)||
11821235
IsA(node,SubPlan)||
11831236
IsA(node,AlternativeSubPlan))
11841237
{
1185-
if (!context->allow_restricted)
1238+
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED,context))
11861239
return true;
11871240
}
11881241

@@ -1192,7 +1245,7 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
11921245
*/
11931246
elseif (IsA(node,Param))
11941247
{
1195-
if (!context->allow_restricted)
1248+
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED,context))
11961249
return true;
11971250
}
11981251

@@ -1207,20 +1260,24 @@ has_parallel_hazard_walker(Node *node, has_parallel_hazard_arg *context)
12071260

12081261
/* SELECT FOR UPDATE/SHARE must be treated as unsafe */
12091262
if (query->rowMarks!=NULL)
1263+
{
1264+
context->max_hazard=PROPARALLEL_UNSAFE;
12101265
return true;
1266+
}
12111267

12121268
/* Recurse into subselects */
12131269
returnquery_tree_walker(query,
1214-
has_parallel_hazard_walker,
1270+
max_parallel_hazard_walker,
12151271
context,0);
12161272
}
12171273

12181274
/* Recurse to check arguments */
12191275
returnexpression_tree_walker(node,
1220-
has_parallel_hazard_walker,
1276+
max_parallel_hazard_walker,
12211277
context);
12221278
}
12231279

1280+
12241281
/*****************************************************************************
12251282
*Check clauses for nonstrict functions
12261283
*****************************************************************************/

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp