Optimization that attempts to cache results for inputs that are dictionary encoded and use the same base vector between subsequent input batches. Since this hold onto a reference to the base vector and the cached results, it can be memory intensive. Therefore in order to reduce this consumption and ensure it is only employed for cases where it can be useful, it only starts caching result after it
| 1200 | // it is only employed for cases where it can be useful, it only starts caching |
| 1201 | // result after it encounters the same base at least twice. |
| 1202 | void Expr::evalWithMemo( |
| 1203 | const SelectivityVector& rows, |
| 1204 | EvalCtx& context, |
| 1205 | VectorPtr& result) { |
| 1206 | VectorPtr base; |
| 1207 | distinctFields_[0]->evalSpecialForm(rows, context, base); |
| 1208 | |
| 1209 | if (base.get() != baseOfDictionaryRawPtr_ || |
| 1210 | baseOfDictionaryWeakPtr_.expired()) { |
| 1211 | baseOfDictionaryRepeats_ = 0; |
| 1212 | baseOfDictionaryWeakPtr_ = base; |
| 1213 | baseOfDictionaryRawPtr_ = base.get(); |
| 1214 | context.releaseVector(baseOfDictionary_); |
| 1215 | context.releaseVector(dictionaryCache_); |
| 1216 | context.exprSet()->dictionaryCacheTotalSize() -= dictionaryCacheSize_; |
| 1217 | dictionaryCacheSize_ = 0; |
| 1218 | evalWithNulls(rows, context, result); |
| 1219 | return; |
| 1220 | } |
| 1221 | ++baseOfDictionaryRepeats_; |
| 1222 | |
| 1223 | if (baseOfDictionaryRepeats_ == 1) { |
| 1224 | evalWithNulls(rows, context, result); |
| 1225 | baseOfDictionary_ = base; |
| 1226 | dictionaryCache_ = result; |
| 1227 | dictionaryCacheSize_ = dictionaryCache_->retainedSize(); |
| 1228 | context.exprSet()->dictionaryCacheTotalSize() += dictionaryCacheSize_; |
| 1229 | if (!cachedDictionaryIndices_) { |
| 1230 | cachedDictionaryIndices_ = |
| 1231 | context.execCtx()->getSelectivityVector(rows.end()); |
| 1232 | } |
| 1233 | *cachedDictionaryIndices_ = rows; |
| 1234 | context.deselectErrors(*cachedDictionaryIndices_); |
| 1235 | return; |
| 1236 | } |
| 1237 | |
| 1238 | if (cachedDictionaryIndices_) { |
| 1239 | LocalSelectivityVector cachedHolder(context, rows); |
| 1240 | auto cached = cachedHolder.get(); |
| 1241 | BOLT_DCHECK(cached != nullptr); |
| 1242 | cached->intersect(*cachedDictionaryIndices_); |
| 1243 | if (cached->hasSelections()) { |
| 1244 | context.ensureWritable(rows, type(), result); |
| 1245 | result->copy( |
| 1246 | dictionaryCache_.get(), *cached, nullptr, context.isFinalSelection()); |
| 1247 | } |
| 1248 | } |
| 1249 | LocalSelectivityVector uncachedHolder(context, rows); |
| 1250 | auto uncached = uncachedHolder.get(); |
| 1251 | BOLT_DCHECK(uncached != nullptr); |
| 1252 | if (cachedDictionaryIndices_) { |
| 1253 | uncached->deselect(*cachedDictionaryIndices_); |
| 1254 | } |
| 1255 | if (uncached->hasSelections()) { |
| 1256 | // Fix finalSelection at "rows" if uncached rows is a strict subset to |
| 1257 | // avoid losing values not in uncached rows that were copied earlier into |
| 1258 | // "result" from the cached rows. |
| 1259 | ScopedFinalSelectionSetter scopedFinalSelectionSetter( |
nothing calls this directly
no test coverage detected