@@ -10,9 +10,10 @@ use std::pin::Pin;
10
10
use std:: sync:: Arc ;
11
11
12
12
use anyhow:: Result ;
13
+ use itertools:: Itertools ;
13
14
use tracing:: trace;
14
15
15
- use super :: memo:: { ArcMemoPlanNode , GroupInfo , Memo } ;
16
+ use super :: memo:: { ArcMemoPlanNode , GroupInfo , Memo , WinnerInfo } ;
16
17
use super :: NaiveMemo ;
17
18
use crate :: cascades:: memo:: Winner ;
18
19
use crate :: cascades:: tasks2:: { TaskContext , TaskDesc } ;
@@ -38,16 +39,92 @@ pub struct OptimizerContext {
38
39
39
40
#[ derive( Default , Clone , Debug ) ]
40
41
pub struct OptimizerProperties {
42
+ /// Panic the optimizer if the budget is reached, used in planner tests.
41
43
pub panic_on_budget : bool ,
42
44
/// If the number of rules applied exceeds this number, we stop applying logical rules.
43
45
pub partial_explore_iter : Option < usize > ,
44
46
/// Plan space can be expanded by this number of times before we stop applying logical rules.
45
47
pub partial_explore_space : Option < usize > ,
46
48
/// Disable pruning during optimization.
47
49
pub disable_pruning : bool ,
50
+ /// Enable tracing during optimization.
51
+ pub enable_tracing : bool ,
48
52
}
49
53
50
- #[ derive( Debug , Default ) ]
54
+ #[ derive( Clone ) ]
55
+ pub enum OptimizerTrace {
56
+ /// A winner decision is made
57
+ DecideWinner {
58
+ /// The stage and step number when a trace is recorded
59
+ stage : usize ,
60
+ step : usize ,
61
+ /// The group ID when a trace is recorded
62
+ group_id : GroupId ,
63
+ /// The proposed winner
64
+ proposed_winner_info : WinnerInfo ,
65
+ /// The winner of the children
66
+ children_winner : Vec < ExprId > ,
67
+ } ,
68
+ /// The group is created by applying a rule
69
+ ApplyRule {
70
+ /// The step number when a trace is recorded
71
+ stage : usize ,
72
+ step : usize ,
73
+ /// The group ID when a trace is recorded
74
+ group_id : GroupId ,
75
+ /// The expression being applied
76
+ applied_expr_id : ExprId ,
77
+ /// The expression being produced
78
+ produced_expr_id : ExprId ,
79
+ /// The rule ID
80
+ rule_id : usize ,
81
+ } ,
82
+ }
83
+
84
+ impl OptimizerTrace {
85
+ pub fn stage_step ( & self ) -> ( usize , usize ) {
86
+ match self {
87
+ OptimizerTrace :: DecideWinner { stage, step, .. } => ( * stage, * step) ,
88
+ OptimizerTrace :: ApplyRule { stage, step, .. } => ( * stage, * step) ,
89
+ }
90
+ }
91
+ }
92
+
93
+ impl std:: fmt:: Display for OptimizerTrace {
94
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
95
+ match self {
96
+ OptimizerTrace :: DecideWinner {
97
+ stage,
98
+ step,
99
+ group_id,
100
+ proposed_winner_info,
101
+ children_winner,
102
+ } => {
103
+ write ! (
104
+ f,
105
+ "step={}/{} decide_winner group_id={} proposed_winner_expr={} children_winner_exprs=[{}] total_weighted_cost={}" ,
106
+ stage, step, group_id, proposed_winner_info. expr_id, children_winner. iter( ) . join( "," ) , proposed_winner_info. total_weighted_cost
107
+ )
108
+ }
109
+ OptimizerTrace :: ApplyRule {
110
+ stage,
111
+ step,
112
+ group_id,
113
+ applied_expr_id,
114
+ produced_expr_id,
115
+ rule_id,
116
+ } => {
117
+ write ! (
118
+ f,
119
+ "step={}/{} apply_rule group_id={} applied_expr_id={} produced_expr_id={} rule_id={}" ,
120
+ stage, step, group_id, applied_expr_id, produced_expr_id, rule_id
121
+ )
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ #[ derive( Default ) ]
51
128
pub struct CascadesStats {
52
129
pub rule_match_count : HashMap < usize , usize > ,
53
130
pub rule_total_bindings : HashMap < usize , usize > ,
@@ -56,6 +133,7 @@ pub struct CascadesStats {
56
133
pub optimize_expr_count : usize ,
57
134
pub apply_rule_count : usize ,
58
135
pub optimize_input_count : usize ,
136
+ pub trace : HashMap < GroupId , Vec < OptimizerTrace > > ,
59
137
}
60
138
61
139
pub struct CascadesOptimizer < T : NodeType , M : Memo < T > = NaiveMemo < T > > {
@@ -70,6 +148,7 @@ pub struct CascadesOptimizer<T: NodeType, M: Memo<T> = NaiveMemo<T>> {
70
148
logical_property_builders : Arc < [ Box < dyn LogicalPropertyBuilderAny < T > > ] > ,
71
149
pub ctx : OptimizerContext ,
72
150
pub prop : OptimizerProperties ,
151
+ stage : usize ,
73
152
}
74
153
75
154
/// `RelNode` only contains the representation of the plan nodes. Sometimes, we need more context,
@@ -137,6 +216,7 @@ impl<T: NodeType> CascadesOptimizer<T, NaiveMemo<T>> {
137
216
prop,
138
217
stats : CascadesStats :: default ( ) ,
139
218
disabled_rules : HashSet :: new ( ) ,
219
+ stage : 0 ,
140
220
}
141
221
}
142
222
@@ -163,14 +243,6 @@ impl<T: NodeType> CascadesOptimizer<T, NaiveMemo<T>> {
163
243
}
164
244
165
245
impl < T : NodeType , M : Memo < T > > CascadesOptimizer < T , M > {
166
- pub fn panic_on_explore_limit ( & mut self , enabled : bool ) {
167
- self . prop . panic_on_budget = enabled;
168
- }
169
-
170
- pub fn disable_pruning ( & mut self , enabled : bool ) {
171
- self . prop . disable_pruning = enabled;
172
- }
173
-
174
246
pub fn cost ( & self ) -> Arc < dyn CostModel < T , M > > {
175
247
self . cost . clone ( )
176
248
}
@@ -217,7 +289,7 @@ impl<T: NodeType, M: Memo<T>> CascadesOptimizer<T, M> {
217
289
self . disabled_rules . contains ( & rule_id)
218
290
}
219
291
220
- pub fn dump ( & self ) {
292
+ pub fn dump ( & self , mut f : impl std :: fmt :: Write ) -> std :: fmt :: Result {
221
293
for group_id in self . memo . get_all_group_ids ( ) {
222
294
let winner_str = match & self . memo . get_group_info ( group_id) . winner {
223
295
Winner :: Impossible => "winner=<impossible>" . to_string ( ) ,
@@ -234,28 +306,41 @@ impl<T: NodeType, M: Memo<T>> CascadesOptimizer<T, M> {
234
306
)
235
307
}
236
308
} ;
237
- println ! ( "group_id={} {}" , group_id, winner_str) ;
309
+ writeln ! ( f , "group_id={} {}" , group_id, winner_str) ? ;
238
310
let group = self . memo . get_group ( group_id) ;
239
311
for ( id, property) in self . logical_property_builders . iter ( ) . enumerate ( ) {
240
- println ! (
312
+ writeln ! (
313
+ f,
241
314
" {}={}" ,
242
315
property. property_name( ) ,
243
316
group. properties[ id] . as_ref( )
244
- )
317
+ ) ? ;
245
318
}
246
319
let mut all_predicates = BTreeSet :: new ( ) ;
247
320
for expr_id in self . memo . get_all_exprs_in_group ( group_id) {
248
321
let memo_node = self . memo . get_expr_memoed ( expr_id) ;
249
322
for pred in & memo_node. predicates {
250
323
all_predicates. insert ( * pred) ;
251
324
}
252
- println ! ( " expr_id={} | {}" , expr_id, memo_node) ;
325
+ writeln ! ( f , " expr_id={} | {}" , expr_id, memo_node) ? ;
253
326
}
254
327
for pred in all_predicates {
255
- println ! ( " {}={}" , pred, self . memo. get_pred( pred) ) ;
328
+ writeln ! ( f, " {}={}" , pred, self . memo. get_pred( pred) ) ?;
329
+ }
330
+ let mut traces = Vec :: new ( ) ;
331
+ for ( that_group_id, trace) in & self . stats . trace {
332
+ if self . memo . reduce_group ( * that_group_id) == group_id {
333
+ traces. extend ( trace. iter ( ) ) ;
334
+ }
335
+ }
336
+ traces. sort_by_key ( |x| x. stage_step ( ) ) ;
337
+ for t in traces {
338
+ writeln ! ( f, " {}" , t) ?;
256
339
}
257
340
}
341
+ Ok ( ( ) )
258
342
}
343
+
259
344
/// Optimize a `RelNode`.
260
345
pub fn step_optimize_rel ( & mut self , root_rel : ArcPlanNode < T > ) -> Result < GroupId > {
261
346
trace ! ( event = "step_optimize_rel" , rel = %root_rel) ;
@@ -287,15 +372,18 @@ impl<T: NodeType, M: Memo<T>> CascadesOptimizer<T, M> {
287
372
}
288
373
} ) ;
289
374
if res. is_err ( ) && cfg ! ( debug_assertions) {
290
- self . dump ( ) ;
375
+ let mut buf = String :: new ( ) ;
376
+ self . dump ( & mut buf) . unwrap ( ) ;
377
+ eprintln ! ( "{}" , buf) ;
291
378
}
292
379
res
293
380
}
294
381
295
382
pub fn fire_optimize_tasks ( & mut self , group_id : GroupId ) -> Result < ( ) > {
296
383
use pollster:: FutureExt as _;
297
384
trace ! ( event = "fire_optimize_tasks" , root_group_id = %group_id) ;
298
- let mut task = TaskContext :: new ( self ) ;
385
+ self . stage += 1 ;
386
+ let mut task = TaskContext :: new ( self , self . stage ) ;
299
387
// 32MB stack for the optimization process, TODO: reduce memory footprint
300
388
stacker:: grow ( 32 * 1024 * 1024 , || {
301
389
let fut: Pin < Box < dyn Future < Output = ( ) > > > = Box :: pin ( task. fire_optimize ( group_id) ) ;
0 commit comments