1 | #include "duckdb/common/vector_operations/vector_operations.hpp" |
2 | #include "duckdb/planner/expression/bound_conjunction_expression.hpp" |
3 | #include "duckdb/execution/adaptive_filter.hpp" |
4 | #include <vector> |
5 | |
6 | using namespace duckdb; |
7 | using namespace std; |
8 | |
9 | AdaptiveFilter::AdaptiveFilter(Expression &expr) |
10 | : iteration_count(0), observe_interval(10), execute_interval(20), warmup(true) { |
11 | auto &conj_expr = (BoundConjunctionExpression &)expr; |
12 | assert(conj_expr.children.size() > 1); |
13 | for (idx_t idx = 0; idx < conj_expr.children.size(); idx++) { |
14 | permutation.push_back(idx); |
15 | if (idx != conj_expr.children.size() - 1) { |
16 | swap_likeliness.push_back(100); |
17 | } |
18 | } |
19 | right_random_border = 100 * (conj_expr.children.size() - 1); |
20 | } |
21 | |
22 | AdaptiveFilter::AdaptiveFilter(unordered_map<idx_t, vector<TableFilter>> &table_filters) |
23 | : iteration_count(0), observe_interval(10), execute_interval(20), warmup(true) { |
24 | for (auto &table_filter : table_filters) { |
25 | permutation.push_back(table_filter.first); |
26 | swap_likeliness.push_back(100); |
27 | } |
28 | swap_likeliness.pop_back(); |
29 | right_random_border = 100 * (table_filters.size() - 1); |
30 | } |
31 | void AdaptiveFilter::AdaptRuntimeStatistics(double duration) { |
32 | iteration_count++; |
33 | runtime_sum += duration; |
34 | |
35 | if (!warmup) { |
36 | // the last swap was observed |
37 | if (observe && iteration_count == observe_interval) { |
38 | // keep swap if runtime decreased, else reverse swap |
39 | if (prev_mean - (runtime_sum / iteration_count) <= 0) { |
40 | // reverse swap because runtime didn't decrease |
41 | swap(permutation[swap_idx], permutation[swap_idx + 1]); |
42 | |
43 | // decrease swap likeliness, but make sure there is always a small likeliness left |
44 | if (swap_likeliness[swap_idx] > 1) { |
45 | swap_likeliness[swap_idx] /= 2; |
46 | } |
47 | } else { |
48 | // keep swap because runtime decreased, reset likeliness |
49 | swap_likeliness[swap_idx] = 100; |
50 | } |
51 | observe = false; |
52 | |
53 | // reset values |
54 | iteration_count = 0; |
55 | runtime_sum = 0.0; |
56 | } else if (!observe && iteration_count == execute_interval) { |
57 | // save old mean to evaluate swap |
58 | prev_mean = runtime_sum / iteration_count; |
59 | |
60 | // get swap index and swap likeliness |
61 | uniform_int_distribution<int> distribution(1, right_random_border); // a <= i <= b |
62 | idx_t random_number = distribution(generator) - 1; |
63 | |
64 | swap_idx = random_number / 100; // index to be swapped |
65 | idx_t likeliness = random_number - 100 * swap_idx; // random number between [0, 100) |
66 | |
67 | // check if swap is going to happen |
68 | if (swap_likeliness[swap_idx] > likeliness) { // always true for the first swap of an index |
69 | // swap |
70 | swap(permutation[swap_idx], permutation[swap_idx + 1]); |
71 | |
72 | // observe whether swap will be applied |
73 | observe = true; |
74 | } |
75 | |
76 | // reset values |
77 | iteration_count = 0; |
78 | runtime_sum = 0.0; |
79 | } |
80 | } else { |
81 | if (iteration_count == 5) { |
82 | // initially set all values |
83 | iteration_count = 0; |
84 | runtime_sum = 0.0; |
85 | observe = false; |
86 | warmup = false; |
87 | } |
88 | } |
89 | } |
90 | |