github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/perf/linux/send_recv_benchmark.cc (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <netinet/in.h>
    16  #include <netinet/tcp.h>
    17  #include <poll.h>
    18  #include <sys/ioctl.h>
    19  #include <sys/socket.h>
    20  
    21  #include <cstring>
    22  
    23  #include "gtest/gtest.h"
    24  #include "absl/synchronization/notification.h"
    25  #include "benchmark/benchmark.h"
    26  #include "test/syscalls/linux/socket_test_util.h"
    27  #include "test/util/file_descriptor.h"
    28  #include "test/util/logging.h"
    29  #include "test/util/posix_error.h"
    30  #include "test/util/test_util.h"
    31  #include "test/util/thread_util.h"
    32  
    33  namespace gvisor {
    34  namespace testing {
    35  
    36  namespace {
    37  
    38  constexpr ssize_t kMessageSize = 1024;
    39  
    40  class Message {
    41   public:
    42    explicit Message(int byte = 0) : Message(byte, kMessageSize, 0) {}
    43  
    44    explicit Message(int byte, int sz) : Message(byte, sz, 0) {}
    45  
    46    explicit Message(int byte, int sz, int cmsg_sz)
    47        : buffer_(sz, byte), cmsg_buffer_(cmsg_sz, 0) {
    48      iov_.iov_base = buffer_.data();
    49      iov_.iov_len = sz;
    50      hdr_.msg_iov = &iov_;
    51      hdr_.msg_iovlen = 1;
    52      hdr_.msg_control = cmsg_buffer_.data();
    53      hdr_.msg_controllen = cmsg_sz;
    54    }
    55  
    56    struct msghdr* header() {
    57      return &hdr_;
    58    }
    59  
    60   private:
    61    std::vector<char> buffer_;
    62    std::vector<char> cmsg_buffer_;
    63    struct iovec iov_ = {};
    64    struct msghdr hdr_ = {};
    65  };
    66  
    67  void BM_Recvmsg(benchmark::State& state) {
    68    int sockets[2];
    69    TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
    70    FileDescriptor send_socket(sockets[0]), recv_socket(sockets[1]);
    71    absl::Notification notification;
    72    Message send_msg('a'), recv_msg;
    73  
    74    ScopedThread t([&send_msg, &send_socket, &notification] {
    75      while (!notification.HasBeenNotified()) {
    76        sendmsg(send_socket.get(), send_msg.header(), 0);
    77      }
    78    });
    79  
    80    int64_t bytes_received = 0;
    81    for (auto ignored : state) {
    82      int n = recvmsg(recv_socket.get(), recv_msg.header(), 0);
    83      TEST_CHECK(n > 0);
    84      bytes_received += n;
    85    }
    86  
    87    notification.Notify();
    88    recv_socket.reset();
    89  
    90    state.SetBytesProcessed(bytes_received);
    91  }
    92  
    93  BENCHMARK(BM_Recvmsg)->UseRealTime();
    94  
    95  void BM_Sendmsg(benchmark::State& state) {
    96    int sockets[2];
    97    TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
    98    FileDescriptor send_socket(sockets[0]), recv_socket(sockets[1]);
    99    absl::Notification notification;
   100    Message send_msg('a'), recv_msg;
   101  
   102    ScopedThread t([&recv_msg, &recv_socket, &notification] {
   103      while (!notification.HasBeenNotified()) {
   104        recvmsg(recv_socket.get(), recv_msg.header(), 0);
   105      }
   106    });
   107  
   108    int64_t bytes_sent = 0;
   109    for (auto ignored : state) {
   110      int n = sendmsg(send_socket.get(), send_msg.header(), 0);
   111      TEST_CHECK(n > 0);
   112      bytes_sent += n;
   113    }
   114  
   115    notification.Notify();
   116    send_socket.reset();
   117  
   118    state.SetBytesProcessed(bytes_sent);
   119  }
   120  
   121  BENCHMARK(BM_Sendmsg)->UseRealTime();
   122  
   123  void BM_Recvfrom(benchmark::State& state) {
   124    int sockets[2];
   125    TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
   126    FileDescriptor send_socket(sockets[0]), recv_socket(sockets[1]);
   127    absl::Notification notification;
   128    char send_buffer[kMessageSize], recv_buffer[kMessageSize];
   129  
   130    ScopedThread t([&send_socket, &send_buffer, &notification] {
   131      while (!notification.HasBeenNotified()) {
   132        sendto(send_socket.get(), send_buffer, kMessageSize, 0, nullptr, 0);
   133      }
   134    });
   135  
   136    int bytes_received = 0;
   137    for (auto ignored : state) {
   138      int n = recvfrom(recv_socket.get(), recv_buffer, kMessageSize, 0, nullptr,
   139                       nullptr);
   140      TEST_CHECK(n > 0);
   141      bytes_received += n;
   142    }
   143  
   144    notification.Notify();
   145    recv_socket.reset();
   146  
   147    state.SetBytesProcessed(bytes_received);
   148  }
   149  
   150  BENCHMARK(BM_Recvfrom)->UseRealTime();
   151  
   152  void BM_Sendto(benchmark::State& state) {
   153    int sockets[2];
   154    TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
   155    FileDescriptor send_socket(sockets[0]), recv_socket(sockets[1]);
   156    absl::Notification notification;
   157    char send_buffer[kMessageSize], recv_buffer[kMessageSize];
   158  
   159    ScopedThread t([&recv_socket, &recv_buffer, &notification] {
   160      while (!notification.HasBeenNotified()) {
   161        recvfrom(recv_socket.get(), recv_buffer, kMessageSize, 0, nullptr,
   162                 nullptr);
   163      }
   164    });
   165  
   166    int64_t bytes_sent = 0;
   167    for (auto ignored : state) {
   168      int n = sendto(send_socket.get(), send_buffer, kMessageSize, 0, nullptr, 0);
   169      TEST_CHECK(n > 0);
   170      bytes_sent += n;
   171    }
   172  
   173    notification.Notify();
   174    send_socket.reset();
   175  
   176    state.SetBytesProcessed(bytes_sent);
   177  }
   178  
   179  BENCHMARK(BM_Sendto)->UseRealTime();
   180  
   181  PosixErrorOr<sockaddr_storage> InetLoopbackAddr(int family) {
   182    struct sockaddr_storage addr;
   183    memset(&addr, 0, sizeof(addr));
   184    addr.ss_family = family;
   185    switch (family) {
   186      case AF_INET:
   187        reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr.s_addr =
   188            htonl(INADDR_LOOPBACK);
   189        break;
   190      case AF_INET6:
   191        reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr =
   192            in6addr_loopback;
   193        break;
   194      default:
   195        return PosixError(EINVAL,
   196                          absl::StrCat("unknown socket family: ", family));
   197    }
   198    return addr;
   199  }
   200  
   201  // BM_RecvmsgWithControlBuf measures the performance of recvmsg when we allocate
   202  // space for control messages. Note that we do not expect to receive any.
   203  void BM_RecvmsgWithControlBuf(benchmark::State& state) {
   204    auto listen_socket =
   205        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP));
   206  
   207    // Initialize address to the loopback one.
   208    sockaddr_storage addr = ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(AF_INET6));
   209    socklen_t addrlen = sizeof(addr);
   210  
   211    // Bind to some port then start listening.
   212    ASSERT_THAT(bind(listen_socket.get(),
   213                     reinterpret_cast<struct sockaddr*>(&addr), addrlen),
   214                SyscallSucceeds());
   215  
   216    ASSERT_THAT(listen(listen_socket.get(), SOMAXCONN), SyscallSucceeds());
   217  
   218    // Get the address we're listening on, then connect to it. We need to do this
   219    // because we're allowing the stack to pick a port for us.
   220    ASSERT_THAT(getsockname(listen_socket.get(),
   221                            reinterpret_cast<struct sockaddr*>(&addr), &addrlen),
   222                SyscallSucceeds());
   223  
   224    auto send_socket =
   225        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP));
   226  
   227    ASSERT_THAT(
   228        RetryEINTR(connect)(send_socket.get(),
   229                            reinterpret_cast<struct sockaddr*>(&addr), addrlen),
   230        SyscallSucceeds());
   231  
   232    // Accept the connection.
   233    auto recv_socket =
   234        ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_socket.get(), nullptr, nullptr));
   235  
   236    absl::Notification notification;
   237    Message send_msg('a');
   238    // Create a msghdr with a buffer allocated for control messages.
   239    Message recv_msg(0, kMessageSize, /*cmsg_sz=*/24);
   240  
   241    ScopedThread t([&send_msg, &send_socket, &notification] {
   242      while (!notification.HasBeenNotified()) {
   243        sendmsg(send_socket.get(), send_msg.header(), 0);
   244      }
   245    });
   246  
   247    int64_t bytes_received = 0;
   248    for (auto ignored : state) {
   249      int n = recvmsg(recv_socket.get(), recv_msg.header(), 0);
   250      TEST_CHECK(n > 0);
   251      bytes_received += n;
   252    }
   253  
   254    notification.Notify();
   255    recv_socket.reset();
   256  
   257    state.SetBytesProcessed(bytes_received);
   258  }
   259  
   260  BENCHMARK(BM_RecvmsgWithControlBuf)->UseRealTime();
   261  
   262  // BM_SendmsgTCP measures the sendmsg throughput with varying payload sizes.
   263  //
   264  // state.Args[0] indicates whether the underlying socket should be blocking or
   265  // non-blocking w/ 0 indicating non-blocking and 1 to indicate blocking.
   266  // state.Args[1] is the size of the payload to be used per sendmsg call.
   267  void BM_SendmsgTCP(benchmark::State& state) {
   268    auto listen_socket =
   269        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
   270  
   271    // Initialize address to the loopback one.
   272    sockaddr_storage addr = ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(AF_INET));
   273    socklen_t addrlen = sizeof(addr);
   274  
   275    // Bind to some port then start listening.
   276    ASSERT_THAT(bind(listen_socket.get(),
   277                     reinterpret_cast<struct sockaddr*>(&addr), addrlen),
   278                SyscallSucceeds());
   279  
   280    ASSERT_THAT(listen(listen_socket.get(), SOMAXCONN), SyscallSucceeds());
   281  
   282    // Get the address we're listening on, then connect to it. We need to do this
   283    // because we're allowing the stack to pick a port for us.
   284    ASSERT_THAT(getsockname(listen_socket.get(),
   285                            reinterpret_cast<struct sockaddr*>(&addr), &addrlen),
   286                SyscallSucceeds());
   287  
   288    auto send_socket =
   289        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
   290  
   291    ASSERT_THAT(
   292        RetryEINTR(connect)(send_socket.get(),
   293                            reinterpret_cast<struct sockaddr*>(&addr), addrlen),
   294        SyscallSucceeds());
   295  
   296    // Accept the connection.
   297    auto recv_socket =
   298        ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_socket.get(), nullptr, nullptr));
   299  
   300    // Check if we want to run the test w/ a blocking send socket
   301    // or non-blocking.
   302    const int blocking = state.range(0);
   303    if (!blocking) {
   304      // Set the send FD to O_NONBLOCK.
   305      int opts;
   306      ASSERT_THAT(opts = fcntl(send_socket.get(), F_GETFL), SyscallSucceeds());
   307      opts |= O_NONBLOCK;
   308      ASSERT_THAT(fcntl(send_socket.get(), F_SETFL, opts), SyscallSucceeds());
   309    }
   310  
   311    absl::Notification notification;
   312  
   313    // Get the buffer size we should use for this iteration of the test.
   314    const int buf_size = state.range(1);
   315    Message send_msg('a', buf_size), recv_msg(0, buf_size);
   316  
   317    ScopedThread t([&recv_msg, &recv_socket, &notification] {
   318      while (!notification.HasBeenNotified()) {
   319        TEST_CHECK(recvmsg(recv_socket.get(), recv_msg.header(), 0) >= 0);
   320      }
   321    });
   322  
   323    int64_t bytes_sent = 0;
   324    int ncalls = 0;
   325    for (auto ignored : state) {
   326      int sent = 0;
   327      while (true) {
   328        struct msghdr hdr = {};
   329        struct iovec iov = {};
   330        struct msghdr* snd_header = send_msg.header();
   331        iov.iov_base = static_cast<char*>(snd_header->msg_iov->iov_base) + sent;
   332        iov.iov_len = snd_header->msg_iov->iov_len - sent;
   333        hdr.msg_iov = &iov;
   334        hdr.msg_iovlen = 1;
   335        int n = RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0);
   336        ncalls++;
   337        if (n > 0) {
   338          sent += n;
   339          if (sent == buf_size) {
   340            break;
   341          }
   342          // n can be > 0 but less than requested size. In which case we don't
   343          // poll.
   344          continue;
   345        }
   346        // Poll the fd for it to become writable.
   347        struct pollfd poll_fd = {send_socket.get(), POLL_OUT, 0};
   348        EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10),
   349                    SyscallSucceedsWithValue(0));
   350      }
   351      bytes_sent += static_cast<int64_t>(sent);
   352    }
   353  
   354    notification.Notify();
   355    send_socket.reset();
   356    state.SetBytesProcessed(bytes_sent);
   357  }
   358  
   359  void Args(benchmark::internal::Benchmark* benchmark) {
   360    for (int blocking = 0; blocking < 2; blocking++) {
   361      for (int buf_size = 1024; buf_size <= 256 << 20; buf_size *= 2) {
   362        benchmark->Args({blocking, buf_size});
   363      }
   364    }
   365  }
   366  
   367  BENCHMARK(BM_SendmsgTCP)->Apply(&Args)->UseRealTime();
   368  
   369  }  // namespace
   370  
   371  }  // namespace testing
   372  }  // namespace gvisor