我正在关注this示例。第37行表示工作线程数应该等于cpu核心数。为什么会这样?
如果有10k连接且我的系统有8个内核,这是否意味着8个工作线程将处理10k连接?为什么我不应该增加这个数字?
答案 0 :(得分:3)
上下文切换
对于OS来说,线程之间的上下文切换需要一点时间。拥有大量线程,每个线程执行相对较少的工作,意味着上下文切换时间开始成为应用程序整体运行时的重要部分。
例如,执行上下文切换可能需要大约10微秒的操作系统;如果线程在返回睡眠之前只做了15微秒的工作,那么40%的运行时只是上下文切换!
效率低下,当您的硬件,电源和冷却成本达到顶峰时,这种低效率真正开始出现。线程很少意味着操作系统不必像任何时候那样切换上下文。
因此,在您的情况下,如果您的要求是计算机处理10,000个连接并且您有8个核心,那么效率最佳点将是每个核心1250个连接。
每线程更多客户
在服务器处理客户端请求的情况下,它归结为处理每个客户端涉及多少工作。如果这只是少量工作,那么每个线程都需要处理来自多个客户端的请求,以便应用程序可以处理大量客户端而无需大量线程。
在网络服务器中,这意味着熟悉select()或epoll()系统调用。调用时,这些都会使线程进入休眠状态,直到其中一个提到的文件描述符以某种方式变为就绪状态。但是,如果没有其他线程纠缠操作系统的运行时,操作系统不一定需要执行上下文切换;线程可以坐在那里打瞌睡,直到有事可做(至少那是我对OS操作的理解。每个人,如果我错了,请纠正我!)。当某些数据从客户端出现时,它可以更快地恢复。
这当然会使线程的源代码变得复杂得多。例如,您无法阻止从客户端读取数据;由epoll()告知文件描述符已经准备好读取并不意味着您可以立即读取您希望从客户端接收的所有数据。如果线程由于影响多个客户端的错误而停止。但这是为了获得最高效率所付出的代价。
并不一定就是你只需要8个线程就可以使用8个内核和10,000个连接。如果每次处理单个连接时,您的线程必须为每个连接执行某些操作,那么需要最小化的开销(通过每个线程拥有更多线程和更少连接)。 [select()系统调用就是这样,这就是epoll()被发明的原因。]你必须平衡上行开销与上下文切换的开销。
对于Linux中的单个进程,10,000个文件描述符很多(太多?),因此您可能必须有多个进程而不是多个线程。然后,在您的系统具有的任何响应时间/连接要求中,硬件是否能够从根本上支持10,000,这是一个小问题。如果它没有,那么您将被迫在两台或多台服务器上分发您的应用程序,这可能会变得非常复杂!
准确了解每个线程要处理的客户端数量取决于处理过程,是否涉及硬盘活动等等。因此,没有一个单一的答案;它对于不同的应用程序是不同的,并且对于不同机器上的相同应用程序也是如此。调整客户端/线程以实现最高效率是一项非常艰巨的工作。这就是Solaris上的dtrace,Linux上的ftrace,(尤其是像this那样使用的分析工具,我在x86硬件上的Linux上使用了很多)等等,可以提供帮助,因为它们可以让你理解在一个非常精细的范围内,您的线程处理来自客户端的请求所涉及的运行时很精确。
像谷歌这样的服装当然非常热衷于效率;他们经历了很多电力。我认为,当谷歌选择CPU,硬盘驱动器,内存等放入他们着名的本土服务器时,他们会按照每瓦特的搜索量来衡量性能"显然你必须成为一个非常大的装备才能让你对事情变得那么挑剔,但这就是事情的最终方式。其他效率
处理诸如TCP网络连接之类的事情本身就占用了大量的CPU时间,并且很难理解系统中所有CPU运行时已经消失的情况。对于网络连接,智能网卡中的TCP卸载等功能可以带来真正的好处,因为这样可以避免CPU承担纠错计算等负担。
TCP卸载镜像我们在高速大规模实时嵌入式信号处理领域所做的工作。我们使用的(奇怪的)互连需要零CPU时间来运行它们。因此,所有CPU时间都专用于处理数据,而专用硬件则用于移动数据。这带来了一些相当惊人的效率,因此可以构建一个具有更适度,更低成本,更少耗电的CPU的系统。
语言也会对效率产生根本影响;像Ruby,PHP,Perl这样的东西都非常好,但是最初使用它们但随后迅速发展的每个人最终都会获得像Java / Scala,C ++等更高效的东西。
答案 1 :(得分:1)
你的问题甚至比你想象的要好! :-P
如果您使用libevent执行网络,它可以在套接字上执行非阻塞I / O 。 一个线程可以做到这一点(使用一个核心),但这会使CPU利用不足。
但是如果你做“重”文件I / O ,那么内核就有没有非阻塞接口。 (许多系统完全没有任何关系,其他系统在该领域中有一些半生不熟的东西,但是不可移植。-Living不会使用它。) - 如果文件I / O瓶颈你的程序/ test,那么更多的线程就有意义了!如果使用硬盘,并且i / o调度程序正在重新排序请求以避免磁头移动等,则将取决于调度程序为完成其工作而考虑的请求数量。 100个待处理请求可能比8更好。
为什么不增加话题号?
如果完成非阻塞I / O:所有内核都使用thread-count = core-count。更多线程只意味着更多的线程切换而没有增益。
阻止I / O:你应该增加它!