Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief The Order Preserving Minimal Perfect Hash Map Predictor.
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include <kdbassert.h>
10 : #include <kdbhelper.h>
11 : #include <kdblogger.h>
12 : #include <kdbopmphmpredictor.h>
13 : #include <stdlib.h>
14 : #include <string.h>
15 :
16 : /**
17 : * The benchmarked and evaluated values of the predictors configuration
18 : */
19 : const uint16_t opmphmPredictorHistoryMask = 0x1FF; // 9 bit history
20 : const size_t opmphmPredictorActionLimit = 599;
21 :
22 : /**
23 : * Prediction Automata A2
24 : *
25 : * state 0 and 1 are not worth using the OPMPHM
26 : * state 2 and 3 are worth using the OPMPHM
27 : */
28 : static uint8_t predictionAutomata[4][2] = {
29 : // state/0v1 0 1
30 : // clang-format off
31 : /* 0 */ {0,1},
32 : /* 1 */ {0,2},
33 : /* 2 */ {1,3},
34 : /* 3 */ {2,3}
35 : // clang-format on
36 : };
37 :
38 : /**
39 : * @brief Heuristic function above the OPMPHM usage is worth.
40 : *
41 : * This easy and fast computable heuristic function tells how many ksLookup (...) invocations without alteration
42 : * of the KeySet need to be made to justify the OPMPHM usage.
43 : * This heuristic function is developed and measured by benchmarks.
44 : *
45 : * @param n the number of elements in the KeySet
46 : *
47 : * @retval size_t the heuristic value
48 : */
49 0 : inline size_t opmphmPredictorWorthOpmphm (size_t n)
50 : {
51 45320 : return n + 5000;
52 : }
53 :
54 : /**
55 : * @brief Increases the counter when the OPMPHM was used for the ksLookup (...) .
56 : *
57 : * @param op the Predictor
58 : */
59 2 : inline void opmphmPredictorIncCountOpmphm (OpmphmPredictor * op)
60 : {
61 2 : ELEKTRA_NOT_NULL (op);
62 2 : ++op->lookupCount;
63 2 : }
64 :
65 : /**
66 : * @brief Increases the counter when the Binary Search was used for the ksLookup (...) .
67 : *
68 : * Prevents also an endless Binary Search usage by a simple heuristic.
69 : *
70 : * @param op the Predictor
71 : * @param n the number of elements in the KeySet
72 : *
73 : * @retval 1 it is worth using the OPMPHM
74 : * @retval 0 it is not worth using the OPMPHM
75 : */
76 36012 : inline int opmphmPredictorIncCountBinarySearch (OpmphmPredictor * op, size_t n)
77 : {
78 36012 : ELEKTRA_NOT_NULL (op);
79 36012 : ++op->lookupCount;
80 36012 : size_t worthOpmphm = opmphmPredictorWorthOpmphm (n);
81 36012 : worthOpmphm = worthOpmphm + worthOpmphm + worthOpmphm; // 3 * worthOpmphm
82 36012 : return op->lookupCount > worthOpmphm;
83 : }
84 :
85 : /**
86 : * @brief Predictcs at the first ksLookup (...) after a KeySet changed if it will be worth using the OPMPHM.
87 : *
88 : * Uses the opmphmPredictorWorthOpmphm (...) to check if the previous sequence of ksLookup (...) invocations without
89 : * alteration was worth using the OPMPHM. Updates the state with the predictionAutomata and the worth information
90 : * of the previous history. Alters the history with the worth information and makes the prediction for the
91 : * next sequence of ksLookup (...) invocations.
92 : *
93 : * @param op the Predictor
94 : * @param n the number of elements in the KeySet
95 : *
96 : * @retval 1 it is worth using the OPMPHM
97 : * @retval 0 it is not worth using the OPMPHM
98 : */
99 9298 : int opmphmPredictor (OpmphmPredictor * op, size_t n)
100 : {
101 9298 : ELEKTRA_NOT_NULL (op);
102 9298 : ELEKTRA_ASSERT (n > 0, "n is 0");
103 : /*
104 : * update patterTable with worth information
105 : */
106 : // check if previos lookup sequence was worth hashing
107 18596 : size_t worthOpmphm = opmphmPredictorWorthOpmphm (op->ksSize);
108 9298 : uint8_t wasItWorth = op->lookupCount > worthOpmphm ? 1 : 0;
109 : // find position in array
110 9298 : uint16_t pos = op->history & opmphmPredictorHistoryMask;
111 9298 : uint8_t * state = &(op->patternTable[pos >> 2]); // (pos * 2) / 8 == pos >> 2
112 : // extract 2 bit state
113 9298 : uint8_t newState = (*state >> ((pos & 0x3) << 1)) & 0x3; // ((pos & 0x3) << 1) == (pos % 4) * 2), 0x3 is 2 bit mask
114 : // feed state and worth information to the predictionAutomata
115 9298 : newState = predictionAutomata[newState][wasItWorth];
116 : // delete old state
117 9298 : *state &= ~(0x3 << ((pos & 0x3) << 1)); // ((pos & 0x3) << 1) == (pos % 4) * 2), 0x3 is 2 bit mask
118 : // store the new state
119 9298 : *state |= newState << ((pos & 0x3) << 1); // ((pos & 0x3) << 1) == (pos % 4) * 2), 0x3 is 2 bit mask
120 : /*
121 : * predict with updated history
122 : */
123 : // add the worth information to the history
124 9298 : op->history = ((op->history << 1) | wasItWorth) & opmphmPredictorHistoryMask;
125 9298 : pos = op->history & opmphmPredictorHistoryMask;
126 9298 : state = &(op->patternTable[pos >> 2]); // (pos * 2) / 8 == pos >> 2
127 : // extract 2 bit state
128 9298 : uint8_t prediction = (*state >> ((pos & 0x3) << 1)) & 0x3; // ((pos & 0x3) << 1) == (pos % 4) * 2), 0x3 is 2 bit mask
129 : // reset lookupCount
130 9298 : op->lookupCount = 1;
131 : // set new keyset size
132 9298 : op->ksSize = n;
133 : // determine prediction
134 9298 : return prediction > 1;
135 : }
136 :
137 : /**
138 : * @brief Allocates and initializes the OpmphmPredictor.
139 : *
140 : * Reserves for all possible values of opmphmPredictorHistoryMask two bits
141 : * to store all 4 states of the Prediction Automata A2.
142 : * Sets the initial state to 0.
143 : *
144 : * @retval OpmphmPredictor * success
145 : * @retval NULL memory error
146 : */
147 16 : OpmphmPredictor * opmphmPredictorNew (void)
148 : {
149 16 : OpmphmPredictor * out = elektraCalloc (sizeof (OpmphmPredictor));
150 16 : if (!out)
151 : {
152 : return NULL;
153 : }
154 : // per 1 bit in opmphmPredictorHistoryMask 2 bits in the patternTable are needed
155 16 : uint16_t bytesInPatternTable = (opmphmPredictorHistoryMask + 1) >> 2; // x >> 2 == (x * 2) / 8
156 : if (!bytesInPatternTable)
157 : {
158 : bytesInPatternTable = 1;
159 : }
160 16 : out->patternTable = elektraCalloc (bytesInPatternTable * sizeof (uint8_t));
161 16 : if (!out->patternTable)
162 : {
163 0 : elektraFree (out);
164 0 : return NULL;
165 : }
166 16 : out->size = bytesInPatternTable;
167 16 : return out;
168 : }
169 :
170 : /**
171 : * @brief Make a copy of the OpmphmPredictor.
172 : *
173 : * @param source the OpmphmPredictor source
174 : * @param dest the OpmphmPredictor destination
175 : */
176 6 : void opmphmPredictorCopy (OpmphmPredictor * dest, OpmphmPredictor * source)
177 : {
178 6 : ELEKTRA_NOT_NULL (source);
179 6 : ELEKTRA_NOT_NULL (dest);
180 : // copy values
181 6 : dest->history = source->history;
182 6 : dest->size = source->size;
183 6 : dest->lookupCount = source->lookupCount;
184 6 : dest->ksSize = source->ksSize;
185 : // copy patternTable
186 6 : memcpy (dest->patternTable, source->patternTable, source->size * sizeof (uint8_t));
187 6 : }
188 :
189 : /**
190 : * @brief Deletes the OpmphmPredictor.
191 : *
192 : * Clears and frees all memory in OpmphmPredictor.
193 : *
194 : * @param op the OpmphmPredictor
195 : */
196 16 : void opmphmPredictorDel (OpmphmPredictor * op)
197 : {
198 16 : ELEKTRA_NOT_NULL (op);
199 16 : elektraFree (op->patternTable);
200 16 : elektraFree (op);
201 16 : }
|