Skip to content

Commit 1e23a29

Browse files
committed
Bug#17234723: Shortcut execution path in JOIN::optimize()
This is a partial fix for bug no. 68825/16598320: Performance regressions for single-threaded workloads. This patch shortcuts processing for some special cases, by skipping some functions calls. Shortcuts are made based on the following conditions: - plan_is_const() - materialized_table_count == 0 - partitioned_table_count == 0 - outer_join == 0 - select_lex->sj_nests.elements == 0 - unit->item == 0 (query block is not a subquery) Some loops are also made into functions, so that branching in case the function is skipped is minimized, thus avoiding cache misses. The gain is up to 5% on a simple synthetic benchmark (see benchmark BM-1 in bug 68825 for a description), but notice that the expected gain is platform-dependent.
1 parent 49dc042 commit 1e23a29

File tree

5 files changed

+170
-104
lines changed

5 files changed

+170
-104
lines changed

sql/sql_lex.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,12 @@ class st_select_lex: public st_select_lex_node
760760
/// Array of pointers to top elements of all_fields list
761761
Ref_ptr_array ref_pointer_array;
762762

763+
/// Number of derived tables and views
764+
uint derived_table_count;
765+
/// Number of materialized derived tables and views
766+
uint materialized_table_count;
767+
/// Number of partitioned tables
768+
uint partitioned_table_count;
763769
/*
764770
number of items in select_list and HAVING clause used to get number
765771
bigger then can be number of entries that will be added to all item

sql/sql_optimizer.cc

Lines changed: 120 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,6 @@ JOIN::optimize()
189189
}
190190
}
191191
#endif
192-
SELECT_LEX *sel= thd->lex->current_select;
193192
if (first_optimization)
194193
{
195194
/*
@@ -224,7 +223,7 @@ JOIN::optimize()
224223
st_select_lex::fix_prepare_information(), and remove this second copy
225224
below.
226225
*/
227-
sel->prep_where=
226+
select_lex->prep_where=
228227
conds ? conds->copy_andor_structure(thd, true) : NULL;
229228
}
230229

@@ -266,29 +265,11 @@ JOIN::optimize()
266265
}
267266

268267
#ifdef WITH_PARTITION_STORAGE_ENGINE
268+
if (select_lex->partitioned_table_count && prune_table_partitions(thd))
269269
{
270-
TABLE_LIST *tbl;
271-
for (tbl= select_lex->leaf_tables; tbl; tbl= tbl->next_leaf)
272-
{
273-
/*
274-
If tbl->embedding!=NULL that means that this table is in the inner
275-
part of the nested outer join, and we can't do partition pruning
276-
(TODO: check if this limitation can be lifted.
277-
This also excludes semi-joins. Is that intentional?)
278-
This will try to prune non-static conditions, which can
279-
be used after the tables are locked.
280-
*/
281-
if (!tbl->embedding)
282-
{
283-
Item *prune_cond= tbl->join_cond() ? tbl->join_cond() : conds;
284-
if (prune_partitions(thd, tbl->table, prune_cond))
285-
{
286-
error= 1;
287-
DBUG_PRINT("error", ("Error from prune_partitions"));
288-
DBUG_RETURN(1);
289-
}
290-
}
291-
}
270+
error= 1;
271+
DBUG_PRINT("error", ("Error from prune_partitions"));
272+
DBUG_RETURN(1);
292273
}
293274
#endif
294275

@@ -431,11 +412,14 @@ JOIN::optimize()
431412
}
432413

433414
error= 0;
434-
reset_nj_counters(join_list);
435-
make_outerjoin_info(this);
436-
415+
if (outer_join)
416+
{
417+
reset_nj_counters(join_list);
418+
make_outerjoin_info(this);
419+
}
437420
// Assign map of "available" tables to all tables belonging to query block
438-
set_prefix_tables();
421+
if (!plan_is_const())
422+
set_prefix_tables();
439423

