blob: 5598871c88401b752b8118941d92bb98e9b19e86 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 Copyright (c) 2012 Cisco and/or its affiliates.
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15*/
16
17#include <vppinfra/slist.h>
18
19/*
20 * skip-list implementation
21 *
22 * Good news / bad news. As balanced binary tree schemes go,
23 * this one seems pretty fast and is reasonably simple. There's a very
24 * limited amount that can be done to mitigate sdram read latency.
25 *
26 * Each active clib_slist_elt_t is on from 1 to N lists. Each active element
27 * is always on the "level-0" list. Since most elements are *only* on
28 * level 0, we keep the level 0 (and level 1) in the element. For those
29 * elements on more than two lists, we switch to a vector. Hence, the
Dave Barachc3799992016-08-15 11:12:27 -040030 * "n" union in slib_slist_elt_t.
31 *
Ed Warnickecb9cada2015-12-08 15:45:58 -070032 * The low-order bit of elt->n.next0[0] is 1 for inlined next indices,
33 * 0 for vector indices (since the allocator always aligns to at least
34 * a 4-byte boundary). We can only represent 2e9 items, but since the
35 * practical performance limit is O(1e7), it doesn't matter.
36 *
37 * We create a "head" element which (by construction) is always
38 * lexically lighter than any other element. This makes a large number
39 * of irritating special cases go away.
40 *
41 * User code is in charge of comparing a supplied key with
42 * the key component of a user pool element. The user tells this code
43 * to add or delete (opaque key, 32-bit integer) pairs to the skip-list.
Dave Barachc3799992016-08-15 11:12:27 -040044 *
Ed Warnickecb9cada2015-12-08 15:45:58 -070045 * The algorithm adds new elements to one or more lists.
46 * For levels greater than zero, the probability of a new element landing on
47 * a list is branching_factor**N. Branching_factor = 0.2 seems to work
48 * OK, yielding about 50 compares per search at O(1e7) items.
49 */
50
51clib_error_t *
Dave Barachc3799992016-08-15 11:12:27 -040052clib_slist_init (clib_slist_t * sp, f64 branching_factor,
53 clib_slist_key_compare_function_t compare,
54 format_function_t format_user_element)
Ed Warnickecb9cada2015-12-08 15:45:58 -070055{
56 clib_slist_elt_t *head;
Dave Barachb7b92992018-10-17 10:38:51 -040057 clib_memset (sp, 0, sizeof (sp[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -070058 sp->branching_factor = branching_factor;
59 sp->format_user_element = format_user_element;
60 sp->compare = compare;
61 sp->seed = 0xdeaddabe;
62 pool_get (sp->elts, head);
Dave Barachc3799992016-08-15 11:12:27 -040063 vec_add1 (head->n.nexts, (u32) ~ 0);
64 head->user_pool_index = (u32) ~ 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -070065 vec_validate (sp->path, 1);
66 vec_validate (sp->occupancy, 0);
67
68 return 0;
69}
70
71/*
72 * slist_search_internal
73 */
74static inline clib_slist_search_result_t
Dave Barachc3799992016-08-15 11:12:27 -040075slist_search_internal (clib_slist_t * sp, void *key, int need_full_path)
Ed Warnickecb9cada2015-12-08 15:45:58 -070076{
77 int level, comp_result;
78 clib_slist_elt_t *search_elt, *head_elt;
79
80 sp->ncompares = 0;
Dave Barachc3799992016-08-15 11:12:27 -040081 /*
82 * index 0 is the magic listhead element which is
Ed Warnickecb9cada2015-12-08 15:45:58 -070083 * lexically lighter than / to the left of every element
84 */
Dave Barachc3799992016-08-15 11:12:27 -040085 search_elt = head_elt = pool_elt_at_index (sp->elts, 0);
Ed Warnickecb9cada2015-12-08 15:45:58 -070086
Dave Barachc3799992016-08-15 11:12:27 -040087 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -070088 * Initial negotiating position, only the head_elt is
89 * lighter than the supplied key
90 */
Dave Barachb7b92992018-10-17 10:38:51 -040091 clib_memset (sp->path, 0, vec_len (head_elt->n.nexts) * sizeof (u32));
Ed Warnickecb9cada2015-12-08 15:45:58 -070092
93 /* Walk the fastest lane first */
94 level = vec_len (head_elt->n.nexts) - 1;
95 _vec_len (sp->path) = level + 1;
96
97 while (1)
98 {
99 u32 next_index_this_level;
100 clib_slist_elt_t *prefetch_elt;
101
Dave Barachc3799992016-08-15 11:12:27 -0400102 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700103 * Prefetching the next element at this level makes a measurable
104 * difference, but doesn't fix the dependent read stall problem
105 */
Dave Barachc3799992016-08-15 11:12:27 -0400106 prefetch_elt = sp->elts +
107 clib_slist_get_next_at_level (search_elt, level);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700108
Dave Barachc3799992016-08-15 11:12:27 -0400109 CLIB_PREFETCH (prefetch_elt, CLIB_CACHE_LINE_BYTES, READ);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700110
111 /* Compare the key with the current element */
112 comp_result = (search_elt == head_elt) ? 1 :
Dave Barachc3799992016-08-15 11:12:27 -0400113 sp->compare (key, search_elt->user_pool_index);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700114
115 sp->ncompares++;
116 /* key "lighter" than this element */
Dave Barachc3799992016-08-15 11:12:27 -0400117 if (comp_result < 0)
118 {
119 /*
120 * Back up to previous item on this list
121 * and search the next finer-grained list
122 * starting there.
123 */
124 search_elt = pool_elt_at_index (sp->elts, sp->path[level]);
125 next_list:
126 if (level > 0)
127 {
128 level--;
129 continue;
130 }
131 else
132 {
133 return CLIB_SLIST_NO_MATCH;
134 }
135 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700136 /* Match */
Dave Barachc3799992016-08-15 11:12:27 -0400137 if (comp_result == 0)
138 {
139 /*
140 * If we're trying to delete an element, we need to
141 * track down all of the elements which point at it.
142 * Otherwise, don't bother with it
143 */
144 if (need_full_path && level > 0)
145 {
146 search_elt = pool_elt_at_index (sp->elts, sp->path[level]);
147 level--;
148 continue;
149 }
150 level = vec_len (head_elt->n.nexts);
151 sp->path[level] = search_elt - sp->elts;
152 _vec_len (sp->path) = level + 1;
153 return CLIB_SLIST_MATCH;
154 }
155 /*
Ed Warnickecb9cada2015-12-08 15:45:58 -0700156 * comp_result positive, key is to the right of
157 * this element
Dave Barachc3799992016-08-15 11:12:27 -0400158 */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700159 sp->path[level] = search_elt - sp->elts;
160
161 /* Out of list at this level? */
Dave Barachc3799992016-08-15 11:12:27 -0400162 next_index_this_level =
163 clib_slist_get_next_at_level (search_elt, level);
164 if (next_index_this_level == (u32) ~ 0)
165 goto next_list;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700166
167 /* No, try the next element */
168 search_elt = pool_elt_at_index (sp->elts, next_index_this_level);
169 }
Dave Barachc3799992016-08-15 11:12:27 -0400170 return 0; /* notreached */
Ed Warnickecb9cada2015-12-08 15:45:58 -0700171}
172
Dave Barachc3799992016-08-15 11:12:27 -0400173u32
174clib_slist_search (clib_slist_t * sp, void *key, u32 * ncompares)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700175{
176 clib_slist_search_result_t rv;
177
Dave Barachc3799992016-08-15 11:12:27 -0400178 rv = slist_search_internal (sp, key, 0 /* dont need full path */ );
Ed Warnickecb9cada2015-12-08 15:45:58 -0700179 if (rv == CLIB_SLIST_MATCH)
180 {
181 clib_slist_elt_t *elt;
Dave Barachc3799992016-08-15 11:12:27 -0400182 elt = pool_elt_at_index (sp->elts, sp->path[vec_len (sp->path) - 1]);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700183 if (ncompares)
Dave Barachc3799992016-08-15 11:12:27 -0400184 *ncompares = sp->ncompares;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700185 return elt->user_pool_index;
186 }
Dave Barachc3799992016-08-15 11:12:27 -0400187 return (u32) ~ 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700188}
189
Dave Barachc3799992016-08-15 11:12:27 -0400190void
191clib_slist_add (clib_slist_t * sp, void *key, u32 user_pool_index)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700192{
193 clib_slist_elt_t *new_elt;
194 clib_slist_search_result_t search_result;
195 int level;
196
Dave Barachc3799992016-08-15 11:12:27 -0400197 search_result = slist_search_internal (sp, key,
198 0 /* don't need full path */ );
Ed Warnickecb9cada2015-12-08 15:45:58 -0700199
200 /* Special case: key exists, just replace user_pool_index */
Dave Barachc3799992016-08-15 11:12:27 -0400201 if (PREDICT_FALSE (search_result == CLIB_SLIST_MATCH))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700202 {
203 clib_slist_elt_t *elt;
204 elt = pool_elt_at_index (sp->elts, sp->path[0]);
205 elt->user_pool_index = user_pool_index;
206 return;
207 }
Dave Barachc3799992016-08-15 11:12:27 -0400208
Ed Warnickecb9cada2015-12-08 15:45:58 -0700209 pool_get (sp->elts, new_elt);
210 new_elt->n.nexts = 0;
211 new_elt->user_pool_index = user_pool_index;
212
213 /* sp->path lists elements to the left of key, by level */
Dave Barachc3799992016-08-15 11:12:27 -0400214 for (level = 0; level < vec_len (sp->path); level++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700215 {
216 clib_slist_elt_t *prev_elt_this_level;
217 u32 prev_elt_next_index_this_level;
218
219 /* Add to list at the current level */
220 prev_elt_this_level = pool_elt_at_index (sp->elts, sp->path[level]);
Dave Barachc3799992016-08-15 11:12:27 -0400221 prev_elt_next_index_this_level = clib_slist_get_next_at_level
222 (prev_elt_this_level, level);
223
224 clib_slist_set_next_at_level (new_elt, prev_elt_next_index_this_level,
225 level);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700226
227 clib_slist_set_next_at_level (prev_elt_this_level, new_elt - sp->elts,
Dave Barachc3799992016-08-15 11:12:27 -0400228 level);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700229 sp->occupancy[level]++;
Dave Barachc3799992016-08-15 11:12:27 -0400230
Ed Warnickecb9cada2015-12-08 15:45:58 -0700231 /* Randomly add to the next-higher level */
232 if (random_f64 (&sp->seed) > sp->branching_factor)
Dave Barachc3799992016-08-15 11:12:27 -0400233 break;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700234 }
Dave Barachc3799992016-08-15 11:12:27 -0400235 {
Ed Warnickecb9cada2015-12-08 15:45:58 -0700236 /* Time to add a new ply? */
Dave Barachc3799992016-08-15 11:12:27 -0400237 clib_slist_elt_t *head_elt = pool_elt_at_index (sp->elts, 0);
238 int top_level = vec_len (head_elt->n.nexts) - 1;
239 if (((f64) sp->occupancy[top_level]) * sp->branching_factor > 1.0)
240 {
241 vec_add1 (sp->occupancy, 0);
242 vec_add1 (head_elt->n.nexts, (u32) ~ 0);
243 /* full match case returns n+1 items */
244 vec_validate (sp->path, vec_len (head_elt->n.nexts));
245 }
246 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700247}
248
249clib_slist_search_result_t
Dave Barachc3799992016-08-15 11:12:27 -0400250clib_slist_del (clib_slist_t * sp, void *key)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700251{
252 clib_slist_search_result_t search_result;
253 clib_slist_elt_t *del_elt;
254 int level;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700255
Dave Barachc3799992016-08-15 11:12:27 -0400256 search_result = slist_search_internal (sp, key, 1 /* need full path */ );
257
258 if (PREDICT_FALSE (search_result == CLIB_SLIST_NO_MATCH))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700259 return search_result;
260
Dave Barachc3799992016-08-15 11:12:27 -0400261 del_elt = pool_elt_at_index (sp->elts, sp->path[vec_len (sp->path) - 1]);
262 ASSERT (vec_len (sp->path) > 1);
263
264 for (level = 0; level < vec_len (sp->path) - 1; level++)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700265 {
266 clib_slist_elt_t *path_elt;
267 u32 path_elt_next_index;
Dave Barachc3799992016-08-15 11:12:27 -0400268
Ed Warnickecb9cada2015-12-08 15:45:58 -0700269 path_elt = pool_elt_at_index (sp->elts, sp->path[level]);
270 path_elt_next_index = clib_slist_get_next_at_level (path_elt, level);
Dave Barachc3799992016-08-15 11:12:27 -0400271
Ed Warnickecb9cada2015-12-08 15:45:58 -0700272 /* Splice the item out of the list if it's adjacent to the victim */
273 if (path_elt_next_index == del_elt - sp->elts)
Dave Barachc3799992016-08-15 11:12:27 -0400274 {
275 sp->occupancy[level]--;
276 path_elt_next_index = clib_slist_get_next_at_level (del_elt, level);
277 clib_slist_set_next_at_level (path_elt, path_elt_next_index, level);
278 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700279 }
280
281 /* If this element is on more than two lists it has a vector of nexts */
Dave Barachc3799992016-08-15 11:12:27 -0400282 if (!(del_elt->n.next0[0] & 1))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700283 vec_free (del_elt->n.nexts);
284 pool_put (sp->elts, del_elt);
285 return CLIB_SLIST_MATCH;
286}
287
Dave Barachc3799992016-08-15 11:12:27 -0400288u8 *
289format_slist (u8 * s, va_list * args)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700290{
291 clib_slist_t *sl = va_arg (*args, clib_slist_t *);
292 int verbose = va_arg (*args, int);
293 int i;
294 clib_slist_elt_t *head_elt, *elt;
295
Dave Barachc3799992016-08-15 11:12:27 -0400296 s = format (s, "slist 0x%x, %u items, branching_factor %.2f\n", sl,
297 sl->occupancy ? sl->occupancy[0] : 0, sl->branching_factor);
298
299 if (pool_elts (sl->elts) == 0)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700300 return s;
Dave Barachc3799992016-08-15 11:12:27 -0400301
Ed Warnickecb9cada2015-12-08 15:45:58 -0700302 head_elt = pool_elt_at_index (sl->elts, 0);
Dave Barachc3799992016-08-15 11:12:27 -0400303
Ed Warnickecb9cada2015-12-08 15:45:58 -0700304 for (i = 0; i < vec_len (head_elt->n.nexts); i++)
305 {
Dave Barachc3799992016-08-15 11:12:27 -0400306 s = format (s, "level %d: %d elts\n", i,
307 sl->occupancy ? sl->occupancy[i] : 0);
308
309 if (verbose && head_elt->n.nexts[i] != (u32) ~ 0)
310 {
311 elt = pool_elt_at_index (sl->elts, head_elt->n.nexts[i]);
312 while (elt)
313 {
314 u32 next_index;
315 s = format (s, "%U(%d) ", sl->format_user_element,
316 elt->user_pool_index, elt - sl->elts);
317 next_index = clib_slist_get_next_at_level (elt, i);
318 ASSERT (next_index != 0x7fffffff);
319 if (next_index == (u32) ~ 0)
320 break;
321 else
322 elt = pool_elt_at_index (sl->elts, next_index);
323 }
324 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700325 s = format (s, "\n");
326 }
327 return s;
328}
Dave Barachc3799992016-08-15 11:12:27 -0400329
330/*
331 * fd.io coding-style-patch-verification: ON
332 *
333 * Local Variables:
334 * eval: (c-set-style "gnu")
335 * End:
336 */