// SPDX-License-Identifier: GPL-2.0 #ifdef HAVE_EVENTFD_SUPPORT /* * Copyright (C) 2018 Davidlohr Bueso. * * This program benchmarks concurrent epoll_wait(2) monitoring multiple * file descriptors under one or two load balancing models. The first, * and default, is the single/combined queueing (which refers to a single * epoll instance for N worker threads): * * |---> [worker A] * |---> [worker B] * [combined queue] .---> [worker C] * |---> [worker D] * |---> [worker E] * * While the second model, enabled via --multiq option, uses multiple * queueing (which refers to one epoll instance per worker). For example, * short lived tcp connections in a high throughput httpd server will * distribute the accept()'ing connections across CPUs. In this case each * worker does a limited amount of processing. * * [queue A] ---> [worker] * [queue B] ---> [worker] * [queue C] ---> [worker] * [queue D] ---> [worker] * [queue E] ---> [worker] * * Naturally, the single queue will enforce more concurrency on the epoll * instance, and can therefore scale poorly compared to multiple queues. * However, this is a benchmark raw data and must be taken with a grain of * salt when choosing how to make use of sys_epoll.
* Each thread has a number of private, nonblocking file descriptors, * referred to as fdmap. A writer thread will constantly be writing to * the fdmaps of all threads, minimizing each threads's chances of * epoll_wait not finding any ready read events and blocking as this * is not what we want to stress. The size of the fdmap can be adjusted * by the user; enlarging the value will increase the chances of * epoll_wait(2) blocking as the lineal writer thread will take "longer", * at least at a high level. * * Note that because fds are private to each thread, this workload does * not stress scenarios where multiple tasks are awoken per ready IO; ie: * EPOLLEXCLUSIVE semantics. * * The end result/metric is throughput: number of ops/second where an * operation consists of: * * epoll_wait(2) + [others] * * ... where [others] is the cost of re-adding the fd (EPOLLET), * or rearming it (EPOLLONESHOT). * * * The purpose of this is program is that it be useful for measuring * kernel related changes to the sys_epoll, and not comparing different * IO polling methods, for example. Hence everything is very adhoc and * outputs raw microbenchmark numbers. Also this uses eventfd, similar * tools tend to use pipes or sockets, but the result is the same.
*/
/* For the CLR_() macros */ #include <string.h> #include <pthread.h> #include <unistd.h>
/* * Arrange the N elements of ARRAY in random order. * Only effective if N is much smaller than RAND_MAX; * if this may not be the case, use a better random * number generator. -- Ben Pfaff.
*/ staticvoid shuffle(void *array, size_t n, size_t size)
{ char *carray = array; void *aux;
size_t i;
if (n <= 1) return;
aux = calloc(1, size); if (!aux)
err(EXIT_FAILURE, "calloc");
for (i = 1; i < n; ++i) {
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
j *= size;
staticvoid *workerfn(void *arg)
{ int fd, ret, r; struct worker *w = (struct worker *) arg; unsignedlong ops = w->ops; struct epoll_event ev;
uint64_t val; int to = nonblocking? 0 : -1; int efd = multiq ? w->epollfd : epollfd;
mutex_lock(&thread_lock);
threads_starting--; if (!threads_starting)
cond_signal(&thread_parent);
cond_wait(&thread_worker, &thread_lock);
mutex_unlock(&thread_lock);
do { /* * Block indefinitely waiting for the IN event. * In order to stress the epoll_wait(2) syscall, * call it event per event, instead of a larger * batch (max)limit.
*/ do {
ret = epoll_wait(efd, &ev, 1, to);
} while (ret < 0 && errno == EINTR); if (ret < 0)
err(EXIT_FAILURE, "epoll_wait");
fd = ev.data.fd;
do {
r = read(fd, &val, sizeof(val));
} while (!done && (r < 0 && errno == EAGAIN));
if (et) {
ev.events = EPOLLIN | EPOLLET;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
}
if (oneshot) { /* rearm the file descriptor with a new event mask */
ev.events |= EPOLLIN | EPOLLONESHOT;
ret = epoll_ctl(efd, EPOLL_CTL_MOD, fd, &ev);
}
mutex_lock(&thread_lock); while (threads_starting)
cond_wait(&thread_parent, &thread_lock);
cond_broadcast(&thread_worker);
mutex_unlock(&thread_lock);
/* * At this point the workers should be blocked waiting for read events * to become ready. Launch the writer which will constantly be writing * to each thread's fdmap.
*/
ret = pthread_create(&wthread, NULL, writerfn,
(void *)(struct worker *) worker); if (ret)
err(EXIT_FAILURE, "pthread_create");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.