Skip to content

Commit d951b2c

Browse files
committed
examples/proxy: add wait batch count
Use -w to set the desired number of CQEs to wait for before processing them. This is typical batching behavior in processing. Can be used with -t for setting the wait timeout as well. This allows tightly controlling latencies, while still providing efficiency. Batching can make a substantial difference in effiency. For example, running proxy as a sink and using the default wait of 1, and sending it 32 byte packets over a 10G link yields the following performance based on wait_batch size: Wait batch QPS ============================ 1 5.7M 4 7.7M 32 8.7M Even just a small wait batch can drastically improve the performance at which a single threaded application (like proxy) can receive. Signed-off-by: Jens Axboe <[email protected]>
1 parent 12118cc commit d951b2c

File tree

1 file changed

+40
-7
lines changed

1 file changed

+40
-7
lines changed

examples/proxy.c

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ static int bidi;
7676
static int ipv6;
7777
static int napi;
7878
static int napi_timeout;
79+
static int wait_batch = 1;
80+
static int wait_usec = 1000000;
7981
static int verbose;
8082

8183
static int nr_bufs = 256;
@@ -1059,6 +1061,8 @@ static void usage(const char *name)
10591061
printf("\t-S:\t\tUse SQPOLL (%d)\n", sqpoll);
10601062
printf("\t-b:\t\tSend/receive buf size (%d)\n", buf_size);
10611063
printf("\t-n:\t\tNumber of provided buffers (pow2) (%d)\n", nr_bufs);
1064+
printf("\t-w:\t\tNumber of CQEs to wait for each loop (%d)\n", wait_batch);
1065+
printf("\t-t:\t\tTimeout for waiting on CQEs (usec) (%d)\n", wait_usec);
10621066
printf("\t-s:\t\tAct only as a sink (%d)\n", is_sink);
10631067
printf("\t-f:\t\tUse only fixed files (%d)\n", fixed_files);
10641068
printf("\t-B:\t\tUse bi-directional mode (%d)\n", bidi);
@@ -1093,6 +1097,7 @@ static void check_for_close(struct io_uring *ring)
10931097
*/
10941098
static int event_loop(struct io_uring *ring, int fd)
10951099
{
1100+
struct __kernel_timespec active_ts, idle_ts = { .tv_sec = 1, };
10961101
struct io_uring_sqe *sqe;
10971102

10981103
/*
@@ -1114,19 +1119,41 @@ static int event_loop(struct io_uring *ring, int fd)
11141119
io_uring_prep_multishot_accept(sqe, fd, NULL, NULL, 0);
11151120
__encode_userdata(sqe, 0, __ACCEPT, 0, 0, fd);
11161121

1122+
if (wait_usec > 1000000) {
1123+
active_ts.tv_sec = wait_usec / 1000000;
1124+
wait_usec -= active_ts.tv_sec * 1000000;
1125+
}
1126+
active_ts.tv_nsec = wait_usec * 1000;
1127+
11171128
while (1) {
1118-
struct __kernel_timespec ts = {
1119-
.tv_sec = 1,
1120-
};
1129+
struct __kernel_timespec *ts = &idle_ts;
11211130
struct io_uring_cqe *cqe;
11221131
unsigned int head;
11231132
int flags, i, to_wait;
11241133

1134+
/*
1135+
* If wait_batch is set higher than 1, then we'll wait on
1136+
* that amount of CQEs to be posted each loop. If used with
1137+
* DEFER_TASKRUN, this can provide a substantial reduction
1138+
* in context switch rate as the task isn't woken until the
1139+
* requested number of events can be returned.
1140+
*
1141+
* Can be used with -t to set a wait_usec timeout as well.
1142+
* For example, if an application can deal with 250 usec
1143+
* of wait latencies, it can set -w8 -t250 which will cause
1144+
* io_uring to return when either 8 events have been received,
1145+
* or if 250 usec of waiting has passed.
1146+
*
1147+
* If we don't have any open connections, wait on just 1
1148+
* always.
1149+
*/
11251150
to_wait = 1;
1126-
if (open_conns)
1127-
to_wait = open_conns;
1151+
if (open_conns) {
1152+
ts = &active_ts;
1153+
to_wait = open_conns * wait_batch;
1154+
}
11281155

1129-
io_uring_submit_and_wait_timeout(ring, &cqe, to_wait, &ts, NULL);
1156+
io_uring_submit_and_wait_timeout(ring, &cqe, to_wait, ts, NULL);
11301157

11311158
i = flags = 0;
11321159
io_uring_for_each_cqe(ring, head, cqe) {
@@ -1168,7 +1195,7 @@ int main(int argc, char *argv[])
11681195
return 1;
11691196
}
11701197

1171-
while ((opt = getopt(argc, argv, "m:d:S:s:b:f:H:r:p:n:B:N:T:6Vh?")) != -1) {
1198+
while ((opt = getopt(argc, argv, "m:d:S:s:b:f:H:r:p:n:B:N:T:w:t:6Vh?")) != -1) {
11721199
switch (opt) {
11731200
case 'm':
11741201
mshot = !!atoi(optarg);
@@ -1185,6 +1212,12 @@ int main(int argc, char *argv[])
11851212
case 'n':
11861213
nr_bufs = atoi(optarg);
11871214
break;
1215+
case 'w':
1216+
wait_batch = atoi(optarg);
1217+
break;
1218+
case 't':
1219+
wait_usec = atoi(optarg);
1220+
break;
11881221
case 's':
11891222
is_sink = !!atoi(optarg);
11901223
break;

0 commit comments

Comments
 (0)