使用boost asio TCP套接字无法解释带宽性能不佳的问题

时间:2016-11-27 16:57:08

标签: c++ networking tcp boost-asio

以下是我写的一个简单的TCP服务器和匹配客户端的示例,以开始练习boost的asio库,Example TCP Client/Server

  • 客户端只需从内存缓冲区中尽可能快地连接和发送数据。
  • 服务器只监听消息并打印从完整消息中获取的字节数。

它就是 - 仅此而已。展位示例运行在几个线程上,大多数情况下使用默认设置,没有任何随意放置的睡眠可能会让事情失效......他们真的很容易理解,实际上什么都没有除了直接呼吁提升以解决问题的目标。

问题是,客户的输出如下:

Mbytes / sec:51.648908,Gbytes / sec:0.051649,Mbits / sec:413.191267,Gbits / sec:0.413191

注意:

  • 我现在正在关闭电池供电。如果我将其插入电源插座,它将跳至~0.7 Gbits / sec。
  • 我尝试向当前的8 MB消息发送小的2048字节消息。
  • 我已尝试启用和禁用nagle算法。
  • 我已尝试调整发送和接收操作系统缓冲区的大小。
  • 所有这些都是通过环回运行127.0.0.1
  • 通过Wireshark监控环回显示了相同的低带宽使用率。

促使我写这个问题的实际观点是这一点。 iperf tool能够通过localhost使用TCP实现 33.0 Gbits / sec

$ iperf --client 127.0.0.1
------------------------------------------------------------
Client connecting to 127.0.0.1, TCP port 5001
TCP window size: 2.50 MByte (default)
------------------------------------------------------------
[  3] local 127.0.0.1 port 41952 connected with 127.0.0.1 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  38.4 GBytes  33.0 Gbits/sec

所有搜索的内容都符合" TCP提升asio性能"倾向于最终提出禁用nagle或调整OS套接字缓冲区的建议。

如果有人能指出我正确的方向来理解为什么我使用boost asio获得如此低带宽的性能,我将非常感激!

1 个答案:

答案 0 :(得分:3)