440424
/*
441425
Among the equal fields belonging to the same multiple equality
@@ -485,7 +469,8 @@ JOIN::optimize()
485469
conds=new Item_int((longlong) 0,1); // Always false
486470
}
487471

488-
drop_unused_derived_keys();
472+
if (select_lex->materialized_table_count)
473+
drop_unused_derived_keys();
489474

490475
if (set_access_methods())
491476
{
@@ -796,7 +781,7 @@ JOIN::optimize()
796781
}
797782

798783
/* Cache constant expressions in WHERE, HAVING, ON clauses. */
799-
if (cache_const_exprs())
784+
if (!plan_is_const() && cache_const_exprs())
800785
DBUG_RETURN(1);
801786

802787
// See if this subquery can be evaluated with subselect_indexsubquery_engine
@@ -1051,6 +1036,45 @@ JOIN::optimize()
10511036
}
10521037

10531038

1039+
#ifdef WITH_PARTITION_STORAGE_ENGINE
1040+
1041+
/**
1042+
Prune partitions for all tables of a join (query block).
1043+
1044+
Requires that tables have been locked.
1045+
1046+
@param thd Thread pointer
1047+
1048+
@returns false if success, true if error
1049+
*/
1050+
bool JOIN::prune_table_partitions(THD *thd)
1051+
{
1052+
DBUG_ASSERT(select_lex->partitioned_table_count);
1053+
1054+
for (TABLE_LIST *tbl= select_lex->leaf_tables; tbl; tbl= tbl->next_leaf)
1055+
{
1056+
/*
1057+
If tbl->embedding!=NULL that means that this table is in the inner
1058+
part of the nested outer join, and we can't do partition pruning
1059+
(TODO: check if this limitation can be lifted.
1060+
This also excludes semi-joins. Is that intentional?)
1061+
This will try to prune non-static conditions, which can
1062+
be used after the tables are locked.
1063+
*/
1064+
if (!tbl->embedding)
1065+
{
1066+
if (prune_partitions(thd, tbl->table,
1067+
tbl->join_cond() ? tbl->join_cond() : conds))
1068+
return true;
1069+
}
1070+
}
1071+
1072+
return false;
1073+
}
1074+
1075+
#endif
1076+
1077+
10541078
/**
10551079
Set NESTED_JOIN::counter=0 in all nested joins in passed list.
10561080
@@ -3020,6 +3044,7 @@ bool JOIN::update_equalities_for_sjm()
30203044

30213045
void JOIN::set_prefix_tables()
30223046
{
3047+
DBUG_ASSERT(!plan_is_const());
30233048
/*
30243049
The const tables are available together with the first non-const table in
30253050
the join order.
@@ -3154,7 +3179,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, Item *conds,
31543179
DBUG_RETURN(true);
31553180

31563181
// Up to one extra slot per semi-join nest is needed (if materialized)
3157-
const uint sj_nests= join->select_lex->sj_nests.elements;
3182+
uint sj_nests= join->select_lex->sj_nests.elements;
31583183
if (!(join->best_positions=
31593184
new (thd->mem_root) POSITION[table_count + sj_nests + 1]))
31603185
DBUG_RETURN(true);
@@ -3310,10 +3335,11 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, Item *conds,
33103335
throughout the lifetime of a query, so this operation can be performed
33113336
on the first optimization only.
33123337
*/
3313-
if (first_optimization)
3338+
if (first_optimization && sj_nests)
33143339
{
33153340
if (pull_out_semijoin_tables(join))
33163341
DBUG_RETURN(true);
3342+
sj_nests= join->select_lex->sj_nests.elements;
33173343
}
33183344

