2016-04-22 19:40:09 -07:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2008, 2009, 2010, 2013, 2014, 2016 Nicira, Inc.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* A non-exhaustive test for some of the functions and macros declared in
|
|
|
|
|
* ccmap.h. */
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#undef NDEBUG
|
|
|
|
|
#include "ccmap.h"
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <getopt.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include "bitmap.h"
|
|
|
|
|
#include "command-line.h"
|
|
|
|
|
#include "fat-rwlock.h"
|
|
|
|
|
#include "hash.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
|
#include "openvswitch/hmap.h"
|
2016-04-22 19:40:09 -07:00
|
|
|
|
#include "ovstest.h"
|
|
|
|
|
#include "ovs-thread.h"
|
|
|
|
|
#include "random.h"
|
|
|
|
|
#include "timeval.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
|
|
|
|
|
typedef size_t hash_func(int value);
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
compare_uint32s(const void *a_, const void *b_)
|
|
|
|
|
{
|
|
|
|
|
const uint32_t *a = a_;
|
|
|
|
|
const uint32_t *b = b_;
|
|
|
|
|
return *a < *b ? -1 : *a > *b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Verifies that 'ccmap' contains exactly the 'n' values in 'values'. */
|
|
|
|
|
static void
|
|
|
|
|
check_ccmap(struct ccmap *ccmap, const int values[], size_t n, hash_func *hash)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *hashes = xmalloc(sizeof *hashes * n);
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
hashes[i] = hash(values[i]);
|
|
|
|
|
}
|
|
|
|
|
qsort(hashes, n, sizeof *hashes, compare_uint32s);
|
|
|
|
|
|
|
|
|
|
/* Check that all the values are there in lookup. */
|
|
|
|
|
for (i = 0; i < n; i++) {
|
2017-08-02 15:03:06 -07:00
|
|
|
|
uint32_t h = hashes[i];
|
|
|
|
|
size_t count = ccmap_find(ccmap, h);
|
2016-04-22 19:40:09 -07:00
|
|
|
|
|
|
|
|
|
assert(count); /* Must have at least one. */
|
|
|
|
|
assert(i + count <= n); /* May not have too many. */
|
|
|
|
|
|
|
|
|
|
/* Skip colliding hash values and assert they were in the count. */
|
|
|
|
|
while (--count) {
|
|
|
|
|
i++;
|
2017-08-02 15:03:06 -07:00
|
|
|
|
assert(hashes[i] == h);
|
2016-04-22 19:40:09 -07:00
|
|
|
|
}
|
|
|
|
|
/* Make sure next hash is different. */
|
|
|
|
|
if (i + 1 < n) {
|
2017-08-02 15:03:06 -07:00
|
|
|
|
assert(hashes[i + 1] != h);
|
2016-04-22 19:40:09 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check counters. */
|
|
|
|
|
assert(ccmap_is_empty(ccmap) == !n);
|
|
|
|
|
assert(ccmap_count(ccmap) == n);
|
|
|
|
|
|
|
|
|
|
free(hashes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
shuffle(int *p, size_t n)
|
|
|
|
|
{
|
|
|
|
|
for (; n > 1; n--, p++) {
|
|
|
|
|
int *q = &p[random_range(n)];
|
|
|
|
|
int tmp = *p;
|
|
|
|
|
|
|
|
|
|
*p = *q;
|
|
|
|
|
*q = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
identity_hash(int value)
|
|
|
|
|
{
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
good_hash(int value)
|
|
|
|
|
{
|
|
|
|
|
return hash_int(value, 0x1234abcd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
constant_hash(int value OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
return 123;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tests basic ccmap increment and decrement. */
|
|
|
|
|
static void
|
|
|
|
|
test_ccmap_inc_dec(hash_func *hash)
|
|
|
|
|
{
|
|
|
|
|
enum { N_ELEMS = 1000 };
|
|
|
|
|
|
|
|
|
|
int values[N_ELEMS];
|
|
|
|
|
struct ccmap ccmap;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
ccmap_init(&ccmap);
|
|
|
|
|
for (i = 0; i < N_ELEMS; i++) {
|
|
|
|
|
ccmap_inc(&ccmap, hash(i));
|
|
|
|
|
values[i] = i;
|
|
|
|
|
check_ccmap(&ccmap, values, i + 1, hash);
|
|
|
|
|
}
|
|
|
|
|
shuffle(values, N_ELEMS);
|
2025-02-06 12:09:39 +01:00
|
|
|
|
for (i = 0; i < (N_ELEMS - 1); i++) {
|
2016-04-22 19:40:09 -07:00
|
|
|
|
ccmap_dec(&ccmap, hash(values[i]));
|
|
|
|
|
check_ccmap(&ccmap, values + (i + 1), N_ELEMS - (i + 1), hash);
|
|
|
|
|
}
|
|
|
|
|
ccmap_destroy(&ccmap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
run_test(void (*function)(hash_func *))
|
|
|
|
|
{
|
|
|
|
|
hash_func *hash_funcs[] = { identity_hash, good_hash, constant_hash };
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(hash_funcs); i++) {
|
|
|
|
|
function(hash_funcs[i]);
|
|
|
|
|
printf(".");
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
run_tests(struct ovs_cmdl_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
int n = ctx->argc >= 2 ? atoi(ctx->argv[1]) : 100;
|
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
|
run_test(test_ccmap_inc_dec);
|
|
|
|
|
}
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int n_elems; /* Number of elements to insert. */
|
|
|
|
|
static int n_threads; /* Number of threads to search and mutate. */
|
|
|
|
|
static uint32_t mutation_frac; /* % mutations, as fraction of UINT32_MAX. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void benchmark_ccmap(void);
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
elapsed(const struct timeval *start)
|
|
|
|
|
{
|
|
|
|
|
struct timeval end;
|
|
|
|
|
|
|
|
|
|
xgettimeofday(&end);
|
|
|
|
|
return timeval_to_msec(&end) - timeval_to_msec(start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
run_benchmarks(struct ovs_cmdl_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
n_elems = strtol(ctx->argv[1], NULL, 10);
|
|
|
|
|
n_threads = strtol(ctx->argv[2], NULL, 10);
|
|
|
|
|
mutation_frac = strtod(ctx->argv[3], NULL) / 100.0 * UINT32_MAX;
|
|
|
|
|
|
|
|
|
|
printf("Benchmarking with n=%d, %d threads, %.2f%% mutations\n",
|
|
|
|
|
n_elems, n_threads, (double) mutation_frac / UINT32_MAX * 100.);
|
|
|
|
|
|
|
|
|
|
benchmark_ccmap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ccmap benchmark. */
|
|
|
|
|
|
|
|
|
|
struct ccmap_aux {
|
|
|
|
|
struct ovs_mutex mutex;
|
|
|
|
|
struct ccmap *ccmap;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
search_ccmap(void *aux_)
|
|
|
|
|
{
|
|
|
|
|
struct ccmap_aux *aux = aux_;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
if (mutation_frac) {
|
|
|
|
|
for (i = 0; i < n_elems; i++) {
|
|
|
|
|
uint32_t hash = hash_int(i, 0);
|
|
|
|
|
|
|
|
|
|
if (random_uint32() < mutation_frac) {
|
|
|
|
|
ovs_mutex_lock(&aux->mutex);
|
|
|
|
|
uint32_t count = ccmap_find(aux->ccmap, hash);
|
|
|
|
|
if (count) {
|
|
|
|
|
ccmap_dec(aux->ccmap, hash);
|
|
|
|
|
}
|
|
|
|
|
ovs_mutex_unlock(&aux->mutex);
|
|
|
|
|
} else {
|
|
|
|
|
ignore(ccmap_find(aux->ccmap, hash));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (i = 0; i < n_elems; i++) {
|
|
|
|
|
ignore(ccmap_find(aux->ccmap, hash_int(i, 0)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
benchmark_ccmap(void)
|
|
|
|
|
{
|
|
|
|
|
struct ccmap ccmap;
|
|
|
|
|
struct timeval start;
|
|
|
|
|
pthread_t *threads;
|
|
|
|
|
struct ccmap_aux aux;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
/* Insertions. */
|
|
|
|
|
xgettimeofday(&start);
|
|
|
|
|
ccmap_init(&ccmap);
|
|
|
|
|
for (i = 0; i < n_elems; i++) {
|
|
|
|
|
ccmap_inc(&ccmap, hash_int(i, 0));
|
|
|
|
|
}
|
|
|
|
|
printf("ccmap insert: %5d ms\n", elapsed(&start));
|
|
|
|
|
|
|
|
|
|
/* Search and mutation. */
|
|
|
|
|
xgettimeofday(&start);
|
|
|
|
|
aux.ccmap = &ccmap;
|
|
|
|
|
ovs_mutex_init(&aux.mutex);
|
|
|
|
|
threads = xmalloc(n_threads * sizeof *threads);
|
|
|
|
|
for (i = 0; i < n_threads; i++) {
|
|
|
|
|
threads[i] = ovs_thread_create("search", search_ccmap, &aux);
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < n_threads; i++) {
|
|
|
|
|
xpthread_join(threads[i], NULL);
|
|
|
|
|
}
|
|
|
|
|
free(threads);
|
|
|
|
|
printf("ccmap search: %5d ms\n", elapsed(&start));
|
|
|
|
|
|
|
|
|
|
/* Destruction. */
|
|
|
|
|
xgettimeofday(&start);
|
|
|
|
|
for (i = 0; i < n_elems; i++) {
|
|
|
|
|
uint32_t hash = hash_int(i, 0);
|
|
|
|
|
|
|
|
|
|
if (ccmap_find(&ccmap, hash)) {
|
|
|
|
|
/* Also remove any colliding hashes. */
|
|
|
|
|
while (ccmap_dec(&ccmap, hash)) {
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ccmap_destroy(&ccmap);
|
|
|
|
|
printf("ccmap destroy: %5d ms\n", elapsed(&start));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const struct ovs_cmdl_command commands[] = {
|
2016-08-15 18:47:29 +00:00
|
|
|
|
{"check", NULL, 0, 1, run_tests, OVS_RO},
|
|
|
|
|
{"benchmark", NULL, 3, 3, run_benchmarks, OVS_RO},
|
|
|
|
|
{NULL, NULL, 0, 0, NULL, OVS_RO},
|
2016-04-22 19:40:09 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_ccmap_main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
struct ovs_cmdl_context ctx = {
|
|
|
|
|
.argc = argc - optind,
|
|
|
|
|
.argv = argv + optind,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
set_program_name(argv[0]);
|
|
|
|
|
ovs_cmdl_run_command(&ctx, commands);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OVSTEST_REGISTER("test-ccmap", test_ccmap_main);
|