linux/tools/virtio/ringtest/main.c
Paolo Bonzini 948a8ac296 ringtest: poll for new buffers once before updating event index
Updating the event index has a memory barrier and causes more work
on the other side to actually signal the event.  It is unnecessary
if a new buffer has already appeared on the ring, so poll once before
doing the update.

The effect of this on the 0.9 ring implementation is pretty much
invisible, but on the new-style ring it provides a consistent 3%
performance improvement.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2016-10-31 00:21:43 +02:00

379 lines
6.2 KiB
C

/*
* Copyright (C) 2016 Red Hat, Inc.
* Author: Michael S. Tsirkin <mst@redhat.com>
* This work is licensed under the terms of the GNU GPL, version 2.
*
* Command line processing and common functions for ring benchmarking.
*/
#define _GNU_SOURCE
#include <getopt.h>
#include <pthread.h>
#include <assert.h>
#include <sched.h>
#include "main.h"
#include <sys/eventfd.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
int runcycles = 10000000;
int max_outstanding = INT_MAX;
int batch = 1;
bool do_sleep = false;
bool do_relax = false;
bool do_exit = true;
unsigned ring_size = 256;
static int kickfd = -1;
static int callfd = -1;
void notify(int fd)
{
unsigned long long v = 1;
int r;
vmexit();
r = write(fd, &v, sizeof v);
assert(r == sizeof v);
vmentry();
}
void wait_for_notify(int fd)
{
unsigned long long v = 1;
int r;
vmexit();
r = read(fd, &v, sizeof v);
assert(r == sizeof v);
vmentry();
}
void kick(void)
{
notify(kickfd);
}
void wait_for_kick(void)
{
wait_for_notify(kickfd);
}
void call(void)
{
notify(callfd);
}
void wait_for_call(void)
{
wait_for_notify(callfd);
}
void set_affinity(const char *arg)
{
cpu_set_t cpuset;
int ret;
pthread_t self;
long int cpu;
char *endptr;
if (!arg)
return;
cpu = strtol(arg, &endptr, 0);
assert(!*endptr);
assert(cpu >= 0 || cpu < CPU_SETSIZE);
self = pthread_self();
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
ret = pthread_setaffinity_np(self, sizeof(cpu_set_t), &cpuset);
assert(!ret);
}
void poll_used(void)
{
while (used_empty())
busy_wait();
}
static void __attribute__((__flatten__)) run_guest(void)
{
int completed_before;
int completed = 0;
int started = 0;
int bufs = runcycles;
int spurious = 0;
int r;
unsigned len;
void *buf;
int tokick = batch;
for (;;) {
if (do_sleep)
disable_call();
completed_before = completed;
do {
if (started < bufs &&
started - completed < max_outstanding) {
r = add_inbuf(0, "Buffer\n", "Hello, world!");
if (__builtin_expect(r == 0, true)) {
++started;
if (!--tokick) {
tokick = batch;
if (do_sleep)
kick_available();
}
}
} else
r = -1;
/* Flush out completed bufs if any */
if (get_buf(&len, &buf)) {
++completed;
if (__builtin_expect(completed == bufs, false))
return;
r = 0;
}
} while (r == 0);
if (completed == completed_before)
++spurious;
assert(completed <= bufs);
assert(started <= bufs);
if (do_sleep) {
if (used_empty() && enable_call())
wait_for_call();
} else {
poll_used();
}
}
}
void poll_avail(void)
{
while (avail_empty())
busy_wait();
}
static void __attribute__((__flatten__)) run_host(void)
{
int completed_before;
int completed = 0;
int spurious = 0;
int bufs = runcycles;
unsigned len;
void *buf;
for (;;) {
if (do_sleep) {
if (avail_empty() && enable_kick())
wait_for_kick();
} else {
poll_avail();
}
if (do_sleep)
disable_kick();
completed_before = completed;
while (__builtin_expect(use_buf(&len, &buf), true)) {
if (do_sleep)
call_used();
++completed;
if (__builtin_expect(completed == bufs, false))
return;
}
if (completed == completed_before)
++spurious;
assert(completed <= bufs);
if (completed == bufs)
break;
}
}
void *start_guest(void *arg)
{
set_affinity(arg);
run_guest();
pthread_exit(NULL);
}
void *start_host(void *arg)
{
set_affinity(arg);
run_host();
pthread_exit(NULL);
}
static const char optstring[] = "";
static const struct option longopts[] = {
{
.name = "help",
.has_arg = no_argument,
.val = 'h',
},
{
.name = "host-affinity",
.has_arg = required_argument,
.val = 'H',
},
{
.name = "guest-affinity",
.has_arg = required_argument,
.val = 'G',
},
{
.name = "ring-size",
.has_arg = required_argument,
.val = 'R',
},
{
.name = "run-cycles",
.has_arg = required_argument,
.val = 'C',
},
{
.name = "outstanding",
.has_arg = required_argument,
.val = 'o',
},
{
.name = "batch",
.has_arg = required_argument,
.val = 'b',
},
{
.name = "sleep",
.has_arg = no_argument,
.val = 's',
},
{
.name = "relax",
.has_arg = no_argument,
.val = 'x',
},
{
.name = "exit",
.has_arg = no_argument,
.val = 'e',
},
{
}
};
static void help(void)
{
fprintf(stderr, "Usage: <test> [--help]"
" [--host-affinity H]"
" [--guest-affinity G]"
" [--ring-size R (default: %d)]"
" [--run-cycles C (default: %d)]"
" [--batch b]"
" [--outstanding o]"
" [--sleep]"
" [--relax]"
" [--exit]"
"\n",
ring_size,
runcycles);
}
int main(int argc, char **argv)
{
int ret;
pthread_t host, guest;
void *tret;
char *host_arg = NULL;
char *guest_arg = NULL;
char *endptr;
long int c;
kickfd = eventfd(0, 0);
assert(kickfd >= 0);
callfd = eventfd(0, 0);
assert(callfd >= 0);
for (;;) {
int o = getopt_long(argc, argv, optstring, longopts, NULL);
switch (o) {
case -1:
goto done;
case '?':
help();
exit(2);
case 'H':
host_arg = optarg;
break;
case 'G':
guest_arg = optarg;
break;
case 'R':
ring_size = strtol(optarg, &endptr, 0);
assert(ring_size && !(ring_size & (ring_size - 1)));
assert(!*endptr);
break;
case 'C':
c = strtol(optarg, &endptr, 0);
assert(!*endptr);
assert(c > 0 && c < INT_MAX);
runcycles = c;
break;
case 'o':
c = strtol(optarg, &endptr, 0);
assert(!*endptr);
assert(c > 0 && c < INT_MAX);
max_outstanding = c;
break;
case 'b':
c = strtol(optarg, &endptr, 0);
assert(!*endptr);
assert(c > 0 && c < INT_MAX);
batch = c;
break;
case 's':
do_sleep = true;
break;
case 'x':
do_relax = true;
break;
case 'e':
do_exit = true;
break;
default:
help();
exit(4);
break;
}
}
/* does nothing here, used to make sure all smp APIs compile */
smp_acquire();
smp_release();
smp_mb();
done:
if (batch > max_outstanding)
batch = max_outstanding;
if (optind < argc) {
help();
exit(4);
}
alloc_ring();
ret = pthread_create(&host, NULL, start_host, host_arg);
assert(!ret);
ret = pthread_create(&guest, NULL, start_guest, guest_arg);
assert(!ret);
ret = pthread_join(guest, &tret);
assert(!ret);
ret = pthread_join(host, &tret);
assert(!ret);
return 0;
}