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, ¬ification] { 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, ¬ification] { 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, ¬ification] { 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, ¬ification] { 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, ¬ification] { 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, ¬ification] { 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