首先让我指出你为这种测试做错的事情。

  1. 手动设置TCP缓冲区大小
  2. 最好让TCP算法找出最佳尺寸。这通常在TCP slow-start阶段确定,其中TCP算法最终根据拥塞决定最佳可能window-size。由于我们使用的是本地主机,并且在网络组件之间没有任何连接点指向连接,因此拥塞将接近于零。

    1. 启用Nagles算法
    2. 实际上并不需要这样做,因为您没有发送短长度的成帧数据包。通常情况下,启用Nagles会在latency给你带来一些好处(对于吞吐量,我不确定是否会有任何改进)。

      1. 服务器端的不需要的处理
      2. 我看到你正在迭代收到的缓冲区并进行某种无意义的检查。 iperf肯定不会这样做。我已经注释掉了那段代码。

        1. 接收应用程序缓冲区大小
        2. 我不知道但是由于某种原因你选择每次接收只读2048个字节。有什么特别的原因吗?我已将其更改回客户端编写的实际大小。您可能只是在服务器接收部分排队更多数据。

          新服务器代码:

          #include <thread>
          #include <chrono>
          #include <vector>
          #include <signal.h>
          #include <asio.hpp>
          #include <system_error>
          
          namespace
          {
          bool keepGoing = true;
          void shutdown(int)
          {
                  keepGoing = false;
          }
          
          std::size_t bytesAccum = 0;
          void justReceive(std::error_code ec, std::size_t bytesReceived,
              asio::ip::tcp::socket &socket, std::vector<unsigned char> &buffer)
          {
                  bytesAccum += bytesReceived;
          /*
                  auto end = buffer.begin() + bytesReceived;
                  for (auto it = buffer.begin(); it != end; ++it)
                  {
                          if (*it == 'e')
                          {
                                  std::printf("server got: %lu\n", bytesAccum);
                                  bytesAccum = 0;
                          }
                  }
          */
                  socket.async_receive(
                      asio::buffer(buffer),
                      0,
                      [&] (auto ec, auto bytes) {
                        justReceive(ec, bytes, socket, buffer);
                      });
          }
          }
          
          int main(int, char **)
          {
                  signal(SIGINT, shutdown);
          
                  asio::io_service io;
                  asio::io_service::work work(io);
          
                  std::thread t1([&]() { io.run(); });
                  std::thread t2([&]() { io.run(); });
                  std::thread t3([&]() { io.run(); });
                  std::thread t4([&]() { io.run(); });
          
                  asio::ip::tcp::acceptor acceptor(io,
                      asio::ip::tcp::endpoint(
                          asio::ip::address::from_string("127.0.0.1"), 1234));
                  asio::ip::tcp::socket socket(io);
          
                  // accept 1 client
                  std::vector<unsigned char> buffer(131072, 0);
                  acceptor.async_accept(socket, [&socket, &buffer](std::error_code ec)
                  {
                      // options
                      //socket.set_option(asio::ip::tcp::no_delay(true)); 
                      //socket.set_option(asio::socket_base::receive_buffer_size(8192  * 2));
                      //socket.set_option(asio::socket_base::send_buffer_size(8192));
          
                      socket.async_receive(
                          asio::buffer(buffer),
                          0,
                          [&](auto ec, auto bytes) {
                            justReceive(ec, bytes, socket, buffer);
                          });
                  });
          
                  while (keepGoing)
                  {
                          std::this_thread::sleep_for(std::chrono::seconds(1));
                  }
          
                  io.stop();
          
                  t1.join();
                  t2.join();
                  t3.join();
                  t4.join();
          
                  std::printf("server: goodbye\n");
          }
          

          新客户代码:

          #include <thread>
          #include <chrono>
          #include <vector>
          #include <signal.h>
          #include <asio.hpp>
          #include <system_error>
          
          namespace
          {
          bool keepGoing = true;
          void shutdown(int) { keepGoing = false; }
          }
          
          int main(int, char **)
          {
                  signal(SIGINT, shutdown);
          
                  asio::io_service io;
                  asio::io_service::work work(io);
          
                  std::thread t1([&]() { io.run(); });
                  std::thread t2([&]() { io.run(); });
                  std::thread t3([&]() { io.run(); });
                  std::thread t4([&]() { io.run(); });
          
                  asio::ip::tcp::socket socket(io);
                  auto endpoint = asio::ip::tcp::resolver(io).resolve({ 
                      "127.0.0.1", "1234" });
                  asio::connect(socket, endpoint);
          
                  // options to test
                  //socket.set_option(asio::ip::tcp::no_delay(true)); 
                  //socket.set_option(asio::socket_base::receive_buffer_size(8192));
                  //socket.set_option(asio::socket_base::send_buffer_size(8192 * 2));
          
                  std::vector<unsigned char> buffer(131072, 0);
                  buffer.back() = 'e';
          
                  std::chrono::time_point<std::chrono::system_clock> last = 
                      std::chrono::system_clock::now();
          
                  std::chrono::duration<double> delta = std::chrono::seconds(0);
          
                  std::size_t bytesSent = 0;
          
                  while (keepGoing)
                  {
                          // blocks during send
                          asio::write(socket, asio::buffer(buffer));
                          //socket.send(asio::buffer(buffer));
          
                          // accumulate bytes sent
                          bytesSent += buffer.size();
          
                          // accumulate time spent sending
                          delta += std::chrono::system_clock::now() - last;
                          last = std::chrono::system_clock::now();
          
                          // print information periodically
                          if (delta.count() >= 5.0) 
                          {
                                  std::printf("Mbytes/sec: %f, Gbytes/sec: %f, Mbits/sec: %f, Gbits/sec: %f\n",
                                              bytesSent / 1.0e6 / delta.count(),
                                              bytesSent / 1.0e9 / delta.count(),
                                              8 * bytesSent / 1.0e6 / delta.count(),
                                              8 * bytesSent / 1.0e9 / delta.count());
          
                                  // reset accumulators
                                  bytesSent = 0;
                                  delta = std::chrono::seconds(0);
                          }
                  }
          
                  io.stop();
          
                  t1.join();
                  t2.join();
                  t3.join();
                  t4.join();
          
                  std::printf("client: goodbyte\n");
          }
          

          注意:我使用了asio的独立版本,但OP报告的结果可以在我的机器上重现:

          MacBook Pro Yosemite - 2.6 GHz Intel Core i5处理器 - 8GB DDR3内存