Skip to content

Commit

Permalink
tests: Add very simple conntrack benchmark.
Browse files Browse the repository at this point in the history
This introduces a very limited but simple benchmark for
conntrack_execute(). It just sends repeatedly the same batch of packets
through the connection tracker and returns the time spent to process
them.

While this is not a realistic benchmark, it has proven useful during
development to evaluate different batching and locking strategies.

E.g. the line:

`./tests/ovstest test-conntrack benchmark 1 14880000 32`

starts 1 thread that will send 14880000 packets to the connection
tracker, 32 at a time. It will print the time taken to process them.

Signed-off-by: Daniele Di Proietto <[email protected]>
Acked-by: Flavio Leitner <[email protected]>
  • Loading branch information
ddiproietto committed Jul 28, 2016
1 parent 6c54734 commit 8cb1462
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 0 deletions.
1 change: 1 addition & 0 deletions tests/automake.mk
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ tests_ovstest_SOURCES = \
tests/test-classifier.c \
tests/test-ccmap.c \
tests/test-cmap.c \
tests/test-conntrack.c \
tests/test-csum.c \
tests/test-flows.c \
tests/test-hash.c \
Expand Down
172 changes: 172 additions & 0 deletions tests/test-conntrack.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2015 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.
*/

#include <config.h>
#include "conntrack.h"

#include "dp-packet.h"
#include "fatal-signal.h"
#include "flow.h"
#include "netdev.h"
#include "ovs-thread.h"
#include "ovstest.h"
#include "timeval.h"

static const char payload[] = "50540000000a50540000000908004500001c0000000000"
"11a4cd0a0101010a0101020001000200080000";

static struct dp_packet_batch *
prepare_packets(size_t n, bool change, unsigned tid)
{
struct dp_packet_batch *pkt_batch = xzalloc(sizeof *pkt_batch);
struct flow flow;
size_t i;

ovs_assert(n <= ARRAY_SIZE(pkt_batch->packets));

dp_packet_batch_init(pkt_batch);
pkt_batch->count = n;

for (i = 0; i < n; i++) {
struct udp_header *udp;
struct dp_packet *pkt = dp_packet_new(sizeof payload/2);

dp_packet_put_hex(pkt, payload, NULL);
flow_extract(pkt, &flow);

udp = dp_packet_l4(pkt);
udp->udp_src = htons(ntohs(udp->udp_src) + tid);

if (change) {
udp->udp_dst = htons(ntohs(udp->udp_dst) + i);
}

pkt_batch->packets[i] = pkt;
}

return pkt_batch;
}

static void
destroy_packets(struct dp_packet_batch *pkt_batch)
{
dp_packet_delete_batch(pkt_batch, true);
free(pkt_batch);
}

struct thread_aux {
pthread_t thread;
unsigned tid;
};

static struct conntrack ct;
static unsigned long n_threads, n_pkts, batch_size;
static bool change_conn = false;
static struct ovs_barrier barrier;

static void *
ct_thread_main(void *aux_)
{
struct thread_aux *aux = aux_;
struct dp_packet_batch *pkt_batch;
size_t i;

pkt_batch = prepare_packets(batch_size, change_conn, aux->tid);
ovs_barrier_block(&barrier);
for (i = 0; i < n_pkts; i += batch_size) {
conntrack_execute(&ct, pkt_batch, true, 0, NULL, NULL, NULL);
}
ovs_barrier_block(&barrier);
destroy_packets(pkt_batch);

return NULL;
}

static void
test_benchmark(struct ovs_cmdl_context *ctx)
{
struct thread_aux *threads;
long long start;
unsigned i;

fatal_signal_init();

/* Parse arguments */
n_threads = strtoul(ctx->argv[1], NULL, 0);
if (!n_threads) {
ovs_fatal(0, "n_threads must be at least one");
}
n_pkts = strtoul(ctx->argv[2], NULL, 0);
batch_size = strtoul(ctx->argv[3], NULL, 0);
if (batch_size == 0 || batch_size > NETDEV_MAX_BURST) {
ovs_fatal(0, "batch_size must be between 1 and NETDEV_MAX_BURST(%u)",
NETDEV_MAX_BURST);
}
if (ctx->argc > 4) {
change_conn = strtoul(ctx->argv[4], NULL, 0);
}

threads = xcalloc(n_threads, sizeof *threads);
ovs_barrier_init(&barrier, n_threads + 1);
conntrack_init(&ct);

/* Create threads */
for (i = 0; i < n_threads; i++) {
threads[i].tid = i;
threads[i].thread = ovs_thread_create("ct_thread", ct_thread_main,
&threads[i]);
}
/* Starts the work inside the threads */
ovs_barrier_block(&barrier);
start = time_msec();

/* Wait for the threads to finish the work */
ovs_barrier_block(&barrier);
printf("conntrack: %5lld ms\n", time_msec() - start);

for (i = 0; i < n_threads; i++) {
xpthread_join(threads[i].thread, NULL);
}

conntrack_destroy(&ct);
ovs_barrier_destroy(&barrier);
free(threads);
}

static const struct ovs_cmdl_command commands[] = {
/* Connection tracker tests. */
/* Starts 'n_threads' threads. Each thread will send 'n_pkts' packets to
* the connection tracker, 'batch_size' per call. If 'change_connection'
* is '1', each packet in a batch will have a different source and
* destination port */
{"benchmark", "n_threads n_pkts batch_size [change_connection]", 3, 4,
test_benchmark},

{NULL, NULL, 0, 0, NULL},
};

static void
test_conntrack_main(int argc, char *argv[])
{
struct ovs_cmdl_context ctx = {
.argc = argc - 1,
.argv = argv + 1,
};
set_program_name(argv[0]);
ovs_cmdl_run_command(&ctx, commands);
}

OVSTEST_REGISTER("test-conntrack", test_conntrack_main);

0 comments on commit 8cb1462

Please sign in to comment.