2016-11-27 59 views
2

以下是我编写的一个简单TCP服务器和匹配客户端以开始练习boost的asio库Example TCP Client/Server的示例。使用升级asio TCP套接字无法解释不良的带宽性能

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

就是这样 - 仅此而已。展位示例运行在几个线程上,大多使用默认设置,没有任何随意放置的睡觉可能会丢掉东西......它们非常简单易懂,实际上除了直接调用以增强目标隔离问题。

事情是,客户端的输出如下:

兆字节/秒:51.648908,千兆字节/秒:0.051649,兆位/秒:413.191267,比特/秒:0.413191

注:

  • 我现在正在使用笔记本电脑关闭电池电源。如果我将它插入电源插孔,它会跳到〜0.7 Gbits/sec。
  • 我试着发送小2048字节的消息到当前的8兆字节的消息。
  • 我试过了启用和禁用的nagle算法。
  • 我试过调整大小发送和接收OS缓冲区。
  • 所有这些都通过回送,127.0.0.1运行。
  • 通过Wireshark监测环回显示相同的低带宽使用情况。

促使我写这个问题的实际观点就是这一点。 iperf tool能够通过本地主机使用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 boost asio性能”的搜索都倾向于提出禁用nagle或调整OS套接字缓冲区的建议。

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

+0

我通过评论所有套接字的接收和发送缓冲区大小选项并将所需的调整大小推迟到TCP自动调整而将其提升至3GBps。此外,我更改了服务器接收代码,以避免迭代缓冲区,这是浪费。我在http://melpon.org/wandbox/permlink/UUlmqwH5tlD4Ah4f更改了代码 – Arunmu

+0

最好看看iperf在做什么。可能是它使用一些零拷贝机制来发送? – Arunmu

+0

哦..我没有看到它是iperf每秒33G'bits'。然后我的变化是24Gbits每秒:)。足够接近,但我相信很多事情都可以完成。再次重新运行,这次显示了大约30 Gbps的速度。 – Arunmu

回答

3

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

  1. 手册TCP缓冲区大小

的设置这是更好的左边为TCP算法要弄清楚什么是最好的尺寸。这通常在TCP slow-start阶段期间确定,其中TCP算法最终基于拥塞根据最佳可能window-size来做出决定。由于我们使用本地主机,并且在网络组件之间也没有任何指向连接的指向,所以拥塞将接近于零。

  • 启用纳格算法
  • 这不是实际需要的,因为你不发送短长度的成帧分组。通常情况下,开机时Nagles会给你latency的一些好处(对于吞吐量我不太确定是否会有任何改进)。

  • 在服务器端
  • 无用处理我看到你遍历接收到的缓冲器和做某种无意义检查。 iperf肯定不会那样做。我已经注释掉了那段代码。

  • 接收应用程序的缓冲区大小
  • 我不知道,但由于某种原因,你选择了只读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内存条 。

    +1

    感谢您的意见!当我上传我的例子时,我混淆了我的文件,但我也尝试了默认的boost :: asio设置(没有弄乱nagle或OS套接字缓冲区大小)。我没有尝试过的是你的**第四点**。这,特别是我的例子中的瓶颈! Boost只是在服务器端使用如此小的入站缓冲区做了太多的工作。在我的i5 8Gb RAM Linux系统上扩大它使我达到40 Gbits/sec。再次感谢您的帮助! – JDR