我们已使用boost::asio
将每个连接通信模型的线程迁移到基于异步IO的TCP服务器。这种变化的原因是旧模型的扩展性不够好。我们平均持续约2k持续连接,并且每月都有增长趋势。
我的问题是,为完成处理程序调查io_service队列的理想工作线程数是多少 - 虚拟cpu核心的数量?
选择较小的数字可能会导致服务器消耗速度不够快且无法应对客户端发送消息的速率。
在这种情况下动态添加工作线程是否有意义?
更新: 可能这是我的实现,但我发现这个声明是提升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();
}
答案 0 :(得分:1)
在完美无阻塞I / O的理想情况下,一个完全适合L1缓存的工作集,而物理系统中没有其他进程,每个线程将使用处理器核心的整个资源。在这种情况下,理想的线程数是每个逻辑核心一个。
如果I / O的任何部分阻塞,那么添加比核心数更多的线程是有意义的,这样就不会让核心处于空闲状态。如果花费一半的线程时间被阻塞,那么每个核心应该有近2个线程。如果75%的线程时间被占用,则每个核心应该有3或4个,依此类推。为此目的,上下文切换开销被视为阻塞。
我注意到,当微软不得不对此进行盲目猜测时,他们倾向于每个核心使用两到四个线程。根据您做出此决定的预算,我会选择2或4,或者从每个核心的一个线程开始,然后继续工作,测量吞吐量(请求服务/秒)和延迟(最小,最大和平均响应时间)直到我达到最佳点。
如果您正在处理完全不同的程序,则动态调整此值才有意义。对于可预测的工作负载,您的硬件有一个最佳位置,即使需要完成大量工作,也不会有太大变化。如果您正在制作通用Web服务器,则可能需要进行动态调整。