Boost ASIO async_write_some确实很慢

Boost ASIO async_write_some is really slow

本文关键字:write ASIO async Boost some      更新时间:2023-10-16

我终于发现了服务器的瓶颈,原来是async_writeasync_write_some也是如此。

这里有以下基准代码:

struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
//boost::asio::async_write(mMainData.mSocket, boost::asio::buffer(pSendBuff->pBuffer, pSendBuff->dwUsedSize), mMainData.mStrand.wrap(boost::bind(&CServer::WriteHandler, pServer, this, pSendBuff, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));
mMainData.mSocket.async_write_some(boost::asio::buffer(pSendBuff->pBuffer, pSendBuff->dwUsedSize), (boost::bind(&CServer::WriteHandler, pServer, this, pSendBuff, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));
clock_gettime(CLOCK_MONOTONIC, &end);
timespec temp;
if ((end.tv_nsec - start.tv_nsec) < 0)
{
temp.tv_sec = end.tv_sec - start.tv_sec - 1;
temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
}
else
{
temp.tv_sec = end.tv_sec - start.tv_sec;
temp.tv_nsec = end.tv_nsec - start.tv_nsec;
}
pLogger->WriteToFile("./Logs/Benchmark_SendPacketP_AsyncWrite.txt", "dwDiff: %.4frn", (float)temp.tv_nsec / 1000000.0f);

输出:

-[2016.05.21 03:45:19] dwDiff: 0.0552ms
-[2016.05.21 03:45:19] dwDiff: 0.0404ms
-[2016.05.21 03:45:19] dwDiff: 0.0542ms
-[2016.05.21 03:45:20] dwDiff: 0.0576ms

这速度慢得离谱,因为它是一个游戏服务器,我需要在一个频道中有300名玩家的房间频道中广播数据包,想象一下它给我的玩家带来的网络延迟。

当然,这个测试是在只有我自己在服务器上的情况下完成的。

是我的代码错了,还是我在ASIO实现逻辑中遗漏了什么?

CXXFLAGS: -ggdb -ffunction-sections -Ofast -m64 -pthread -fpermissive -w -lboost_system -lboost_thread -Wall -fomit-frame-pointer
LDFLAGS: -Wl,-gc-sections -m64 -pthread -fpermissive -w -lboost_system -lboost_thread -lcurl

硬件是:Intel Xeon E3-1231v3(4核,8线程)64GB内存1GBPS上行链路

我正在生产8名ASIO工人。

所以我用调试器进入async_write,发现了这个:

template <typename ConstBufferSequence, typename Handler>
void async_send(base_implementation_type& impl,
const ConstBufferSequence& buffers,
socket_base::message_flags flags, Handler& handler)
{
bool is_continuation =
boost_asio_handler_cont_helpers::is_continuation(handler);
// Allocate and construct an operation to wrap the handler.
typedef reactive_socket_send_op<ConstBufferSequence, Handler> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
boost_asio_handler_alloc_helpers::allocate(
sizeof(op), handler), 0 };
p.p = new (p.v) op(impl.socket_, buffers, flags, handler);
BOOST_ASIO_HANDLER_CREATION((p.p, "socket", &impl, "async_send"));
start_op(impl, reactor::write_op, p.p, is_continuation, true,
((impl.state_ & socket_ops::stream_oriented)
&& buffer_sequence_adapter<boost::asio::const_buffer,
ConstBufferSequence>::all_empty(buffers)));
p.v = p.p = 0;
}

为什么要在一个本应是高性能的库中调用boost::asio的"new"?有没有预先创建它试图分配的内容?很抱歉,我无法描述内部结构,因为我使用Visual GDB和Microsoft Visual Studio进行开发,GCC 4.8.5工具集在VMWare中运行。

我知道这个答案有点晚了,但我会发布它,以防有人觉得它有用。

事实上,当发布完成处理程序时,会调用new。然而,官方文档解释了如何通过实现自定义内存管理来进行优化,从而避免相关的运行时开销。以下是示例:完成处理程序的自定义内存管理

如果没有探查器,试图确定哪条指令是瓶颈可能是徒劳的耐心测试。创建一个最小的示例可能有助于识别特定环境中问题的根源。例如,在既没有I/O也没有io_service争用的受控场景中,当使用本机write()和Asio的async_write()时,我观察到0.015ms~的写入。

试图解决的问题是以最小的延迟向300个对等体写入相同的消息。一种解决方案可能是将问题并行化:与其让单个作业将消息串行写入300个对等体,不如考虑使用并行运行的n作业,并将消息串行写到300/n个对等体。粗略估计:

  • 如果连续执行300次写入,每次写入花费.015ms(在受控环境中使用本机write()时观察到的平均值),则最终写入将在第一次写入后4.485ms开始
  • 如果根据潜在的并发限制(本例中为8)分批进行300次写入,则将有8个作业并行运行,串行执行38次写入。如果每次写入花费0.0576ms(在实际系统上观察到),则最终写入将在第一次写入后2.13ms开始

根据上述估计,通过并行该问题,即使每个单独的asyc_write操作的时间比预期的要长,写入300个对等点也需要一半的时间。请记住,这些都是粗略的估计,需要进行分析以确定理想的并发量,并确定潜在的瓶颈。