mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:10 +00:00
Improve adaptive polling by updating each AioHandler's poll.ns
every loop iteration using weighted averages. This reduces CPU
consumption while minimizing performance impact.
Background:
Starting from QEMU 10.0, poll.ns was introduced per event handler
to mitigate excessive fluctuations in IOThread polling times
observed in earlier versions (QEMU 9.x). However, the current
design has limitations:
1. poll.ns is updated only when an event occurs, making it
difficult to treat block_ns as a reliable event interval.
2. The IOThread's next polling time is determined by the maximum
poll.ns among all AioHandlers, meaning idle AioHandlers with
high poll.ns can have an outsized impact on polling duration.
3. For io_uring, idle AioHandlers are cleared after
POLL_IDLE_INTERVAL_NS (7s), but for ppoll/epoll there is no
such mechanism, leading to increased CPU consumption from idle
nodes.
Implementation:
This patch treats block_ns as an event interval and updates each
AioHandler's poll.ns in every loop iteration:
- Active handlers (with events): poll.ns is updated using a
weighted average of the current block_ns and previous poll.ns,
smoothing out adjustments and preventing excessive fluctuations.
- Inactive handlers (no events): poll.ns accumulates block_ns
without weighting, allowing rapid isolation of idle nodes. When
poll.ns exceeds poll_max_ns, it resets to 0, preventing
sporadically active handlers from unnecessarily prolonging
iothread polling.
- The iothread polling duration is set based on the largest poll.ns
among active handlers. The shrink divider defaults to 2, matching
the grow rate, to reduce frequent poll_ns resets for slow devices.
The implementation renames poll_idle_timeout to last_dispatch_timestamp
for use as an active handler identifier.
Testing:
POLL_WEIGHT_SHIFT=3 (12.5% weight) was selected based on testing
comparing baseline vs weight=2/3 across various workloads:
Performance results (RHEL 10.1 + QEMU 10.0.0, FCP/FICON, 1-8 iothreads,
numjobs 1/4/8 averaged):
| poll-weight=2 | poll-weight=3
--------------------|--------------------|-----------------
Throughput avg | -2.4% (all tests) | -2.2% (all tests)
CPU consumption avg | -10.9% (all tests) | -9.4% (all tests)
Both configurations achieve ~10% CPU reduction with minimal throughput
impact (~2%). Weight=3 is chosen as default for slightly better
throughput while maintaining substantial CPU savings.
Additional validation testing on s390x SSD with fio (bs=8k, iodepth=8,
numjobs=1) shows how poll_weight affects polling time (poll.ns)
behavior:
RandRead workload:
+-------------+-----------+-----------+-------------+-------------+
| poll_weight | #samples | Mean (ns) | 50th % (ns) | 90th % (ns) |
+-------------+-----------+-----------+-------------+-------------+
| 1 | 4.79M | 8,034 | 5,116 | 20,509 |
| 2 | 5.01M | 12,584 | 11,078 | 24,693 |
| 3 | 5.01M | 15,647 | 14,863 | 28,695 |
| 4 | 5.12M | 16,430 | 15,556 | 30,848 |
| 5 | 5.14M | 16,461 | 15,306 | 32,123 |
+-------------+-----------+-----------+-------------+-------------+
RandWrite workload:
+-------------+-----------+-----------+-------------+-------------+
| poll_weight | #samples | Mean (ns) | 50th % (ns) | 90th % (ns) |
+-------------+-----------+-----------+-------------+-------------+
| 1 | 6.37M | 2,049 | 1,262 | 4,301 |
| 2 | 7.46M | 4,118 | 3,226 | 7,476 |
| 3 | 7.97M | 7,034 | 5,984 | 11,645 |
| 4 | 7.96M | 12,789 | 11,362 | 20,040 |
| 5 | 7.82M | 22,992 | 20,644 | 32,768 |
+-------------+-----------+-----------+-------------+-------------+
Signed-off-by: Jaehoon Kim <jhkim@linux.ibm.com>
Message-ID: <20260423195918.661299-3-jhkim@linux.ibm.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
82 lines
2.2 KiB
C
82 lines
2.2 KiB
C
/*
|
|
* AioContext POSIX event loop implementation internal APIs
|
|
*
|
|
* Copyright IBM, Corp. 2008
|
|
* Copyright Red Hat, Inc. 2020
|
|
*
|
|
* Authors:
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2. See
|
|
* the COPYING file in the top-level directory.
|
|
*
|
|
* Contributions after 2012-01-13 are licensed under the terms of the
|
|
* GNU GPL, version 2 or (at your option) any later version.
|
|
*/
|
|
|
|
#ifndef AIO_POSIX_H
|
|
#define AIO_POSIX_H
|
|
|
|
#include "qemu/aio.h"
|
|
#include "qapi/error.h"
|
|
|
|
struct AioHandler {
|
|
GPollFD pfd;
|
|
IOHandler *io_read;
|
|
IOHandler *io_write;
|
|
AioPollFn *io_poll;
|
|
IOHandler *io_poll_ready;
|
|
IOHandler *io_poll_begin;
|
|
IOHandler *io_poll_end;
|
|
void *opaque;
|
|
QLIST_ENTRY(AioHandler) node;
|
|
QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */
|
|
QLIST_ENTRY(AioHandler) node_deleted;
|
|
QLIST_ENTRY(AioHandler) node_poll;
|
|
#ifdef CONFIG_LINUX_IO_URING
|
|
QSLIST_ENTRY(AioHandler) node_submitted;
|
|
unsigned flags; /* see fdmon-io_uring.c */
|
|
CqeHandler internal_cqe_handler; /* used for POLL_ADD/POLL_REMOVE */
|
|
#endif
|
|
int64_t last_dispatch_timestamp; /* when last handler was dispatched */
|
|
bool poll_ready; /* has polling detected an event? */
|
|
AioPolledEvent poll;
|
|
};
|
|
|
|
/* Add a handler to a ready list */
|
|
void aio_add_ready_handler(AioHandlerList *ready_list, AioHandler *node,
|
|
int revents);
|
|
|
|
extern const FDMonOps fdmon_poll_ops;
|
|
|
|
/* Switch back to poll(2). list_lock must be held. */
|
|
void fdmon_poll_downgrade(AioContext *ctx);
|
|
|
|
#ifdef CONFIG_EPOLL
|
|
bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd);
|
|
void fdmon_epoll_setup(AioContext *ctx);
|
|
|
|
/* list_lock must be held */
|
|
void fdmon_epoll_disable(AioContext *ctx);
|
|
#else
|
|
static inline bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline void fdmon_epoll_setup(AioContext *ctx)
|
|
{
|
|
}
|
|
|
|
static inline void fdmon_epoll_disable(AioContext *ctx)
|
|
{
|
|
}
|
|
#endif /* !CONFIG_EPOLL */
|
|
|
|
#ifdef CONFIG_LINUX_IO_URING
|
|
bool fdmon_io_uring_setup(AioContext *ctx, Error **errp);
|
|
void fdmon_io_uring_destroy(AioContext *ctx);
|
|
#endif /* !CONFIG_LINUX_IO_URING */
|
|
|
|
#endif /* AIO_POSIX_H */
|