258 lines
6.8 KiB
C
258 lines
6.8 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/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:
|
||
|
*/
|