我正在实现需要维护非常大量(100K或更多)长期连接的自定义服务器。服务器只是在套接字之间传递消息,它不会进行任何严肃的数据处理。消息很小,但其中很多都是每秒接收/发送的。减少延迟是目标之一。我意识到使用多个内核不会提高性能,因此我决定通过调用run_one
对象的poll
或io_service
方法在单个线程中运行服务器。无论如何,多线程服务器将更难实现。
可能存在哪些瓶颈?系统调用,带宽,完成队列/事件多路分解?我怀疑调度处理程序可能需要锁定(这是由asio库在内部完成的)。是否可以在boost.asio中禁用偶然队列锁定(或任何其他锁定)?
编辑:相关问题。多线程的系统调用性能是否有所改善?我的感觉是,因为系统调用是内核原子/同步的,所以添加更多线程不会提高速度。
答案 0 :(得分:16)
您可能希望阅读几年前的my question,我在为Blue Gene/Q supercomputer开发系统软件时首次调查Boost.Asio的可扩展性时问过它。
扩展到100k或更多连接应该不是问题,但是您需要了解明显的资源限制,例如打开文件描述符的最大数量。如果您还没有阅读开创性的C10K paper,我建议您阅读它。
使用单个线程和单个io_service
实现应用程序后,我建议调查一个调用io_service::run()
的线程池,然后再调查将io_service
固定到特定的线程和/或cpu。所有这三个设计的Asio文档中都包含多个示例,并且有关更多信息的several questions在SO上。请注意,当您引入多个调用io_service::run()
的线程时,您可能需要实现strand
以确保处理程序具有对共享数据结构的独占访问权。
答案 1 :(得分:9)
使用boost :: asio,您可以以相同的开发成本编写单线程或多线程服务器。您可以将单线程版本编写为第一个版本,然后根据需要将其转换为多线程版本。
通常,boost :: asio的唯一瓶颈是 epoll / kqueue 反应器正在使用互斥锁。所以,只有一个线程同时在做epoll。如果您拥有多线程服务器,这可能会降低性能,该服务器提供大量非常小的数据包。但是,无论如何,它应该比简单的单线程服务器更快。
现在谈谈你的任务。如果你只想在连接之间传递消息 - 我认为它必须是多线程服务器。问题是系统调用(recv / send等)。一个指令很容易想到CPU,但任何系统调用都不是非常“轻”的操作(一切都是相对的,但相对于你的任务中的其他工作)。因此,使用单线程,您将获得大量的系统调用开销,这就是我建议使用多线程方案的原因。
此外,您可以将 io_service 分开并使其成为“每个线程的io_service”惯用法。我认为这必须提供最佳性能,但它有一个缺点:如果io_service中的一个会得到太大的队列 - 其他线程将无法帮助它,所以一些连接可能会减速。另一方面,使用单个io_service - 队列溢出可能导致大的锁定开销。所有你能做的 - 做两种变体并测量带宽/延迟。实现这两种变体应该不会太难。