1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* |
10 | * The dataflow reorder |
11 | * MAL programs are largely logical descriptions of an execution plan. |
12 | * After the mitosis and mergetable optimizers we have a large program, which when |
13 | * executed as is, does not necessarily benefit from the locality |
14 | * of data and operations. The problem is that the execution plan is |
15 | * a DAG for which a topological order should be found that |
16 | * minimizes the life time of variables and maximizes parallel execution. |
17 | * This is an NP hard optimization problem. Therefore, we have |
18 | * to rely on an affordable heuristic steps. |
19 | * |
20 | * The reorder optimizer transfers the breadth-first plans of |
21 | * the mergetable into a depth-first traversal. |
22 | * This increases cohesion for parallel execution. |
23 | * It is performed by basic block that ends in a side-effect |
24 | * bearing instruction. |
25 | * Simply walking backward and pulling out subplans is then |
26 | * the way to go. This step could be optimized somewhat |
27 | * by giving preference to variables that are too far |
28 | * away in the plan from their source. It is however not |
29 | * explored. |
30 | * |
31 | * The secondary approach is to pull instructions to the |
32 | * head of the plan if the dataflow such permits. |
33 | * If you are not careful, you will end up with |
34 | * the breadth first plan again. |
35 | * |
36 | * Beware, variables can be assigned a value multiple times. |
37 | * The order this implies should be respected. |
38 | * |
39 | * Hidden dependencies occur als with e.g. sql.assert() calls. |
40 | * Reordering them may easily lead to an assertion violation. |
41 | * Therefore, reordering should be limited to basic blocks without |
42 | * side-effects. |
43 | */ |
44 | #include "monetdb_config.h" |
45 | #include "opt_reorder.h" |
46 | #include "mal_instruction.h" |
47 | #include "mal_interpreter.h" |
48 | /* |
49 | * Collect the statement dependencies in a table first |
50 | * This can be done in linear time in size of the program. |
51 | * Also check for barrier blocks. We only allow reordering |
52 | * for a linear plan. Future extensions could consider |
53 | * re-ordering basic blocks only. |
54 | */ |
55 | typedef struct{ |
56 | int cnt; |
57 | int used; |
58 | int pos,pos2; |
59 | int stmt[FLEXIBLE_ARRAY_MEMBER]; |
60 | } *Node, NodeRecord; |
61 | |
62 | static void |
63 | OPTremoveDep(Node *list, int lim) |
64 | { |
65 | int i; |
66 | |
67 | for (i=0; i< lim; i++) |
68 | if (list[i]) |
69 | GDKfree(list[i]); |
70 | GDKfree(list); |
71 | } |
72 | |
73 | static Node * |
74 | OPTdependencies(Client cntxt, MalBlkPtr mb, int **Ulist) |
75 | { |
76 | Node *list = (Node *) GDKzalloc(sizeof(Node) * mb->stop); |
77 | int *var = (int*) GDKzalloc(sizeof(int) * mb->vtop), *uselist = NULL; |
78 | int i,j,sz=0; |
79 | InstrPtr p = NULL; |
80 | int block = 0; |
81 | |
82 | (void) cntxt; |
83 | |
84 | if (list == NULL || var == NULL){ |
85 | if (list ) GDKfree(list); |
86 | if (var) GDKfree(var); |
87 | return NULL; |
88 | } |
89 | |
90 | for ( i=0; i< mb->stop; i++){ |
91 | p= getInstrPtr(mb,i); |
92 | block |= p->barrier != 0; |
93 | list[i]= (Node) GDKzalloc(offsetof(NodeRecord, stmt) + sizeof(int) * p->argc); |
94 | if (list[i] == NULL){ |
95 | OPTremoveDep(list, i); |
96 | GDKfree(var); |
97 | return 0; |
98 | } |
99 | list[i]->cnt = p->argc; |
100 | for( j=p->retc; j<p->argc; j++) { |
101 | list[i]->stmt[j] = var[getArg(p,j)]; |
102 | list[var[getArg(p,j)]]->used++; |
103 | } |
104 | /* keep the assignment order */ |
105 | for( j= 0; j < p->retc; j++) { |
106 | if ( var[ getArg(p,j)] ) { |
107 | //list[i]->stmt[j] = var [getArg(p,j)]; |
108 | // escape we should avoid reused variables. |
109 | OPTremoveDep(list, i + 1); |
110 | GDKfree(var); |
111 | return 0; |
112 | } |
113 | } |
114 | /* remember the last assignment */ |
115 | for( j=0; j<p->retc; j++) |
116 | var[getArg(p,j)] = i; |
117 | } |
118 | /* |
119 | * mnstr_printf(cntxt->fdout,"DEPENDENCY TABLE\n"); |
120 | * for(i=0;i<mb->stop; i++) |
121 | * if( list[i]->cnt){ |
122 | * mnstr_printf(cntxt->fdout,"%s.%s [%d,%d]", |
123 | * mb->stmt[i]->modname, |
124 | * mb->stmt[i]->fcnname, i, list[i]->used); |
125 | * for(j=p->retc; j< list[i]->cnt; j++) |
126 | * mnstr_printf(cntxt->fdout, " %d", list[i]->stmt[j]); |
127 | * mnstr_printf(cntxt->fdout,"\n"); |
128 | * } |
129 | */ |
130 | for(i=0;i<mb->stop; i++) { |
131 | list[i]->pos = sz; |
132 | list[i]->pos2 = sz; |
133 | sz += list[i]->used; |
134 | } |
135 | if( sz == 0){ |
136 | OPTremoveDep(list, mb->stop); |
137 | GDKfree(var); |
138 | return NULL; |
139 | } |
140 | uselist = GDKzalloc(sizeof(int)*sz); |
141 | if (!uselist) { |
142 | OPTremoveDep(list, mb->stop); |
143 | GDKfree(var); |
144 | return NULL; |
145 | } |
146 | |
147 | for(i=0;i<mb->stop; i++) { |
148 | if (list[i]->cnt) { |
149 | p= getInstrPtr(mb,i); |
150 | for(j=p->retc; j< list[i]->cnt; j++) { |
151 | uselist[list[list[i]->stmt[j]]->pos2] = i; |
152 | list[list[i]->stmt[j]]->pos2++; |
153 | } |
154 | } |
155 | } |
156 | /* |
157 | * for(i=0, sz = 0; i<mb->stop; i++) { |
158 | * mnstr_printf(cntxt->fdout,"%d is used by", i); |
159 | * for(j=0; j<list[i]->used; j++, sz++) |
160 | * mnstr_printf(cntxt->fdout," %d", uselist[sz]); |
161 | * mnstr_printf(cntxt->fdout,"\n"); |
162 | * } |
163 | */ |
164 | |
165 | if ( block ){ |
166 | OPTremoveDep(list, mb->stop); |
167 | GDKfree(uselist); |
168 | GDKfree(var); |
169 | return NULL; |
170 | } |
171 | GDKfree(var); |
172 | *Ulist = uselist; |
173 | return list; |
174 | } |
175 | |
176 | static int |
177 | OPTbreadthfirst(Client cntxt, MalBlkPtr mb, int pc, int max, InstrPtr old[], Node dep[], int *uselist) |
178 | { |
179 | int i; |
180 | InstrPtr p; |
181 | |
182 | if (pc > max) |
183 | return 0; |
184 | |
185 | p = old[pc]; |
186 | if (p == NULL) |
187 | return 0; |
188 | |
189 | if (THRhighwater()) |
190 | return -1; |
191 | |
192 | for (i= p->retc; i< dep[pc]->cnt; i++) |
193 | if (OPTbreadthfirst(cntxt, mb, dep[pc]->stmt[i], max, old, dep, uselist) < 0) |
194 | return -1; |
195 | if (old[pc] != NULL) { |
196 | old[pc] = 0; |
197 | pushInstruction(mb, p); |
198 | } |
199 | if (getModuleId(p) == groupRef) |
200 | for (i = 0; i< dep[pc]->used; i++) |
201 | if (OPTbreadthfirst(cntxt, mb, uselist[dep[pc]->pos+i], max, old, dep, uselist) < 0) |
202 | return -1; |
203 | return 0; |
204 | } |
205 | |
206 | /* SQL appends are collected to create a better dataflow block */ |
207 | /* alternatively, we should postpone all mcv-chained actions */ |
208 | static int |
209 | OPTpostponeAppends(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) |
210 | { |
211 | int i,j,k=0, actions =0, last=-1; |
212 | InstrPtr *old, *appends; |
213 | int limit; |
214 | (void) cntxt; |
215 | (void) stk; |
216 | (void) p; |
217 | |
218 | appends =(InstrPtr*) GDKzalloc(mb->ssize * sizeof(InstrPtr)); |
219 | if( appends == NULL) |
220 | return 0; |
221 | limit= mb->stop; |
222 | old = mb->stmt; |
223 | if ( newMalBlkStmt(mb, mb->ssize) < 0) { |
224 | GDKfree(appends); |
225 | return 0; |
226 | } |
227 | for( i=0; i<limit; i++){ |
228 | if ( getModuleId(old[i]) == sqlRef && getFunctionId(old[i]) == appendRef){ |
229 | last = i; |
230 | } |
231 | } |
232 | for( i=0; i<limit; i++){ |
233 | if ( getModuleId(old[i]) == sqlRef && getFunctionId(old[i]) == appendRef){ |
234 | // only postpone under strict conditions |
235 | assert( isVarConstant(mb,getArg(old[i],2))); |
236 | assert( isVarConstant(mb,getArg(old[i],3))); |
237 | assert( isVarConstant(mb,getArg(old[i],4))); |
238 | if( actions ) |
239 | pushInstruction(mb, old[i]); |
240 | else { |
241 | if (k > 0 && getArg(old[i],1) == getArg(appends[k-1],0)) |
242 | appends[k++]= old[i]; |
243 | else { |
244 | for(j=0; j<k; j++) |
245 | pushInstruction(mb,appends[j]); |
246 | pushInstruction(mb, old[i]); |
247 | actions++; |
248 | } |
249 | } |
250 | continue; |
251 | } |
252 | if ( i == last){ |
253 | actions++; |
254 | for(j=0; j<k; j++) |
255 | pushInstruction(mb,appends[j]); |
256 | } |
257 | pushInstruction(mb,old[i]); |
258 | } |
259 | for( ; i<limit; i++){ |
260 | pushInstruction(mb,old[i]); |
261 | } |
262 | GDKfree(appends); |
263 | GDKfree(old); |
264 | return actions; |
265 | } |
266 | |
267 | str |
268 | OPTreorderImplementation(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p) |
269 | { |
270 | int i,j, start; |
271 | InstrPtr *old; |
272 | int limit, slimit, *uselist = NULL; |
273 | Node *dep; |
274 | char buf[256]; |
275 | lng usec= GDKusec(); |
276 | str msg = MAL_SUCCEED; |
277 | |
278 | (void) cntxt; |
279 | (void) stk; |
280 | dep = OPTdependencies(cntxt,mb,&uselist); |
281 | if ( dep == NULL) |
282 | return MAL_SUCCEED; |
283 | limit= mb->stop; |
284 | slimit= mb->ssize; |
285 | old = mb->stmt; |
286 | if ( newMalBlkStmt(mb, mb->ssize) < 0) { |
287 | GDKfree(uselist); |
288 | OPTremoveDep(dep, limit); |
289 | throw(MAL,"optimizer.reorder" , SQLSTATE(HY001) MAL_MALLOC_FAIL); |
290 | } |
291 | |
292 | pushInstruction(mb,old[0]); |
293 | old[0]=0; |
294 | |
295 | start=1; |
296 | for (i=1; i<limit; i++){ |
297 | p= old[i]; |
298 | if ( p == 0) |
299 | continue; |
300 | if( p->token == ENDsymbol) |
301 | break; |
302 | if( hasSideEffects(mb, p,FALSE) || isUnsafeFunction(p) || p->barrier ){ |
303 | if (OPTbreadthfirst(cntxt, mb, i, i, old, dep, uselist) < 0) |
304 | break; |
305 | /* remove last instruction and keep for later */ |
306 | if (p == mb->stmt[mb->stop-1]) { |
307 | p= mb->stmt[mb->stop-1]; |
308 | mb->stmt[mb->stop-1]=0; |
309 | mb->stop--; |
310 | } else { |
311 | p = 0; |
312 | } |
313 | /* collect all seen sofar by backward grouping */ |
314 | /* since p has side-effects, we should secure all seen sofar */ |
315 | for(j=i-1; j>=start;j--) { |
316 | |
317 | if( OPTdebug & OPTreorder){ |
318 | if( old[j]){ |
319 | fprintf(stderr,"leftover: %d" ,start+1); |
320 | fprintInstruction(stderr,mb,0,old[j],LIST_MAL_ALL); |
321 | } |
322 | } |
323 | |
324 | if (OPTbreadthfirst(cntxt, mb, j, i, old, dep, uselist) < 0) { |
325 | i = limit; /* cause break from outer loop */ |
326 | break; |
327 | } |
328 | } |
329 | if (p) |
330 | pushInstruction(mb,p); |
331 | start = i+1; |
332 | } |
333 | } |
334 | for(i=0; i<limit; i++) |
335 | if (old[i]) |
336 | pushInstruction(mb,old[i]); |
337 | for(; i<slimit; i++) |
338 | if (old[i]) |
339 | freeInstruction(old[i]); |
340 | OPTremoveDep(dep, limit); |
341 | GDKfree(uselist); |
342 | GDKfree(old); |
343 | (void) OPTpostponeAppends(cntxt, mb, 0, 0); |
344 | |
345 | /* Defense line against incorrect plans */ |
346 | if( 1){ |
347 | chkTypes(cntxt->usermodule, mb, FALSE); |
348 | chkFlow(mb); |
349 | chkDeclarations(mb); |
350 | } |
351 | /* keep all actions taken as a post block comment */ |
352 | usec = GDKusec()- usec; |
353 | snprintf(buf,256,"%-20s actions=%2d time=" LLFMT " usec" ,"reorder" ,1,usec); |
354 | newComment(mb,buf); |
355 | addtoMalBlkHistory(mb); |
356 | |
357 | if( OPTdebug & OPTreorder){ |
358 | fprintf(stderr, "#reorder optimizer entry\n" ); |
359 | fprintFunction(stderr, mb, 0, LIST_MAL_ALL); |
360 | } |
361 | return msg; |
362 | } |
363 | |