33193345
/*
@@ -3720,39 +3746,19 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, Item *conds,
37203746
}
37213747
}
37223748

3723-
/*
3724-
Set pointer to embedding semi-join nest for all semi-joined tables.
3725-
Note that this must be done for every table inside all semi-join nests,
3726-
even for tables within outer join nests embedded in semi-join nests.
3727-
A table can never be part of multiple semi-join nests, hence no
3728-
ambiguities can ever occur.
3729-
Note also that the pointer is not set for TABLE_LIST objects that
3730-
are outer join nests within semi-join nests.
3731-
*/
3732-
for (s= stat; s < stat_end; s++)
3733-
{
3734-
for (TABLE_LIST *tables= s->table->pos_in_table_list;
3735-
tables->embedding;
3736-
tables= tables->embedding)
3737-
{
3738-
if (tables->embedding->sj_on_expr)
3739-
{
3740-
s->emb_sj_nest= tables->embedding;
3741-
break;
3742-
}
3743-
}
3744-
}
3745-
37463749
join->join_tab=stat;
37473750
join->map2table=stat_ref;
37483751
join->const_tables=const_count;
37493752

3753+
if (sj_nests)
3754+
join->set_semijoin_embedding();
3755+
37503756
if (!join->plan_is_const())
37513757
optimize_keyuse(join, keyuse_array);
37523758

37533759
join->allow_outer_refs= true;
37543760

3755-
if (optimize_semijoin_nests_for_materialization(join))
3761+
if (sj_nests && optimize_semijoin_nests_for_materialization(join))
37563762
DBUG_RETURN(true);
37573763

37583764
if (Optimize_table_order(thd, join, NULL).choose_table_order())
@@ -3762,7 +3768,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, Item *conds,
37623768
if (thd->killed || thd->is_error())
37633769
DBUG_RETURN(true);
37643770

3765-
if (join->decide_subquery_strategy())
3771+
if (join->unit->item && join->decide_subquery_strategy())
37663772
DBUG_RETURN(true);
37673773

37683774
join->refine_best_rowcount();
@@ -3806,6 +3812,40 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, Item *conds,
38063812
}
38073813

38083814

3815+
/**
3816+
Set semi-join embedding join nest pointers.
3817+
3818+
Set pointer to embedding semi-join nest for all semi-joined tables.
3819+
Note that this must be done for every table inside all semi-join nests,
3820+
even for tables within outer join nests embedded in semi-join nests.
3821+
A table can never be part of multiple semi-join nests, hence no
3822+
ambiguities can ever occur.
3823+
Note also that the pointer is not set for TABLE_LIST objects that
3824+
are outer join nests within semi-join nests.
3825+
*/
3826+
3827+
void JOIN::set_semijoin_embedding()
3828+
{
3829+
DBUG_ASSERT(!select_lex->sj_nests.is_empty());
3830+
3831+
JOIN_TAB *const tab_end= join_tab + primary_tables;
3832+
3833+
for (JOIN_TAB *tab= join_tab; tab < tab_end; tab++)
3834+
{
3835+
for (TABLE_LIST *tr= tab->table->pos_in_table_list;
3836+
tr->embedding;
3837+
tr= tr->embedding)
3838+
{
3839+
if (tr->embedding->sj_on_expr)
3840+
{
3841+
tab->emb_sj_nest= tr->embedding;
3842+
break;
3843+
}
3844+
}
3845+
}
3846+
}
3847+
3848+
38093849
/**
38103850
@brief Check if semijoin's compared types allow materialization.
38113851
@@ -4454,8 +4494,7 @@ static bool pull_out_semijoin_tables(JOIN *join)
44544494
TABLE_LIST *sj_nest;
44554495
DBUG_ENTER("pull_out_semijoin_tables");
44564496

4457-
if (join->select_lex->sj_nests.is_empty())
4458-
DBUG_RETURN(FALSE);
4497+
DBUG_ASSERT(!join->select_lex->sj_nests.is_empty());
44594498

44604499
List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests);
44614500
Opt_trace_context * const trace= &join->thd->opt_trace;
@@ -5864,20 +5903,22 @@ update_ref_and_keys(THD *thd, Key_use_array *keyuse,JOIN_TAB *join_tab,
58645903
}
58655904

58665905
/* Generate keys descriptions for derived tables */
5867-
if (select_lex->join->generate_derived_keys())
5868-
return TRUE;
5869-
5906+
if (select_lex->materialized_table_count)
5907+
{
5908+
if (select_lex->join->generate_derived_keys())
5909+
return true;
5910+
}
58705911
/* fill keyuse with found key parts */
58715912
for ( ; field != end ; field++)
58725913
{
58735914
if (add_key_part(keyuse,field))
5874-
return TRUE;
5915+
return true;
58755916
}
58765917

