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