【发布时间】:2017-02-06 15:20:46
【问题描述】:
我们已使用boost::asio 将每个连接通信模型的线程迁移到基于异步 IO 的 TCP 服务器。这种变化的原因是旧模型的扩展性不够好。我们平均有大约 2000 个永久连接,并且有每月保持增长的趋势。
我的问题是,将轮询 io_service 队列以获取完成处理程序的理想工作线程数是多少 - 虚拟 cpu 内核的数量?
选择较小的数字会导致服务器消耗不够快,无法应对客户端发送消息的速率的情况。
在这种情况下动态添加工作线程有意义吗?
更新: 可能这是我的实现,但我发现 boost asio 文档中的这个语句的一部分令人困惑:
诸如每个连接线程(仅同步方法需要)等实施策略可能会降低系统性能 性能,由于增加了上下文切换,同步和 CPU之间的数据移动。使用异步操作是可能的 通过最小化的数量来避免上下文切换的成本 操作系统线程——通常是有限的资源——并且只有 激活具有事件的逻辑控制线程 过程。
就好像你有 X 线程在有 X 核的机器上泵送完成事件 - 1)你不能保证每个线程都有一个专用的 cpu 和 2)如果我的连接是持久的,我没有任何保证执行 async_read 的线程与执行完成处理程序的线程相同。
void Connection::read {
boost::asio::async_read(socket, boost::asio::buffer(buffer, 18),
boost::bind(&Connection::handleRead, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void Connection::handleRead(const boost::system::error_code &error,
std::size_t bytes_transferred) {
// processing of the bytes
...
// keep processing on this socket
read();
}
【问题讨论】:
-
这显然是不可能回答的,除非说“这取决于”。我想说的是,理想的设计应该是一个,每个安装的 NIC 不超过两个内核线程,直到 NIC 带宽饱和。如果你超过了,那么你需要重构你的软件以不那么低效。阅读 nginx 如何实现 10 Gbps NIC 可扩展性,或聘请 ASIO 顾问专家为您提供建议。
-
人们(不一定是你)往往会大大低估单线程的威力。单个线程可以做的工作量是巨大的。您需要平衡的问题是 1) 线程过多导致的上下文切换导致延迟和吞吐量问题,2) 泵送完成事件的线程太少导致高延迟和吞吐量低,以及 3) 线程过多导致工作集大小超过必要的大小堆栈导致 CPU 缓存性能不佳。所有这些对于您的工作量的平衡是最佳点,找到它的唯一方法是进行实验和测量。
-
我想对此进行更多跟进。我们在异步 IO 方法中看到的是,延迟问题已按预期消失,但 ctxt 开关/秒的绝对数量大约是我们过去在每个连接模型中的线程中的 10 倍。换句话说,与旧模型中的约 1k 个工作线程相比,24 个 IO 线程泵送完成事件产生的上下文切换要多得多。您能分享一下您对此的看法吗?
-
@ladaManiak - 如果您的旧模型基于轮询,那么它可能不会像异步模型那样产生处理器,从而导致更多的上下文切换。我觉得您减少的延迟与此处上下文切换的增加直接相关 - 减少延迟通常会增加 CPU 开销。
-
@hoodaticus 我试图详细说明并编辑了我最初的问题。
标签: asynchronous io boost-asio