58775918
if (select_lex->ftfunc_list->elements)
58785919
{
58795920
if (add_ft_keys(keyuse,join_tab,cond,normal_tables))
5880-
return TRUE;
5921+
return true;
58815922
}
58825923

58835924
/*
@@ -5942,7 +5983,8 @@ update_ref_and_keys(THD *thd, Key_use_array *keyuse,JOIN_TAB *join_tab,
59425983
keyuse->chop(i);
59435984
}
59445985
print_keyuse_array(&thd->opt_trace, keyuse);
5945-
return FALSE;
5986+
5987+
return false;
59465988
}
59475989

59485990

@@ -6061,6 +6103,9 @@ static void
60616103
make_outerjoin_info(JOIN *join)
60626104
{
60636105
DBUG_ENTER("make_outerjoin_info");
6106+
6107+
DBUG_ASSERT(join->outer_join);
6108+
60646109
for (uint i= join->const_tables; i < join->tables; i++)
60656110
{
60666111
JOIN_TAB *const tab= join->join_tab + i;
@@ -6710,6 +6755,10 @@ static bool convert_subquery_to_semijoin(JOIN *parent_join,
67106755
parent_join->tables+= subq_lex->join->tables;
67116756
parent_join->primary_tables+= subq_lex->join->tables;
67126757

6758+
parent_lex->derived_table_count+= subq_lex->derived_table_count;
6759+
parent_lex->materialized_table_count+= subq_lex->materialized_table_count;
6760+
parent_lex->partitioned_table_count+= subq_lex->partitioned_table_count;
6761+
67136762
nested_join->sj_outer_exprs.empty();
67146763
nested_join->sj_inner_exprs.empty();
67156764

@@ -7120,6 +7169,8 @@ void JOIN::remove_subq_pushed_predicates(Item **where)
71207169

71217170
bool JOIN::generate_derived_keys()
71227171
{
7172+
DBUG_ASSERT(select_lex->materialized_table_count);
7173+
71237174
for (TABLE_LIST *table= select_lex->leaf_tables;
71247175
table;
71257176
table= table->next_leaf)
@@ -7146,6 +7197,8 @@ bool JOIN::generate_derived_keys()
71467197

71477198
void JOIN::drop_unused_derived_keys()
71487199
{
7200+
DBUG_ASSERT(select_lex->materialized_table_count);
7201+
71497202
for (uint i= 0 ; i < tables ; i++)
71507203
{
71517204
JOIN_TAB *tab= join_tab + i;
@@ -7203,8 +7256,7 @@ void JOIN::drop_unused_derived_keys()
72037256
bool JOIN::cache_const_exprs()
72047257
{
72057258
/* No need in cache if all tables are constant. */
7206-
if (plan_is_const())
7207-
return false;
7259+
DBUG_ASSERT(!plan_is_const());
72087260

72097261
for (uint i= const_tables; i < tables; i++)
72107262
{
@@ -9443,8 +9495,7 @@ static void calculate_materialization_costs(JOIN *join,
94439495
*/
94449496
bool JOIN::decide_subquery_strategy()
94459497
{
9446-
if (!unit->item)
9447-
return false;
9498+
DBUG_ASSERT(unit->item);
94489499

94499500
switch (unit->item->substype())
94509501
{

0 commit comments

Comments
 (0)