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