summaryrefslogtreecommitdiffstats
path: root/src/vppinfra/test_bihash_vec88.c
blob: a57ceec301bf6ffa5623c897b843ef2353ef4f0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*
 * Copyright (c) 2017 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <vppinfra/time.h>
#include <vppinfra/cache.h>
#include <vppinfra/error.h>

#include <vppinfra/bihash_vec8_8.h>
#include <vppinfra/bihash_template.h>

#include <vppinfra/bihash_template.c>

typedef struct
{
  u32 seed;
  u32 nbuckets;
  u32 nitems;
  u32 search_iter;
  int careful_delete_tests;
  int verbose;
  int non_random_keys;
  uword *key_hash;
  u8 **keys;
    BVT (clib_bihash) hash;
  clib_time_t clib_time;

  unformat_input_t *input;

} test_main_t;

test_main_t test_main;

uword
vl (void *v)
{
  return vec_len (v);
}

static clib_error_t *
test_bihash (test_main_t * tm)
{
  int i, j;
  uword *p;
  uword total_searches;
  f64 before, delta;
  BVT (clib_bihash) * h;
  BVT (clib_bihash_kv) kv;

  h = &tm->hash;

  BV (clib_bihash_init) (h, "test", tm->nbuckets, 3ULL << 30);

  fformat (stdout, "Pick %lld unique %s keys...\n",
	   tm->nitems, tm->non_random_keys ? "non-random" : "random");

  for (i = 0; i < tm->nitems; i++)
    {
      u8 *random_vec8;

      if (tm->non_random_keys == 0)
	{
	  u32 len;

	again:
	  do
	    {
	      len = random_u32 (&tm->seed);
	      len %= 64;
	    }
	  while (len == 0);

	  random_vec8 = random_string (&tm->seed, len);
	  vec_add1 (random_vec8, 0);

	  p = hash_get_mem (tm->key_hash, random_vec8);
	  if (p)
	    goto again;
	}
      else
	{
	  random_vec8 = format (0, "String%d%c", i, 0);
	}

      hash_set_mem (tm->key_hash, random_vec8, i + 1);
      vec_add1 (tm->keys, random_vec8);
    }

  fformat (stdout, "Add items...\n");
  for (i = 0; i < tm->nitems; i++)
    {
      kv.key = (u64) tm->keys[i];
      kv.value = i + 1;

      BV (clib_bihash_add_del) (h, &kv, 1 /* is_add */ );

      if (tm->verbose > 1)
	{
	  fformat (stdout, "--------------------\n");
	  fformat (stdout, "After adding key %llu value %lld...\n",
		   tm->keys[i], (u64) (i + 1));
	  fformat (stdout, "%U", BV (format_bihash), h,
		   2 /* very verbose */ );
	}
    }

  fformat (stdout, "%U", BV (format_bihash), h, 0 /* very verbose */ );

  fformat (stdout, "Search for items %d times...\n", tm->search_iter);

  before = clib_time_now (&tm->clib_time);

  for (j = 0; j < tm->search_iter; j++)
    {
      for (i = 0; i < tm->nitems; i++)
	{
	  kv.key = (u64) tm->keys[i];
	  if (BV (clib_bihash_search) (h, &kv, &kv) < 0)
	    if (BV (clib_bihash_search) (h, &kv, &kv) < 0)
	      clib_warning ("[%d] search for key %lld failed unexpectedly\n",
			    i, tm->keys[i]);
	  if (kv.value != (u64) (i + 1))
	    clib_warning
	      ("[%d] search for key %lld returned %lld, not %lld\n", i,
	       tm->keys, kv.value, (u64) (i + 1));
	}
    }

  delta = clib_time_now (&tm->clib_time) - before;
  total_searches = (uword) tm->search_iter * (uword) tm->nitems;

  if (delta > 0)
    fformat (stdout, "%.f searches per second\n",
	     ((f64) total_searches) / delta);

  fformat (stdout, "%lld searches in %.6f seconds\n", total_searches, delta);

  fformat (stdout, "Standard E-hash search for items %d times...\n",
	   tm->search_iter);

  before = clib_time_now (&tm->clib_time);

  for (j = 0; j < tm->search_iter; j++)
    {
      for (i = 0; i < tm->nitems; i++)
	{
	  p = hash_get_mem (tm->key_hash, tm->keys[i]);
	  if (p == 0 || p[0] != (uword) (i + 1))
	    clib_warning ("ugh, couldn't find %lld\n", tm->keys[i]);
	}
    }

  delta = clib_time_now (&tm->clib_time) - before;
  total_searches = (uword) tm->search_iter * (uword) tm->nitems;

  fformat (stdout, "%lld searches in %.6f seconds\n", total_searches, delta);

  if (delta > 0)
    fformat (stdout, "%.f searches per second\n",
	     ((f64) total_searches) / delta);

  fformat (stdout, "Delete items...\n");

  for (i = 0; i < tm->nitems; i++)
    {
      int j;
      int rv;

      kv.key = (u64) tm->keys[i];
      kv.value = (u64) (i + 1);
      rv = BV (clib_bihash_add_del) (h, &kv, 0 /* is_add */ );

      if (rv < 0)
	clib_warning ("delete key %lld not ok but should be", tm->keys[i]);

      if (tm->careful_delete_tests)
	{
	  for (j = 0; j < tm->nitems; j++)
	    {
	      kv.key = (u64) tm->keys[j];
	      rv = BV (clib_bihash_search) (h, &kv, &kv);
	      if (j <= i && rv >= 0)
		{
		  clib_warning
		    ("i %d j %d search ok but should not be, value %lld",
		     i, j, kv.value);
		}
	      if (j > i && rv < 0)
		{
		  clib_warning ("i %d j %d search not ok but should be",
				i, j);
		}
	    }
	}
    }

  fformat (stdout, "After deletions, should be empty...\n");

  fformat (stdout, "%U", BV (format_bihash), h, 0 /* very verbose */ );
  return 0;
}

