blob: 19ac4edf2e2638dfe96975a10b26b4e0abba2b6b [file] [log] [blame]
/*
* 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/time.h>
#include <vppinfra/cache.h>
#include <vppinfra/error.h>
#include <vppinfra/heap.h>
#include <vppinfra/format.h>
#include <vppinfra/random.h>
#include <vppinfra/hash.h>
#include <vppinfra/flowhash_8_8.h>
/* Not actually tested here. But included for compilation purposes. */
#include <vppinfra/flowhash_24_16.h>
typedef struct
{
u64 seed;
u32 fixed_entries;
u32 collision_buckets;
u32 nitems;
u32 iterations;
u32 prefetch;
int non_random_keys;
uword *key_hash;
flowhash_lkey_8_8_t *keys;
flowhash_8_8_t *hash;
clib_time_t clib_time;
unformat_input_t *input;
} test_main_t;
test_main_t test_main;
static clib_error_t *
test_flowhash (test_main_t * tm)
{
f64 before, delta;
u64 total;
u32 overflow;
int i, j;
uword *p;
tm->hash = flowhash_alloc_8_8 (tm->fixed_entries, tm->collision_buckets);
if (tm->hash == NULL)
return clib_error_return (0, "Could not alloc hash");
fformat (stdout, "Allocated hash memory size: %llu\n",
flowhash_memory_size (tm->hash));
fformat (stdout, "Pick %lld unique %s keys...\n",
tm->nitems, tm->non_random_keys ? "non-random" : "random");
for (i = 0; i < tm->nitems; i++)
{
flowhash_lkey_8_8_t rndkey;
if (tm->non_random_keys == 0)
{
again:
rndkey.as_u64[0] = random_u64 (&tm->seed);
if ((p = hash_get (tm->key_hash, rndkey.as_u64[0])))
goto again;
}
else
rndkey.as_u64[0] = (u64) (i + 1) << 16;
hash_set (tm->key_hash, rndkey.as_u64[0], i + 1);
vec_add1 (tm->keys, rndkey);
}
hash_free (tm->key_hash);
/* Additions */
overflow = 0;
before = clib_time_now (&tm->clib_time);
fformat (stdout, "Adding %u items...\n", tm->nitems);
for (i = 0; i < tm->nitems; i++)
{
u32 hash = flowhash_hash_8_8 (&tm->keys[i]);
u32 ei;
flowhash_get_8_8 (tm->hash, &tm->keys[i], hash, 1, &ei);
if (flowhash_is_overflow (ei))
overflow++;
/* Set value (No matter if success) */
flowhash_value (tm->hash, ei)->as_u64[0] = i + 1;
/* Save value until time > 1 */
flowhash_timeout (tm->hash, ei) = 1;
}
delta = clib_time_now (&tm->clib_time) - before;
total = tm->nitems;
fformat (stdout, "%lld additions in %.6f seconds\n", total, delta);
if (delta > 0)
fformat (stdout, "%.f additions per second\n", ((f64) total) / delta);
fformat (stdout, "%u elements in table\n", flowhash_elts_8_8 (tm->hash, 1));
fformat (stdout, "Flowhash counters:\n");
fformat (stdout, " collision-lookup: %lu\n",
tm->hash->collision_lookup_counter);
fformat (stdout, " not-enough-buckets: %lu\n",
tm->hash->not_enough_buckets_counter);
fformat (stdout, " overflows: %lu\n", overflow);
/* Lookups (very similar to additions) */
overflow = 0;
before = clib_time_now (&tm->clib_time);
fformat (stdout, "Looking up %u items %u times...\n", tm->nitems,
tm->iterations);
for (j = 0; j < tm->iterations; j++)
{
i = 0;
if (tm->prefetch)
for (; i < tm->nitems - tm->prefetch; i++)
{
u32 ei;
u32 hash = flowhash_hash_8_8 (&tm->keys[i + tm->prefetch]);
flowhash_prefetch (tm->hash, hash);
hash = flowhash_hash_8_8 (&tm->keys[i]);
flowhash_get_8_8 (tm->hash, &tm->keys[i], hash, 1, &ei);
if (flowhash_is_overflow (ei))
overflow++;
else if (flowhash_timeout (tm->hash, ei) != 1)
clib_warning ("Key not found: %lld\n", tm->keys[i].as_u64[0]);
else if (flowhash_value (tm->hash, ei)->as_u64[0] != i + 1)
clib_warning ("Value mismatch for key %lld\n",
tm->keys[i].as_u64[0]);
}
for (; i < tm->nitems; i++)
{
u32 ei;
u32 hash = flowhash_hash_8_8 (&tm->keys[i]);
flowhash_get_8_8 (tm->hash, &tm->keys[i], hash, 1, &ei);
if (flowhash_is_overflow (ei))
overflow++;
else if (flowhash_timeout (tm->hash, ei) != 1)
clib_warning ("Key not found: %lld\n", tm->keys[i].as_u64[0]);
else if (flowhash_value (tm->hash, ei)->as_u64[0] != i + 1)
clib_warning ("Value mismatch for key %lld\n",
tm->keys[i].as_u64[0]);
}
}
delta = clib_time_now (&tm->clib_time) - before;
total = tm->nitems * tm->iterations;
fformat (stdout, "%lld lookups in %.6f seconds\n", total, delta);
if (delta > 0)
fformat (stdout, "%.f lookups per second\n", ((f64) total) / delta);
/* Delete */
for (i = 0; i < tm->nitems; i++)
{
u32 hash = flowhash_hash_8_8 (&tm->keys[i]);
u32 ei;
flowhash_get_8_8 (tm->hash, &tm->keys[i], hash, 1, &ei);
flowhash_timeout (tm->hash, ei) = 0;
}
fformat (stdout, "%u elements in table\n", flowhash_elts_8_8 (tm->hash, 1));
vec_free (tm->keys);
flowhash_free_8_8 (tm->hash);
return NULL;
}
clib_error_t *
test_flowhash_main (test_main_t * tm)
{
unformat_input_t *i = tm->input;
clib_error_t *error;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "seed %u", &tm->seed))
;
else if (unformat (i, "fixed-entries %d", &tm->fixed_entries))
;
else if (unformat (i, "collision-buckets %d", &tm->collision_buckets))
;
else if (unformat (i, "non-random-keys"))
tm->non_random_keys = 1;
else if (unformat (i, "nitems %d", &tm->nitems))
;
else if (unformat (i, "prefetch %d", &tm->prefetch))
;
else if (unformat (i, "iterations %d", &tm->iterations))
;
else
return clib_error_return (0, "unknown input '%U'",
format_unformat_error, i);
}
error = test_flowhash (tm);
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->fixed_entries = 8 << 20;
tm->collision_buckets = 1 << 20;
tm->seed = 0xdeadf00l;
tm->iterations = 1;
tm->input = &i;
tm->nitems = 1000;
tm->non_random_keys = 0;
tm->key_hash = hash_create (0, sizeof (uword));
tm->prefetch = 0;
clib_time_init (&tm->clib_time);
unformat_init_command_line (&i, argv);
error = test_flowhash_main (tm);
unformat_free (&i);
if (error)
{
clib_error_report (error);
return 1;
}
return 0;
return 0;
}
#endif /* CLIB_UNIX */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/