Boost.ASIO 性能不佳
Posted
技术标签:
【中文标题】Boost.ASIO 性能不佳【英文标题】:Poor boost.ASIO performance 【发布时间】:2011-01-03 14:20:50 【问题描述】:我在 Windows 上使用 boost::asio 进行了一个非常简单的服务器/客户端性能测试,它的性能似乎很差。我希望我只是错误地使用了该库,并希望得到任何建议。
我有一个会话类,它先写一个消息长度,然后再写一个消息,然后等待读取一个消息长度,然后再读取一个消息,并且不停地一遍又一遍地这样做。但是,当我在自己的计算机上本地运行它时,我会获得极快的性能;当我在一台计算机上运行服务器并在另一台计算机上运行客户端时,即使在同一网络上,性能也会降低,读取/写入操作需要长达 1 秒的时间。
服务器源代码文件如下:
#include <cstdlib>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
using namespace std;
class Session
public:
Session(io_service& ioService)
: m_socket(ioService)
tcp::socket& GetSocket()
return m_socket;
void StartRead()
m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize);
async_read(m_socket, buffer(m_messageSizeIterator, sizeof(m_messageSize)),
bind(&Session::HandleSizeRead, this, placeholders::error,
placeholders::bytes_transferred));
void StartWrite(const char* message, int messageSize)
m_messageSize = messageSize;
m_message = new char[m_messageSize];
memcpy(m_message, message, m_messageSize);
async_write(m_socket, buffer(&m_messageSize, sizeof(int)),
bind(&Session::HandleSizeWritten, this, placeholders::error));
void HandleSizeRead(const system::error_code& error,
size_t bytes_transferred)
if(!error)
m_message = new char[m_messageSize];
async_read(m_socket, buffer(m_message, m_messageSize),
bind(&Session::HandleMessageRead, this, placeholders::error,
placeholders::bytes_transferred));
else
delete this;
void HandleMessageRead(const system::error_code& error,
size_t bytes_transferred)
if(!error)
cout << string(m_message, m_messageSize) << endl;
async_write(m_socket, buffer(&m_messageSize, sizeof(int)),
bind(&Session::HandleSizeWritten, this, placeholders::error));
else
delete this;
void HandleSizeWritten(const system::error_code& error)
if(!error)
async_write(m_socket, buffer(m_message, m_messageSize),
bind(&Session::HandleMessageWritten, this, placeholders::error));
else
delete this;
void HandleMessageWritten(const system::error_code& error)
if(!error)
delete m_message;
m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize);
async_read(m_socket, buffer(m_messageSizeIterator,
sizeof(m_messageSize)), bind(&Session::HandleSizeRead, this,
placeholders::error, placeholders::bytes_transferred));
else
delete this;
private:
tcp::socket m_socket;
int m_messageSize;
char* m_messageSizeIterator;
char* m_message;
;
class Server
public:
Server(io_service& ioService, short port)
: m_ioService(ioService),
m_acceptor(ioService, tcp::endpoint(tcp::v4(), port))
Session* new_session = new Session(m_ioService);
m_acceptor.async_accept(new_session->GetSocket(), bind(&Server::HandleAccept,
this, new_session,asio::placeholders::error));
void HandleAccept(Session* new_session, const system::error_code& error)
if(!error)
new_session->StartRead();
new_session = new Session(m_ioService);
m_acceptor.async_accept(new_session->GetSocket(), bind(
&Server::HandleAccept, this, new_session, placeholders::error));
else
delete new_session;
private:
io_service& m_ioService;
tcp::acceptor m_acceptor;
;
int main(int argc, char* argv[])
try
if(argc != 2)
cerr << "Usage: server <port>\n";
return 1;
io_service io_service;
Server s(io_service, atoi(argv[1]));
io_service.run();
catch(std::exception& e)
cerr << "Exception: " << e.what() << "\n";
return 0;
而客户端代码如下:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
using namespace std;
class Session
public:
Session(io_service& ioService)
: m_socket(ioService)
tcp::socket& GetSocket()
return m_socket;
void StartRead()
m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize);
async_read(m_socket, buffer(m_messageSizeIterator, sizeof(m_messageSize)),
bind(&Session::HandleSizeRead, this, placeholders::error,
placeholders::bytes_transferred));
void StartWrite(const char* message, int messageSize)
m_messageSize = messageSize;
m_message = new char[m_messageSize];
memcpy(m_message, message, m_messageSize);
async_write(m_socket, buffer(&m_messageSize, sizeof(int)),
bind(&Session::HandleSizeWritten, this, placeholders::error));
void HandleSizeRead(const system::error_code& error,
size_t bytes_transferred)
if(!error)
m_message = new char[m_messageSize];
async_read(m_socket, buffer(m_message, m_messageSize),
bind(&Session::HandleMessageRead, this, placeholders::error,
placeholders::bytes_transferred));
else
delete this;
void HandleMessageRead(const system::error_code& error,
size_t bytes_transferred)
if(!error)
cout << string(m_message, m_messageSize) << endl;
async_write(m_socket, buffer(&m_messageSize, sizeof(int)),
bind(&Session::HandleSizeWritten, this, placeholders::error));
else
delete this;
void HandleSizeWritten(const system::error_code& error)
if(!error)
async_write(m_socket, buffer(m_message, m_messageSize),
bind(&Session::HandleMessageWritten, this, placeholders::error));
else
delete this;
void HandleMessageWritten(const system::error_code& error)
if(!error)
delete m_message;
m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize);
async_read(m_socket, buffer(m_messageSizeIterator,
sizeof(m_messageSize)), bind(&Session::HandleSizeRead, this,
placeholders::error, placeholders::bytes_transferred));
else
delete this;
private:
tcp::socket m_socket;
int m_messageSize;
char* m_messageSizeIterator;
char* m_message;
;
int main(int argc, char* argv[])
try
if(argc != 3)
cerr << "Usage: client <host> <port>\n";
return 1;
io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);
tcp::resolver::iterator iterator = resolver.resolve(query);
Session session(io_service);
tcp::socket& s = session.GetSocket();
s.connect(*iterator);
cout << "Enter message: ";
const int MAX_LENGTH = 1024;
char request[MAX_LENGTH];
cin.getline(request, MAX_LENGTH);
int requestLength = strlen(request);
session.StartWrite(request, requestLength);
io_service.run();
catch (std::exception& e)
cerr << "Exception: " << e.what() << "\n";
return 0;
任何帮助将不胜感激,谢谢。
出于我的目的,发送非常非常小的消息并想要虚拟实时回复,禁用 Nagle 算法是导致性能不佳的原因。
【问题讨论】:
您是否排除了您的路由器可能正在做的事情导致问题的可能性?每台机器的 CPU 使用率如何? 两台机器的CPU使用率都是0。至于路由器的问题,我在不使用 ASIO 的情况下编写了一个类似的程序,它运行得非常快。 首先使用 iperf 测试您的链接。然后,检测整个过程 - 是否创建了套接字?绑定成功了吗?解决方法有用吗?连接到服务器是否有效?第一次发送有用吗?第一个接收有效吗?是否有任何网络 API 调用返回任何错误?有什么事情比预期的要长吗?查看网络流量。服务器上有防火墙吗?是否启用了 Nagle 算法?服务器是否需要很长时间才能响应?客户端中的非网络代码是否有一些您没有预料到的延迟? 感谢您的帮助 Permaquid。我一一浏览了您的所有建议,最后问题是启用了 Nagle 的算法,当出于我的目的发送如此小的消息并希望虚拟实时响应时,最好的办法是禁用它。现在它的表现非常好。 Kranar,你现在的表现怎么样?只是出于兴趣。 【参考方案1】:您必须关闭Nagle algorithm。调用:
m_socket.set_option(tcp::no_delay(true));
适合您的代码的地方。
【讨论】:
+1 用于在 wiki 上的 nagle 中提供建议和链接。但是 - 本来希望看到一个警告,即关闭 Nagle 可能会降低整体吞吐量。 @quixver 如果您发送的字节之间有一个小的间隙,那么任何 nagle 都不会导致网络上出现更多的数据包。合并的计时器(即 Nagle)将导致更少的数据包,从而提高整体网络吞吐量。这适用于交互式键盘流量(例如 telnet、ssh),并且在 20 年前是以太网流量的重要组成部分。对于程序到程序的通信,Nagle 导致整体吞吐量较低(如原始问题中的情况),而不是更高的吞吐量。例如,看到整个消息已传递给 async_write(),因此无需等待发送。【参考方案2】:就我的目的而言,发送非常非常小的消息并想要虚拟实时回复,禁用 Nagle 算法是导致性能不佳的原因。
【讨论】:
以上是关于Boost.ASIO 性能不佳的主要内容,如果未能解决你的问题,请参考以下文章
evpp性能测试: 与Boost.Asio进行吞吐量对比测试
使用 boost::asio::async_wait_until 和 boost::asio::streambuf
boost asio 学习 boost::asio 网络封装