053d093524
Type: improvement Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Id28299a188feefa1899d835fd499f018af95d81b
336 lines
8.1 KiB
C
336 lines
8.1 KiB
C
/*
|
|
* Copyright (c) 2015 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/bitmap.h>
|
|
#include <vppinfra/os.h>
|
|
#include <vppinfra/qhash.h>
|
|
#include <vppinfra/random.h>
|
|
#include <vppinfra/time.h>
|
|
|
|
typedef struct
|
|
{
|
|
u32 n_iter, seed, n_keys, n_hash_keys, verbose;
|
|
|
|
u32 max_vector;
|
|
|
|
uword *hash;
|
|
|
|
uword *keys_in_hash_bitmap;
|
|
|
|
u32 *qhash;
|
|
|
|
uword *keys;
|
|
|
|
uword *lookup_keys;
|
|
uword *lookup_key_indices;
|
|
u32 *lookup_results;
|
|
|
|
u32 *get_multiple_results;
|
|
|
|
clib_time_t time;
|
|
|
|
f64 overflow_fraction, ave_elts;
|
|
f64 get_time, hash_get_time;
|
|
f64 set_time, set_count;
|
|
f64 unset_time, unset_count;
|
|
f64 hash_set_time, hash_unset_time;
|
|
} test_qhash_main_t;
|
|
|
|
clib_error_t *
|
|
test_qhash_main (unformat_input_t * input)
|
|
{
|
|
clib_error_t *error = 0;
|
|
test_qhash_main_t _tm, *tm = &_tm;
|
|
uword i, iter;
|
|
|
|
clib_memset (tm, 0, sizeof (tm[0]));
|
|
tm->n_iter = 10;
|
|
tm->seed = 1;
|
|
tm->n_keys = 10;
|
|
tm->max_vector = 1;
|
|
|
|
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
|
|
{
|
|
if (unformat (input, "iter %d", &tm->n_iter))
|
|
;
|
|
else if (unformat (input, "seed %d", &tm->seed))
|
|
;
|
|
else if (unformat (input, "keys %d", &tm->n_keys))
|
|
;
|
|
else if (unformat (input, "size %d", &tm->n_hash_keys))
|
|
;
|
|
else if (unformat (input, "vector %d", &tm->max_vector))
|
|
;
|
|
else if (unformat (input, "verbose"))
|
|
tm->verbose = 1;
|
|
else
|
|
{
|
|
error = clib_error_create ("unknown input `%U'\n",
|
|
format_unformat_error, input);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (!tm->seed)
|
|
tm->seed = random_default_seed ();
|
|
|
|
clib_time_init (&tm->time);
|
|
|
|
clib_warning ("iter %d, seed %u, keys %d, max vector %d, ",
|
|
tm->n_iter, tm->seed, tm->n_keys, tm->max_vector);
|
|
|
|
vec_resize (tm->keys, tm->n_keys);
|
|
vec_resize (tm->get_multiple_results, tm->n_keys);
|
|
for (i = 0; i < vec_len (tm->keys); i++)
|
|
tm->keys[i] = random_uword (&tm->seed);
|
|
|
|
if (!tm->n_hash_keys)
|
|
tm->n_hash_keys = 2 * max_pow2 (tm->n_keys);
|
|
tm->n_hash_keys = clib_max (tm->n_keys, tm->n_hash_keys);
|
|
qhash_resize (tm->qhash, tm->n_hash_keys);
|
|
|
|
{
|
|
qhash_t *h = qhash_header (tm->qhash);
|
|
int i;
|
|
for (i = 0; i < ARRAY_LEN (h->hash_seeds); i++)
|
|
h->hash_seeds[i] = random_uword (&tm->seed);
|
|
}
|
|
|
|
vec_resize (tm->lookup_keys, tm->max_vector);
|
|
vec_resize (tm->lookup_key_indices, tm->max_vector);
|
|
vec_resize (tm->lookup_results, tm->max_vector);
|
|
|
|
for (iter = 0; iter < tm->n_iter; iter++)
|
|
{
|
|
uword *p, j, n, is_set;
|
|
|
|
n = tm->max_vector;
|
|
|
|
is_set = random_u32 (&tm->seed) & 1;
|
|
is_set |= hash_elts (tm->hash) < (tm->n_keys / 4);
|
|
if (hash_elts (tm->hash) > (3 * tm->n_keys) / 4)
|
|
is_set = 0;
|
|
|
|
_vec_len (tm->lookup_keys) = n;
|
|
_vec_len (tm->lookup_key_indices) = n;
|
|
j = 0;
|
|
while (j < n)
|
|
{
|
|
i = random_u32 (&tm->seed) % vec_len (tm->keys);
|
|
if (clib_bitmap_get (tm->keys_in_hash_bitmap, i) != is_set)
|
|
{
|
|
f64 t[2];
|
|
tm->lookup_key_indices[j] = i;
|
|
tm->lookup_keys[j] = tm->keys[i];
|
|
t[0] = clib_time_now (&tm->time);
|
|
if (is_set)
|
|
hash_set (tm->hash, tm->keys[i], i);
|
|
else
|
|
hash_unset (tm->hash, tm->keys[i]);
|
|
t[1] = clib_time_now (&tm->time);
|
|
if (is_set)
|
|
tm->hash_set_time += t[1] - t[0];
|
|
else
|
|
tm->hash_unset_time += t[1] - t[0];
|
|
tm->keys_in_hash_bitmap
|
|
= clib_bitmap_set (tm->keys_in_hash_bitmap, i, is_set);
|
|
j++;
|
|
}
|
|
}
|
|
|
|
{
|
|
f64 t[2];
|
|
|
|
if (is_set)
|
|
{
|
|
t[0] = clib_time_now (&tm->time);
|
|
qhash_set_multiple (tm->qhash,
|
|
tm->lookup_keys,
|
|
vec_len (tm->lookup_keys),
|
|
tm->lookup_results);
|
|
t[1] = clib_time_now (&tm->time);
|
|
tm->set_time += t[1] - t[0];
|
|
tm->set_count += vec_len (tm->lookup_keys);
|
|
for (i = 0; i < vec_len (tm->lookup_keys); i++)
|
|
{
|
|
uword r = tm->lookup_results[i];
|
|
*vec_elt_at_index (tm->qhash, r) = tm->lookup_key_indices[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
t[0] = clib_time_now (&tm->time);
|
|
qhash_unset_multiple (tm->qhash,
|
|
tm->lookup_keys,
|
|
vec_len (tm->lookup_keys),
|
|
tm->lookup_results);
|
|
t[1] = clib_time_now (&tm->time);
|
|
tm->unset_time += t[1] - t[0];
|
|
tm->unset_count += vec_len (tm->lookup_keys);
|
|
|
|
for (i = 0; i < vec_len (tm->lookup_keys); i++)
|
|
{
|
|
uword r = tm->lookup_results[i];
|
|
*vec_elt_at_index (tm->qhash, r) = ~0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (qhash_elts (tm->qhash) != hash_elts (tm->hash))
|
|
os_panic ();
|
|
|
|
{
|
|
qhash_t *h;
|
|
uword i, k, l, count;
|
|
|
|
h = qhash_header (tm->qhash);
|
|
|
|
for (i = k = 0; k < vec_len (h->hash_key_valid_bitmap); k++)
|
|
i += count_set_bits (h->hash_key_valid_bitmap[k]);
|
|
k = hash_elts (h->overflow_hash);
|
|
l = qhash_elts (tm->qhash);
|
|
if (i + k != l)
|
|
os_panic ();
|
|
|
|
count = hash_elts (h->overflow_hash);
|
|
for (i = 0; i < (1 << h->log2_hash_size); i++)
|
|
count += tm->qhash[i] != ~0;
|
|
if (count != qhash_elts (tm->qhash))
|
|
os_panic ();
|
|
|
|
{
|
|
u32 *tmp = 0;
|
|
|
|
/* *INDENT-OFF* */
|
|
hash_foreach (k, l, h->overflow_hash, ({
|
|
j = qhash_hash_mix (h, k) / QHASH_KEYS_PER_BUCKET;
|
|
vec_validate (tmp, j);
|
|
tmp[j] += 1;
|
|
}));
|
|
/* *INDENT-ON* */
|
|
|
|
for (k = 0; k < vec_len (tmp); k++)
|
|
{
|
|
if (k >= vec_len (h->overflow_counts))
|
|
os_panic ();
|
|
if (h->overflow_counts[k] != tmp[k])
|
|
os_panic ();
|
|
}
|
|
for (; k < vec_len (h->overflow_counts); k++)
|
|
if (h->overflow_counts[k] != 0)
|
|
os_panic ();
|
|
|
|
vec_free (tmp);
|
|
}
|
|
}
|
|
|
|
{
|
|
f64 t[2];
|
|
|
|
t[0] = clib_time_now (&tm->time);
|
|
qhash_get_multiple (tm->qhash, tm->keys, vec_len (tm->keys),
|
|
tm->get_multiple_results);
|
|
t[1] = clib_time_now (&tm->time);
|
|
tm->get_time += t[1] - t[0];
|
|
|
|
for (i = 0; i < vec_len (tm->keys); i++)
|
|
{
|
|
u32 r;
|
|
|
|
t[0] = clib_time_now (&tm->time);
|
|
p = hash_get (tm->hash, tm->keys[i]);
|
|
t[1] = clib_time_now (&tm->time);
|
|
tm->hash_get_time += t[1] - t[0];
|
|
|
|
r = qhash_get (tm->qhash, tm->keys[i]);
|
|
if (p)
|
|
{
|
|
if (p[0] != i)
|
|
os_panic ();
|
|
if (*vec_elt_at_index (tm->qhash, r) != i)
|
|
os_panic ();
|
|
}
|
|
else
|
|
{
|
|
if (r != ~0)
|
|
os_panic ();
|
|
}
|
|
if (r != tm->get_multiple_results[i])
|
|
os_panic ();
|
|
}
|
|
}
|
|
|
|
tm->overflow_fraction +=
|
|
((f64) qhash_n_overflow (tm->qhash) / qhash_elts (tm->qhash));
|
|
tm->ave_elts += qhash_elts (tm->qhash);
|
|
}
|
|
|
|
fformat (stderr, "%d iter %.6e overflow, %.4f ave. elts\n",
|
|
tm->n_iter,
|
|
tm->overflow_fraction / tm->n_iter, tm->ave_elts / tm->n_iter);
|
|
|
|
tm->get_time /= tm->n_iter * vec_len (tm->keys);
|
|
tm->hash_get_time /= tm->n_iter * vec_len (tm->keys);
|
|
|
|
tm->set_time /= tm->set_count;
|
|
tm->unset_time /= tm->unset_count;
|
|
tm->hash_set_time /= tm->set_count;
|
|
tm->hash_unset_time /= tm->unset_count;
|
|
|
|
fformat (stderr,
|
|
"get/set/unset clocks %.2e %.2e %.2e clib %.2e %.2e %.2e ratio %.2f %.2f %.2f\n",
|
|
tm->get_time * tm->time.clocks_per_second,
|
|
tm->set_time * tm->time.clocks_per_second,
|
|
tm->unset_time * tm->time.clocks_per_second,
|
|
tm->hash_get_time * tm->time.clocks_per_second,
|
|
tm->hash_set_time * tm->time.clocks_per_second,
|
|
tm->hash_unset_time * tm->time.clocks_per_second,
|
|
tm->hash_get_time / tm->get_time, tm->hash_set_time / tm->set_time,
|
|
tm->hash_unset_time / tm->unset_time);
|
|
|
|
|
|
done:
|
|
return error;
|
|
}
|
|
|
|
#ifdef CLIB_UNIX
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
unformat_input_t i;
|
|
clib_error_t *error;
|
|
|
|
clib_mem_init (0, 64ULL << 20);
|
|
|
|
unformat_init_command_line (&i, argv);
|
|
error = test_qhash_main (&i);
|
|
unformat_free (&i);
|
|
if (error)
|
|
{
|
|
clib_error_report (error);
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
#endif /* CLIB_UNIX */
|
|
|
|
/*
|
|
* fd.io coding-style-patch-verification: ON
|
|
*
|
|
* Local Variables:
|
|
* eval: (c-set-style "gnu")
|
|
* End:
|
|
*/
|