我有一个与网络相关的Linux应用程序,它从多个源接收RTP流,进行非常简单的数据包修改,然后将流转发到最终目的地。
我应该如何决定需要多少线程来处理这些数据?我认为不能为每个RTP流打开一个线程,因为可能会有成千上万个。我应该考虑CPU核心的数量吗?还有什么其他重要因素吗? 谢谢。
我有一个与网络相关的Linux应用程序,它从多个源接收RTP流,进行非常简单的数据包修改,然后将流转发到最终目的地。
我应该如何决定需要多少线程来处理这些数据?我认为不能为每个RTP流打开一个线程,因为可能会有成千上万个。我应该考虑CPU核心的数量吗?还有什么其他重要因素吗? 谢谢。
了解在服务器上使用多个线程的目的非常重要;在服务器中,许多线程的作用是减少延迟而不是增加速度。你不能通过拥有更多线程来使CPU更快,但你可以使一个线程在给定时间内始终出现以处理请求。
拥有一堆只是并行移动数据的线程是一种相当低效的方法(自然地,为每个请求创建一个线程会完全失败)。使用线程池模式可以是一个更有效、专注于减少延迟的方法。
现在,在线程池中,你至少要有和你的CPU/核心数量一样多的线程。你可以拥有比这更多的线程,但额外的线程只会再次减少延迟而不是增加速度。
把组织服务器线程的问题看作是组织超市排队的问题。你想要很多工作较慢的收银员还是一个工作超级快的收银员?快速收银员的问题不是速度,而是一个购物车很多的顾客可能仍然需要花费他们很多的时间。许多线程的需求来自于可能有一些请求需要很长时间并阻塞所有线程的可能性。按照这种推理,无论你是否从许多较慢的收银员中受益,取决于你是否有相同数量或非常不同数量的杂货。回到基本模型,这意味着你必须玩弄你的线程数,以确定在给定流量特性下什么是最优的,观察处理每个请求所需时间。
传统上,合理线程的数量取决于执行单元的数量、IO与计算之比以及可用内存。
XU
)这个数值表示可以同时活动的线程数量。根据计算情况,这可能或可能不包括超线程——混合指令工作负载效果更好。
%IO
)如果线程从不等待IO而是一直计算(%IO = 0),那么使用更多线程只会增加内存压力和上下文切换的开销。如果线程始终等待IO而从不计算(%IO = 1),那么使用poll()
或select()
的变体可能是个好主意。
对于所有其他情况,XU / %IO
可以近似地确定需要多少线程才能完全利用可用的XU。
Mem
)这更像是一个上限。每个线程使用一定量的系统资源(MemUse
)。Mem / MemUse
可以近似确定系统可以支持多少线程。
即使您可以猜测或(更好的是)测量上述数字,整个系统的性能仍可能受到其他因素的限制。例如,可能还有另一个运行在该系统上的服务,它使用了一些XU和内存。另一个问题是可用的IO带宽(IOCap
)。如果您每传输一个字节所需的计算资源比您的XUs提供的要少,那么显然您需要更少地关注完全利用它们,而更多地关注增加IO吞吐量。
有关后一个问题的更多信息,请参见这篇介绍屋顶线模型的Google Talk视频。
听从那些建议你使用libevent(或特定于操作系统的实用程序,如epoll / kqueue)的人。在许多连接的情况下,这是绝对必要的,因为像你说的那样,创建线程将会给性能带来巨大的影响,而select()也不完全满足要求。
避免尝试为每个客户端请求创建一个(甚至N个)线程是一个好主意。这种方法经典上不可扩展,您肯定会遇到内存使用或上下文切换的问题。相反,您应该考虑使用线程池方法,并将传入的请求视为任何线程在池中处理的任务。然后,此方法的可伸缩性受理想线程数的限制 - 通常与CPU核心数有关。您希望尝试使每个线程在单个核心上恰好使用100%的CPU - 因此,在理想情况下,您将每个核心拥有1个线程,这将将上下文切换减少为零。根据任务的性质,这可能不可能,也许线程必须等待外部数据,或从磁盘读取或其他原因,因此您可能会发现线程数增加了一些比例。