nginxQuic: скорость загрузки при активации kTLS
Roman Arutyunyan
arut на nginx.com
Пн Янв 8 12:18:45 UTC 2024
Добрый день,
On Thu, Jan 04, 2024 at 07:04:31PM +0300, izorkin на gmail.com wrote:
> Добрый вечер, Илья.
>
> Замерил тесты на физическом сервере, пока без без поддержки kTLS.
> Оказывается в тестах на виртуальной машине я неправильно интерпретировал интерпретировал скорости,
> которые выводила утилита curl. Вместо МБит/сек там идёт МБайт/сек.
>
> Результаты тестов при скачивании файла с самого сервера:
> - HTTP/1.1 - ~3 504,14 МБит/сек (CPU load 100%)
> - HTTP/2 - ~3 568,57 МБит/сек (CPU load 100%)
> - HTTP/3 - ~2 872,09 МБит/сек (CPU load 58-62%)
>
> Результаты тестов при скачивании файла по локальной сети:
> - HTTP/1.1 - ~2 325,03 МБит/сек (CPU load 45-50%)
> - HTTP/2 - ~2 333,56 МБит/сек (CPU load 45-50%)
> - HTTP/3 - ~1 350,26 МБит/сек (CPU load 50-55%)
>
> Анализ профиля для протокола HTTP/3 при скачивании файла с самого сервера:
> 482 27.1% 27.1% 482 27.1% __sendmsg
У вас quic_gso включен? Если нет, попробуйте включить:
quic_gso on;
Также попробуйте приаттаченный патч, добавляющий поддержку sendmmsg()
(quic_gso при этом оставьте включенным). nginx будет надо переконфигурить
перед сборкой.
Интересно посмотреть, как изменятся цифры.
> 473 26.6% 53.7% 473 26.6% __libc_pread64
> 367 20.6% 74.4% 367 20.6% _aesni_ctr32_ghash_6x
> 151 8.5% 82.8% 151 8.5% __memmove_avx_unaligned_erms
> 58 3.3% 86.1% 58 3.3% epoll_wait
> 31 1.7% 87.9% 31 1.7% __recvmsg
> 10 0.6% 88.4% 93 5.2% ngx_quic_write_buffer
> 8 0.4% 88.9% 100 5.6% ngx_quic_recvmsg
> 7 0.4% 89.3% 7 0.4% __strcmp_avx2
> 7 0.4% 89.7% 7 0.4% ngx_quic_read_buffer
> 6 0.3% 90.0% 115 6.5% ngx_http_charset_body_filter
> 6 0.3% 90.3% 108 6.1% ngx_http_write_filter
> 6 0.3% 90.7% 82 4.6% ngx_quic_create_frame
> 6 0.3% 91.0% 8 0.4% ossl_gcm_set_ctx_params
> 5 0.3% 91.3% 19 1.1% EVP_CIPHER_CTX_ctrl
> 5 0.3% 91.6% 5 0.3% aesni_ctr32_encrypt_blocks
> 5 0.3% 91.8% 5 0.3% ngx_quic_alloc_buf
> 5 0.3% 92.1% 15 0.8% ngx_quic_handle_ack_frame_range
> 5 0.3% 92.4% 59 3.3% ngx_quic_handle_datagram
> 4 0.2% 92.6% 10 0.6% CRYPTO_gcm128_encrypt_ctr32
[..]
--
Roman Arutyunyan
----------- следующая часть -----------
# HG changeset patch
# User Roman Arutyunyan <arut на nginx.com>
# Date 1692075843 -14400
# Tue Aug 15 09:04:03 2023 +0400
# Node ID 0f7b91d0fea6d132f877ff25992a457a2fe437e6
# Parent 6c8595b77e667bd58fd28186939ed820f2e55e0e
QUIC: use sendmmsg() with UDP segmentation instead of sendmsg().
The syscall allows to send more datagrams with a single call.
Using sendmmsg() will not introduce compatibility issues since this syscall was
added in Linux kernel 3.0, while UDP segmentation was added in 4.18.
diff --git a/auto/os/linux b/auto/os/linux
--- a/auto/os/linux
+++ b/auto/os/linux
@@ -291,4 +291,16 @@ ngx_feature_test="socklen_t optlen = siz
. auto/feature
+ngx_feature="sendmmsg()"
+ngx_feature_name="NGX_HAVE_SENDMMSG"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+ #include <sys/uio.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct mmsghdr msg[UIO_MAXIOV];
+ sendmmsg(0, msg, UIO_MAXIOV, 0);"
+. auto/feature
+
+
CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c
--- a/src/event/quic/ngx_event_quic_output.c
+++ b/src/event/quic/ngx_event_quic_output.c
@@ -48,11 +48,9 @@ static ngx_int_t ngx_quic_create_datagra
static void ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
static void ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
uint64_t pnum);
-#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))
+#if (NGX_HAVE_UDP_SEGMENT && NGX_HAVE_MSGHDR_MSG_CONTROL && NGX_HAVE_SENDMMSG)
static ngx_uint_t ngx_quic_allow_segmentation(ngx_connection_t *c);
static ngx_int_t ngx_quic_create_segments(ngx_connection_t *c);
-static ssize_t ngx_quic_send_segments(ngx_connection_t *c, u_char *buf,
- size_t len, struct sockaddr *sockaddr, socklen_t socklen, size_t segment);
#endif
static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
@@ -80,7 +78,7 @@ ngx_quic_output(ngx_connection_t *c)
in_flight = cg->in_flight;
-#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))
+#if (NGX_HAVE_UDP_SEGMENT && NGX_HAVE_MSGHDR_MSG_CONTROL && NGX_HAVE_SENDMMSG)
if (ngx_quic_allow_segmentation(c)) {
rc = ngx_quic_create_segments(c);
} else
@@ -250,7 +248,7 @@ ngx_quic_revert_send(ngx_connection_t *c
}
-#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))
+#if (NGX_HAVE_UDP_SEGMENT && NGX_HAVE_MSGHDR_MSG_CONTROL && NGX_HAVE_SENDMMSG)
static ngx_uint_t
ngx_quic_allow_segmentation(ngx_connection_t *c)
@@ -308,16 +306,28 @@ ngx_quic_allow_segmentation(ngx_connecti
static ngx_int_t
ngx_quic_create_segments(ngx_connection_t *c)
{
- size_t len, segsize;
+ size_t len, segsize, clen;
ssize_t n;
- u_char *p, *end;
+ u_char *p, *start, *end;
+ uint16_t *valp;
uint64_t preserved_pnum;
- ngx_uint_t nseg;
+ ngx_err_t err;
+ ngx_uint_t nseg, nmsg;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
ngx_quic_path_t *path;
ngx_quic_send_ctx_t *ctx;
ngx_quic_congestion_t *cg;
ngx_quic_connection_t *qc;
- static u_char dst[NGX_QUIC_MAX_UDP_SEGMENT_BUF];
+#if (NGX_HAVE_ADDRINFO_CMSG)
+ char msg_control[CMSG_SPACE(sizeof(uint16_t))
+ + CMSG_SPACE(sizeof(ngx_addrinfo_t))];
+#else
+ char msg_control[CMSG_SPACE(sizeof(uint16_t))];
+#endif
+ struct iovec iovs[UIO_MAXIOV];
+ static u_char dst[UIO_MAXIOV * NGX_QUIC_MAX_UDP_SEGMENT_BUF];
+ struct mmsghdr msgs[UIO_MAXIOV];
qc = ngx_quic_get_connection(c);
cg = &qc->congestion;
@@ -330,94 +340,12 @@ ngx_quic_create_segments(ngx_connection_
}
segsize = ngx_min(path->mtu, NGX_QUIC_MAX_UDP_SEGMENT_BUF);
- p = dst;
- end = dst + sizeof(dst);
-
- nseg = 0;
-
- preserved_pnum = ctx->pnum;
-
- for ( ;; ) {
-
- len = ngx_min(segsize, (size_t) (end - p));
-
- if (len && cg->in_flight + (p - dst) < cg->window) {
-
- n = ngx_quic_output_packet(c, ctx, p, len, len);
- if (n == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- if (n) {
- p += n;
- nseg++;
- }
-
- } else {
- n = 0;
- }
-
- if (p == dst) {
- break;
- }
-
- if (n == 0 || nseg == NGX_QUIC_MAX_SEGMENTS) {
- n = ngx_quic_send_segments(c, dst, p - dst, path->sockaddr,
- path->socklen, segsize);
- if (n == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- if (n == NGX_AGAIN) {
- ngx_quic_revert_send(c, ctx, preserved_pnum);
-
- ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
- break;
- }
-
- ngx_quic_commit_send(c, ctx);
-
- path->sent += n;
-
- p = dst;
- nseg = 0;
- preserved_pnum = ctx->pnum;
- }
- }
-
- return NGX_OK;
-}
-
-
-static ssize_t
-ngx_quic_send_segments(ngx_connection_t *c, u_char *buf, size_t len,
- struct sockaddr *sockaddr, socklen_t socklen, size_t segment)
-{
- size_t clen;
- ssize_t n;
- uint16_t *valp;
- struct iovec iov;
- struct msghdr msg;
- struct cmsghdr *cmsg;
-
-#if (NGX_HAVE_ADDRINFO_CMSG)
- char msg_control[CMSG_SPACE(sizeof(uint16_t))
- + CMSG_SPACE(sizeof(ngx_addrinfo_t))];
-#else
- char msg_control[CMSG_SPACE(sizeof(uint16_t))];
-#endif
ngx_memzero(&msg, sizeof(struct msghdr));
ngx_memzero(msg_control, sizeof(msg_control));
- iov.iov_len = len;
- iov.iov_base = buf;
-
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- msg.msg_name = sockaddr;
- msg.msg_namelen = socklen;
+ msg.msg_name = path->sockaddr;
+ msg.msg_namelen = path->socklen;
msg.msg_control = msg_control;
msg.msg_controllen = sizeof(msg_control);
@@ -431,7 +359,7 @@ ngx_quic_send_segments(ngx_connection_t
clen = CMSG_SPACE(sizeof(uint16_t));
valp = (void *) CMSG_DATA(cmsg);
- *valp = segment;
+ *valp = segsize;
#if (NGX_HAVE_ADDRINFO_CMSG)
if (c->listening && c->listening->wildcard && c->local_sockaddr) {
@@ -442,14 +370,100 @@ ngx_quic_send_segments(ngx_connection_t
msg.msg_controllen = clen;
- n = ngx_sendmsg(c, &msg, 0);
- if (n < 0) {
- return n;
+ for ( ;; ) {
+
+ preserved_pnum = ctx->pnum;
+
+ p = dst;
+
+ for (nmsg = 0; nmsg < UIO_MAXIOV; nmsg++) {
+
+ start = p;
+ end = p + NGX_QUIC_MAX_UDP_SEGMENT_BUF;
+
+ for (nseg = 0; nseg < NGX_QUIC_MAX_SEGMENTS; nseg++) {
+
+ if (cg->in_flight + (p - dst) >= cg->window) {
+ break;
+ }
+
+ len = ngx_min(segsize, (size_t) (end - p));
+ if (len == 0) {
+ break;
+ }
+
+ n = ngx_quic_output_packet(c, ctx, p, len, len);
+
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (n == 0) {
+ break;
+ }
+
+ p += n;
+ }
+
+ if (nseg == 0) {
+ break;
+ }
+
+ iovs[nmsg].iov_base = start;
+ iovs[nmsg].iov_len = p - start;
+
+ msgs[nmsg].msg_hdr = msg;
+ msgs[nmsg].msg_hdr.msg_iov = &iovs[nmsg];
+ msgs[nmsg].msg_hdr.msg_iovlen = 1;
+ msgs[nmsg].msg_len = 0;
+ }
+
+ if (nmsg == 0) {
+ break;
+ }
+
+ eintr:
+
+ n = sendmmsg(c->fd, msgs, nmsg, 0);
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendmmsg() not ready");
+
+ ngx_quic_revert_send(c, ctx, preserved_pnum);
+
+ ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
+ return NGX_OK;
+
+ case NGX_EINTR:
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendmmsg() was interrupted");
+ goto eintr;
+
+ default:
+ c->write->error = 1;
+ ngx_connection_error(c, err, "sendmsg() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ len = p - dst;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendmmsg: %z of %ui msg of size %uz", n, nmsg, len);
+
+ c->sent += len;
+
+ ngx_quic_commit_send(c, ctx);
+
+ path->sent += len;
}
- c->sent += n;
-
- return n;
+ return NGX_OK;
}
#endif
Подробная информация о списке рассылки nginx-ru