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 | /* (author) M.L. Kersten |
10 | */ |
11 | #include "monetdb_config.h" |
12 | #include "mal_resource.h" |
13 | #include "mal_private.h" |
14 | |
15 | /* Memory based admission does not seem to have a major impact so far. */ |
16 | static lng memorypool = 0; /* memory claimed by concurrent threads */ |
17 | |
18 | void |
19 | mal_resource_reset(void) |
20 | { |
21 | memorypool = (lng) MEMORY_THRESHOLD; |
22 | } |
23 | /* |
24 | * Running all eligible instructions in parallel creates |
25 | * resource contention. This means we should implement |
26 | * an admission control scheme where threads are temporarily |
27 | * postponed if the claim for memory exceeds a threshold |
28 | * In general such contentions will be hard to predict, |
29 | * because they depend on the algorithm, the input sizes, |
30 | * concurrent use of the same variables, and the output produced. |
31 | * |
32 | * One heuristic is based on calculating the storage footprint |
33 | * of the operands and assuming it preferrably should fit in memory. |
34 | * Ofcourse, there may be intermediate structures being |
35 | * used and the size of the result is not a priori known. |
36 | * For this, we use a high watermark on the amount of |
37 | * physical memory we pre-allocate for the claims. |
38 | * |
39 | * Instructions are eligible to be executed when the |
40 | * total footprint of all concurrent executions stays below |
41 | * the high-watermark or it is the single expensive |
42 | * instruction being started. |
43 | * |
44 | * When we run out of memory, the instruction is delayed. |
45 | * How long depends on the other instructions to free up |
46 | * resources. The current policy simple takes a local |
47 | * decision by delaying the instruction based on its |
48 | * claim of the memory. |
49 | */ |
50 | |
51 | /* |
52 | * The memory claim is the estimate for the amount of memory hold. |
53 | * Views are consider cheap and ignored. |
54 | * Given that auxiliary structures are important for performance, |
55 | * we use their maximum as an indication of the memory footprint. |
56 | * An alternative would be to focus solely on the base table cost. |
57 | * (Good for a MSc study) |
58 | */ |
59 | lng |
60 | getMemoryClaim(MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, int i, int flag) |
61 | { |
62 | lng total = 0, itotal = 0, t; |
63 | BAT *b; |
64 | |
65 | (void)mb; |
66 | if (stk->stk[getArg(pci, i)].vtype == TYPE_bat) { |
67 | b = BATdescriptor( stk->stk[getArg(pci, i)].val.bval); |
68 | if (b == NULL) |
69 | return 0; |
70 | if (flag && isVIEW(b)) { |
71 | BBPunfix(b->batCacheid); |
72 | return 0; |
73 | } |
74 | |
75 | /* calculate the basic scan size */ |
76 | total += BATcount(b) * b->twidth; |
77 | total += heapinfo(b->tvheap, b->batCacheid); |
78 | |
79 | /* indices should help, find their maximum footprint */ |
80 | itotal = hashinfo(b->thash, d->batCacheid); |
81 | t = IMPSimprintsize(b); |
82 | if( t > itotal) |
83 | itotal = t; |
84 | /* We should also consider the ordered index and mosaic */ |
85 | //total = total > (lng)(MEMORY_THRESHOLD ) ? (lng)(MEMORY_THRESHOLD ) : total; |
86 | BBPunfix(b->batCacheid); |
87 | if ( total < itotal) |
88 | total = itotal; |
89 | } |
90 | return total; |
91 | } |
92 | |
93 | /* |
94 | * The argclaim provides a hint on how much we actually may need to execute |
95 | * |
96 | * The client context also keeps bounds on the memory claim/client. |
97 | * Surpassing this bound may be a reason to not admit the instruction to proceed. |
98 | */ |
99 | static MT_Lock admissionLock = MT_LOCK_INITIALIZER("admissionLock" ); |
100 | |
101 | int |
102 | MALadmission_claim(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, lng argclaim) |
103 | { |
104 | (void) mb; |
105 | (void) pci; |
106 | if (argclaim == 0) |
107 | return 0; |
108 | |
109 | MT_lock_set(&admissionLock); |
110 | /* Check if we are allowed to allocate another worker thread for this client */ |
111 | /* It is somewhat tricky, because we may be in a dataflow recursion, each of which should be counted for. |
112 | * A way out is to attach the thread count to the MAL stacks, which just limits the level |
113 | * of parallism for a single dataflow graph. |
114 | */ |
115 | if(cntxt->workerlimit && cntxt->workerlimit < stk->workers){ |
116 | PARDEBUG |
117 | fprintf(stderr, "#DFLOWadmit worker limit reached, %d <= %d\n" , cntxt->workerlimit, stk->workers); |
118 | MT_lock_unset(&admissionLock); |
119 | return -1; |
120 | } |
121 | /* Determine if the total memory resource is exhausted, because it is overall limitation. */ |
122 | if ( memorypool <= 0){ |
123 | // we accidently released too much memory or need to initialize |
124 | PARDEBUG |
125 | fprintf(stderr, "#DFLOWadmit memorypool reset " ); |
126 | memorypool = (lng) MEMORY_THRESHOLD; |
127 | } |
128 | |
129 | /* the argument claim is based on the input for an instruction */ |
130 | if ( memorypool > argclaim || stk->workers == 0 ) { |
131 | /* If we are low on memory resources, limit the user if he exceeds his memory budget |
132 | * but make sure there is at least one worker thread active */ |
133 | /* on hold until after experiments |
134 | if ( 0 && cntxt->memorylimit) { |
135 | if (argclaim + stk->memory > (lng) cntxt->memorylimit * LL_CONSTANT(1048576)){ |
136 | MT_lock_unset(&admissionLock); |
137 | PARDEBUG |
138 | fprintf(stderr, "#Delayed due to lack of session memory " LLFMT " requested "LLFMT"\n", |
139 | stk->memory, argclaim); |
140 | return -1; |
141 | } |
142 | stk->memory += argclaim; |
143 | } |
144 | */ |
145 | memorypool -= argclaim; |
146 | PARDEBUG |
147 | fprintf(stderr, "#DFLOWadmit thread %d pool " LLFMT "claims " LLFMT "\n" , |
148 | THRgettid(), memorypool, argclaim); |
149 | stk->workers++; |
150 | MT_lock_unset(&admissionLock); |
151 | return 0; |
152 | } |
153 | PARDEBUG |
154 | fprintf(stderr, "#Delayed due to lack of memory " LLFMT " requested " LLFMT "\n" , |
155 | memorypool, argclaim); |
156 | MT_lock_unset(&admissionLock); |
157 | return -1; |
158 | } |
159 | |
160 | void |
161 | MALadmission_release(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci, lng argclaim) |
162 | { |
163 | /* release memory claimed before */ |
164 | (void) cntxt; |
165 | (void) mb; |
166 | (void) pci; |
167 | if (argclaim == 0 ) |
168 | return; |
169 | |
170 | MT_lock_set(&admissionLock); |
171 | /* on hold until after experiments |
172 | if ( 0 && cntxt->memorylimit) { |
173 | PARDEBUG |
174 | fprintf(stderr, "#Return memory to session budget " LLFMT "\n", stk->memory); |
175 | stk->memory -= argclaim; |
176 | } |
177 | */ |
178 | memorypool += argclaim; |
179 | if ( memorypool > (lng) MEMORY_THRESHOLD ){ |
180 | PARDEBUG |
181 | fprintf(stderr, "#DFLOWadmit memorypool reset " ); |
182 | memorypool = (lng) MEMORY_THRESHOLD; |
183 | } |
184 | stk->workers--; |
185 | PARDEBUG |
186 | fprintf(stderr, "#DFLOWadmit thread %d pool " LLFMT " claims " LLFMT "\n" , |
187 | THRgettid(), memorypool, argclaim); |
188 | MT_lock_unset(&admissionLock); |
189 | return; |
190 | } |
191 | |