clib_error_t *
test_bihash_main (test_main_t * tm)
{
  unformat_input_t *i = tm->input;
  clib_error_t *error;
  int which = 0;

  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (i, "seed %u", &tm->seed))
	;

      else if (unformat (i, "nbuckets %d", &tm->nbuckets))
	;
      else if (unformat (i, "non-random-keys"))
	tm->non_random_keys = 1;
      else if (unformat (i, "nitems %d", &tm->nitems))
	;
      else if (unformat (i, "careful %d", &tm->careful_delete_tests))
	;
      else if (unformat (i, "verbose %d", &tm->verbose))
	;
      else if (unformat (i, "search %d", &tm->search_iter))
	;
      else if (unformat (i, "vec64"))
	which = 1;
      else if (unformat (i, "cache"))
	which = 2;

      else if (unformat (i, "verbose"))
	tm->verbose = 1;
      else
	return clib_error_return (0, "unknown input '%U'",
				  format_unformat_error, i);
    }

  switch (which)
    {
    case 0:
      error = test_bihash (tm);
      break;

    default:
      return clib_error_return (0, "no such test?");
    }

  return error;
}

#ifdef CLIB_UNIX
int
main (int argc, char *argv[])
{
  unformat_input_t i;
  clib_error_t *error;
  test_main_t *tm = &test_main;

  clib_mem_init (0, 3ULL << 30);

  tm->input = &i;
  tm->seed = 0xdeaddabe;

  tm->nbuckets = 2;
  tm->nitems = 5;
  tm->verbose = 1;
  tm->search_iter = 1;
  tm->careful_delete_tests = 0;
  tm->key_hash = hash_create_string (0, sizeof (uword));
  clib_time_init (&tm->clib_time);

  unformat_init_command_line (&i, argv);
  error = test_bihash_main (tm);
  unformat_free (&i);

  if (error)
    {
      clib_error_report (error);
      return 1;
    }
  return 0;
}
#endif /* CLIB_UNIX */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
ss="p">; nf->node_runtime_index = ~0; } /* A frame pending dispatch by main loop. */ typedef struct { /* Node and runtime for this frame. */ u32 node_runtime_index; /* Frame index (in the heap). */ u32 frame_index; /* Start of next frames for this node. */ u32 next_frame_index; /* Special value for next_frame_index when there is no next frame. */ #define VLIB_PENDING_FRAME_NO_NEXT_FRAME ((u32) ~0) } vlib_pending_frame_t; typedef struct vlib_node_runtime_t { CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); /**< cacheline mark */ vlib_node_function_t *function; /**< Node function to call. */ vlib_error_t *errors; /**< Vector of errors for this node. */ #if __SIZEOF_POINTER__ == 4 u8 pad[8]; #endif u32 clocks_since_last_overflow; /**< Number of clock cycles. */ u32 max_clock; /**< Maximum clock cycle for an invocation. */ u32 max_clock_n; /**< Number of vectors in the recorded max_clock. */ u32 calls_since_last_overflow; /**< Number of calls. */ u32 vectors_since_last_overflow; /**< Number of vector elements processed by this node. */ u32 next_frame_index; /**< Start of next frames for this node. */ u32 node_index; /**< Node index. */ u32 input_main_loops_per_call; /**< For input nodes: decremented on each main loop interation until it reaches zero and function is called. Allows some input nodes to be called more than others. */ u32 main_loop_count_last_dispatch; /**< Saved main loop counter of last dispatch of this node. */ u32 main_loop_vector_stats[2]; u16 flags; /**< Copy of main node flags. */ u16 state; /**< Input node state. */ u16 n_next_nodes; u16 cached_next_index; /**< Next frame index that vector arguments were last enqueued to last time this node ran. Set to zero before first run of this node. */ u16 thread_index; /**< thread this node runs on */ u8 runtime_data[0]; /**< Function dependent node-runtime data. This data is thread local, and it is not cloned from main thread. It needs to be initialized for each thread before it is used unless runtime_data template exists in vlib_node_t. */ } vlib_node_runtime_t; #define VLIB_NODE_RUNTIME_DATA_SIZE (sizeof (vlib_node_runtime_t) - STRUCT_OFFSET_OF (vlib_node_runtime_t, runtime_data)) typedef struct { /* Number of allocated frames for this scalar/vector size. */ u32 n_alloc_frames; /* Vector of free frame indices for this scalar/vector size. */ u32 *free_frame_indices; } vlib_frame_size_t; typedef struct { /* Users opaque value for event type. */ uword opaque; } vlib_process_event_type_t; typedef struct { /* Node runtime for this process. */ vlib_node_runtime_t node_runtime; /* Where to longjmp when process is done. */ clib_longjmp_t return_longjmp; #define VLIB_PROCESS_RETURN_LONGJMP_RETURN ((uword) ~0 - 0) #define VLIB_PROCESS_RETURN_LONGJMP_SUSPEND ((uword) ~0 - 1) /* Where to longjmp to resume node after suspend. */ clib_longjmp_t resume_longjmp; #define VLIB_PROCESS_RESUME_LONGJMP_SUSPEND 0 #define VLIB_PROCESS_RESUME_LONGJMP_RESUME 1 u16 flags; #define VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_CLOCK (1 << 0) #define VLIB_PROCESS_IS_SUSPENDED_WAITING_FOR_EVENT (1 << 1) /* Set to indicate that this process has been added to resume vector. */ #define VLIB_PROCESS_RESUME_PENDING (1 << 2) /* Process function is currently running. */ #define VLIB_PROCESS_IS_RUNNING (1 << 3) /* Size of process stack. */ u16 log2_n_stack_bytes; u32 suspended_process_frame_index; /* Number of times this process was suspended. */ u32 n_suspends; /* Vectors of pending event data indexed by event type index. */ void **pending_event_data_by_type_index; /* Bitmap of event type-indices with non-empty vectors. */ uword *non_empty_event_type_bitmap; /* Bitmap of event type-indices which are one time events. */ uword *one_time_event_type_bitmap; /* Type is opaque pointer -- typically a pointer to an event handler function. Hash table to map opaque to a type index. */ uword *event_type_index_by_type_opaque; /* Pool of currently valid event types. */ vlib_process_event_type_t *event_type_pool; /* * When suspending saves clock time (10us ticks) when process * is to be resumed. */ u64 resume_clock_interval; /* Handle from timer code, to cancel an unexpired timer */ u32 stop_timer_handle; /* Default output function and its argument for any CLI outputs within the process. */ vlib_cli_output_function_t *output_function; uword output_function_arg; #ifdef CLIB_UNIX /* Pad to a multiple of the page size so we can mprotect process stacks */ #define PAGE_SIZE_MULTIPLE 0x1000 #define ALIGN_ON_MULTIPLE_PAGE_BOUNDARY_FOR_MPROTECT __attribute__ ((aligned (PAGE_SIZE_MULTIPLE))) #else #define ALIGN_ON_MULTIPLE_PAGE_BOUNDARY_FOR_MPROTECT #endif /* Process stack. Starts here and extends 2^log2_n_stack_bytes bytes. */ #define VLIB_PROCESS_STACK_MAGIC (0xdead7ead) u32 stack[0] ALIGN_ON_MULTIPLE_PAGE_BOUNDARY_FOR_MPROTECT; } vlib_process_t __attribute__ ((aligned (CLIB_CACHE_LINE_BYTES))); #ifdef CLIB_UNIX /* Ensure that the stack is aligned on the multiple of the page size */ typedef char assert_process_stack_must_be_aligned_exactly_to_page_size_multiple[(sizeof (vlib_process_t) - PAGE_SIZE_MULTIPLE) == 0 ? 0 : -1]; #endif typedef struct { u32 node_index; u32 one_time_event; } vlib_one_time_waiting_process_t; typedef struct { u16 n_data_elts; u16 n_data_elt_bytes; /* n_data_elts * n_data_elt_bytes */ u32 n_data_bytes; /* Process node & event type to be used to signal event. */ u32 process_node_index; u32 event_type_index; union { u8 inline_event_data[64 - 3 * sizeof (u32) - 2 * sizeof (u16)]; /* Vector of event data used only when data does not fit inline. */ u8 *event_data_as_vector; }; } vlib_signal_timed_event_data_t; always_inline uword vlib_timing_wheel_data_is_timed_event (u32 d) { return d & 1; } always_inline u32 vlib_timing_wheel_data_set_suspended_process (u32 i) { return 0 + 2 * i; } always_inline u32 vlib_timing_wheel_data_set_timed_event (u32 i) { return 1 + 2 * i; } always_inline uword vlib_timing_wheel_data_get_index (u32 d) { return d / 2; } typedef struct { /* Public nodes. */ vlib_node_t **nodes; /* Node index hashed by node name. */ uword *node_by_name; u32 flags; #define VLIB_NODE_MAIN_RUNTIME_STARTED (1 << 0) /* Nodes segregated by type for cache locality. Does not apply to nodes of type VLIB_NODE_TYPE_INTERNAL. */ vlib_node_runtime_t *nodes_by_type[VLIB_N_NODE_TYPE]; /* Node runtime indices for input nodes with pending interrupts. */ u32 *pending_interrupt_node_runtime_indices; clib_spinlock_t pending_interrupt_lock; /* Input nodes are switched from/to interrupt to/from polling mode when average vector length goes above/below polling/interrupt thresholds. */ u32 polling_threshold_vector_length; u32 interrupt_threshold_vector_length; /* Vector of next frames. */ vlib_next_frame_t *next_frames; /* Vector of internal node's frames waiting to be called. */ vlib_pending_frame_t *pending_frames; /* Timing wheel for scheduling time-based node dispatch. */ void *timing_wheel; vlib_signal_timed_event_data_t *signal_timed_event_data_pool; /* Opaque data vector added via timing_wheel_advance. */ u32 *data_from_advancing_timing_wheel; /* CPU time of next process to be ready on timing wheel. */ f64 time_next_process_ready; /* Vector of process nodes. One for each node of type VLIB_NODE_TYPE_PROCESS. */ vlib_process_t **processes; /* Current running process or ~0 if no process running. */ u32 current_process_index; /* Pool of pending process frames. */ vlib_pending_frame_t *suspended_process_frames; /* Vector of event data vectors pending recycle. */ void **recycled_event_data_vectors; /* Current counts of nodes in each state. */ u32 input_node_counts_by_state[VLIB_N_NODE_STATE]; /* Hash of (scalar_size,vector_size) to frame_sizes index. */ uword *frame_size_hash; /* Per-size frame allocation information. */ vlib_frame_size_t *frame_sizes; /* Time of last node runtime stats clear. */ f64 time_last_runtime_stats_clear; /* Node registrations added by constructors */ vlib_node_registration_t *node_registrations; } vlib_node_main_t; #define FRAME_QUEUE_MAX_NELTS 32 typedef struct { CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); u64 head; u64 head_hint; u64 tail; u32 n_in_use; u32 nelts; u32 written; u32 threshold; i32 n_vectors[FRAME_QUEUE_MAX_NELTS]; } frame_queue_trace_t; typedef struct { u64 count[FRAME_QUEUE_MAX_NELTS]; } frame_queue_nelt_counter_t; #endif /* included_vlib_node_h */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */