摘要
本文通过实验测试对linux、solaris (for intel)、freebsd和windows 2000在运行高性能网络应用程序方面的速度进行了比较。描述了如何根据需要从您的软件提供商那里获得相应性能和软件体系结构的产品,解释了每个设计是如何产生不同的性能特性的,并且最终确定了对于每个通用的网络程序设计来说哪个操作系统最为合适。我们通过仿真和真实环境测试提出了操作系统基准,并对相应结果给出了评价。
我们发现,与其所依赖的操作系统相比较,软件应用程序的体系结构是决定其执行速度的更主要因素。我们的基准程序(benchmarks)证明,基于进程的体系结构与异步任务体系结构之间会有12倍的性能差异。值得注意的是,即使使用最有效的异步体系结构,不同操作系统间的性能差异也会相差75%以上。疑惧我们的度量基准,我们发现linux是执行性能最好的操作系统,它比排在第二位的solaris的性能高35%,然后才是windows,排在最后的是freebsd。
背景
在lyris技术公司,我们的任务是编写高性能、跨平台、基于email的应用程序。众所周知,更好的应用程序性能是决定竞争优势的关键因素,所以我们花了大量的时间在影响应用程序性能的所有因素(软件、硬件和操作系统)方面进行调整。我们的客户经常问的一个问题是哪个操作系统最适合运行我们的软件。另一种情况是,他们已经选择了一个操作系统,他们会问怎样才能让他们的系统更快地运行我们的应用程序。另外,我们运营了一个主机托管(hosting)部门,我们希望在为我们的托管客户提供最好性能的前提下尽量降低我们的硬件成本。
大多互联网应用程序都是按照下面的步骤进行工作的:
接受一个tcp/ip连接请求或者与另外一台计算机建立一个连接;
一旦建立了连接,通过tcp/ip 交换各种各样基于文本的命令;
这些命令会引发不同的行为,如读磁盘(例如浏览网页)、写磁盘(例如将一条接收到的电子邮件消息进行排队)或者调用外部功能(例如邮件过滤、反向dns查找等)。
通常,一个网络应用程序涉及的性能问题包括:
用尽可能快的速度完成大量的并发任务;
有效地处理大量的等待(由慢速的tcp/ip连接引起或者等待连接的另一端发送下一个命令);
有效地执行tcp/ip操作。
为了设计最佳性能的网络应用程序,应用程序的软件设计人员必需选择一个充分考虑了上面的性能准则的软件体系结构。两个最主要的因素是任务体系结构和tcp/ip调用体系结构。
任务体系结构
在任务体系结构领域,有三个主要的技术:
每个任务一个进程(面向进程的)——运行程序的很多复本,每个复本每次只处理一个任务。有时候,每次创建一个新任务都需要相应地创建一个新的进程(如inetd、sendmail),但有些也设计成可以重用进程(如apache)。这种体系结构在低负载的情况下会产生很好的性能。在运行中等程度的负载时,如果进程影象比较小(如qmail)、已经实现了针对该应用程序的效率改进或者该类型的应用程序不会创建太多的同时任务时,其性能也还可以。如果采用了进程缓冲技术并且同时运行的进程总数不是很多(如中低程度的负载),就可以采用多cpu来解决问题。这种技术在所有的操作系统上都可以实现,然而,从实现的的角度讲,unix明显地要比windows更有效。(windows没有fork()系统调用,很少有windows应用程序采用这种技术,因为它在windows上实在太慢了)。
每个任务一个线程(多线程)——只运行该程序的一个复本,在这个复本中,每个任务都由一个单独的执行线程来处理。多线程应用程序在低-中负载情况下的性能非常好,在更高的负载下,其性能就明显下降了(但通常也是可以接受的)。然而,超高负载会将多线程应用程序带入死亡旋涡(death-spiral)。通常情况下,多线程应用程序最多可以处理500到1000个并发的任务。这在大多情况下是可以接受的。每个新认为使用一个新的线程,相对于新进程而言,新线程消耗更少的内存和更少的cpu处理能力。仅仅最流行的unix变种(通常是商用操作系统)才能够在沉重的多线程负载下保持稳定,所以很少有开放源码工程采用多线程技术。这种技术在多cpu上的性能可能比在单cpu上的性能还要差,因为多处理器计算机上的信号量(semaphore)锁处理的代价非常高。(多线程软件的例子有netscape web server和apache on windows)
一个线程处理多个任务(异步处理)——程序的一个复本运行固定数量的线程(通常每种类型的任务对应一个线程),每个线程通过一种称为异步(非阻塞)tcp/ip的技术处理大量的同类型任务。因为大多数程序根本不需要处理高负载,并且异步编程非常困难,所以很少有支持这种技术的程序出现。异步程序可以在多cpu计算机上平滑扩展,因为它们大多采用彼此独立的长效线程。这种技术很少需要跨cpu的锁,因此每个线程都可以永久且有效地分配给一个单独的cpu(如dns bind守侯进程daemon)
tcp/ip调用体系结构
第二个影响性能的主要因素是tcp/ip调用体系结构。在操作系统级,有多种方法可以实现相同的网络操作。tcp/ip的速度与优化程序设计之间存在着权衡的问题(更快的技术对编程人员来说意味着更多的工作)。加之,更快的技术并不是对所有的平台都可用;更高的性能需求可能会限制平台选择的自由度。
阻塞式tcp/ip调用
阻塞式tcp/ip调用等待所有被请求的操作完成后立即对结果进行操作。在任务数量比较少时,操作结果会在事件发生时立即予以响应;但在任务数量比较大的情况下,会导致操作系统的巨量上下文切换系统开销,这时的效率相当低。在低负载的情况下,阻塞(同步)tcp/ip调用产生很少的等待时间,这对低负载的web服务器一类的应用来说非常理想(页面响应非常迅速,条件是负载永远不会太高)。然而,如果使用的是面向进程的体系结构,且每一个新的网络连接都要建立一个相应的新进程(如inetd)时,阻塞tcp/ip所带来的等待时间上的性能就会被运行一个新进程所导致的显著的系统调用迅速抵消。
非阻塞tcp/ip调用
非阻塞(异步)tcp/ip调用发起一个操作后就转而进行其它的工作。当操作完成或者发生了一个事件,这个进程就会获得通知并进行相应的响应。这种两不处理模式需要更多的编程工作,有时响应新事件会花费少量的时间(增加了等待延时)。在中-高负载的情况下,这种非阻塞技术会产生更好的执行性能,并且可以避免讨厌的高负荷,但同阻塞tcp/ip调用相比,等待延时可能稍微长了点儿。
上述的每一个任务处理体系结构都与相应的tcp/ip系统调用模型相匹配。面向进程和多线程的程序可能会趋向于采用阻塞tcp/ip调用,因为这是编程的最简单方法并且大多数情况下只需要处理低负载。然而采用异步任务体系结构的应用程序就必需使用非阻塞的tcp/ip操作以处理多任务了:根本不能选择阻塞tcp/ip。因此,如果您发现一个网络应用程序采用了高度可扩展的异步任务体系结构,您当然也是从其所采用的最具扩展性的tcp/ip调用体系结构(非阻塞)上获得了不少好处。
真实环境测试
为了评估不同操作系统和网络应用程序的性能,我们进行了三类不同的测试:真实环境、磁盘i/o和任务体系结构比较。我们检测的操作系统包括linux (red hat 7.0, kernel 2.2.16-22)、solaris 2.8 for intel、freebsd 4.2和windows 2000 server。这些操作系统都是可获得的商业版本中最新的,并且没有重新编译(或者说我们将操作系统软件开包安装后就进行了测试)。我们在相同的4-gb scsi-3驱动器上(ibm 型号是 dcas-34330)安装了上述操作系统,并且在相同的机器(asus p3b主板、intel pentium iii 550-mhz处理器、384-mb sdram、adaptec 2940uw scsi控制器、ati rage pro 3d显卡、intel etherexpress pro 10/100 ethernet网卡)上进行测试。
我们采用测试我们自己开发的mailengine软件的邮件发送速度来进行真实环境测试。mailengine是一个邮件发送服务器,它可以移植到所有要进行测试的平台上(其实还包括sparc上的solaris),它采用的是异步体系结构(带有采用poll()系统调用的非阻塞tcp/ip)。为了不将电子邮件真的发送到具有200,000个成员的测试列表,我们是在测试模式下运行的mailengine。在这种模式下,mailengine实现发送邮件的所有步骤但是最后使用rset命令而不是data命令。这样就在真正发送数据之前退出quit了smtp连接,这样就不会向接收者发送任何电子邮件了。我们的工作量是将一条消息发送到分布在9113个域下的200,000个电子邮件地址。因为是同一条消息在内存中为所有的接收者进行排队,所以磁盘i/o并不是影响性能的主要因素。我们缓慢地提高同时连接的数量,以便观察负载的提升对性能的影响。
图1 操作系统的比较
图1(操作系统的比较)显示了在mailengine的测试模式中,不同操作系统上一定范围的同时连接数量下的电子邮件传输速度的测试结果。linux在速度上占有明显的优势,它比排在第二位的solaris快约35%。总体上来说,性能随着连接数量的增加而提高,这张图也显示速度的增长边际超过1500个连接。freebsd在加到多于1500个连接时的性能会有些下降。
在unix风格的操作系统上,需要适当的调整内核以便允许在一个进程中使用如此多的连接。除内核调整外,当负载超过2500个连接时,freebsd还会给出资源不足的警告并且停止运行。
文件系统测试
许多网络应用程序同样需要将信息在硬盘上进行排队以便以后处理(如sendmail的邮件队列)或处理溢出情况的能力。为了模仿在典型情况文件系统的效率,我们写了一个在单个目录下创建、写入和读回10,000个文件的c++程序,它每次只操作一个文件。为了全面测试文件系统对不同种类文件的整体效率,测试文件的大小从4 kb到128 kb递增。
图2 创建、写和读10,000个文件所需要的时间
图2显示了文件系统的测试结果。linux和windows的速度基本相同,它们都比另外两个明显地快得多:是freebsd的6倍、solaris的10倍。每个操作系统所使用的文件系统分别是:linux – ext2, solaris – ufs, windows 2000 – ntfs, freebsd – ufs。其它的文件系统无疑将会产生不同的性能结果。如果您的软件应用程序严重依赖于磁盘,我建议您使用linux或windows或者其它运行于freebsd或solaris上的替代文件系统。
应用程序体系结构测试
最后,我们不同的网络应用程序体系结构在每一操作系统上的表现进行了测试。我们写了一个简单的c++服务程序,它对每一个连接请求都给出一个”450 too busy”的响应消息。我们程序测试的三个体系结构是:(1)基于进程的体系结构,对每个连接都产生一个新的进程予以处理;(2)多线程体系结构,为每个进程分配一个线程;(3)异步体系结构,所有连接都使用非阻塞的tcp/ip予以应答。一个独立的c++程序运行在另外一台计算机(使用linux操作系统)上,它试图以最快的速度连接我们的服务器,缓慢增加同时的连接负载并且计算成功接收的响应信息。对于本文来说,这么多的测试结果图(12个)显然太多了,因此我们绘制了每个任务体系结构的平均值以便显示大体上的性能差异。
图3 每种网络结构的平均吞吐量
图3显示了每种类型的任务体系结构在所有操作系统平台上的平均性能。虽然不同平台上性能的差异是非常明显的,但其显著性与体系结构的选择所造成的显著性差异还是相去甚远。最慢的网络应用体系结构是基于进程的结构,它对连接的处理能力只有异步方法的5%。在1000个同时连接的情况下,同基于线程的方法相比,异步方法的负载能力要高出35%。趋势线显示,随着负载的增长,多线程方法与异步方法之间的性能差异将拉大。
为高性能应用进行内核调整
在缺省配置中,我们测试的unix风格的操作系统并不支持多线程和异步程序所需要的如此大量的同时tcp/ip连接。这种限制严重地限制了应用程序的性能,甚至错误地规劝系统管理员不要采用这些高性能体系结构。值得庆幸的是,这些限制可以容易地通过内核调整来克服。在unix上,每个tcp/ip连接使用一个文件描述符,所以您必需提高操作系统上可以提供的文件描述符的总量,同时也要提高每个进程允许使用的描述符的最大值。所有的unix风格的操作系统都有一个ulimit的shell命令(sh和bash),这个命令可以让使用它进行了适当的内核调整之后的shell上运行的命令可以打开更多的文件描述符。我们建议使用ulimit -n 8192。这里是我们推荐的内核调整过程:
在linux上: echo 65536 > /proc/sys/fs/file-max改变系统可提供的文件描述符的数量。
在freebsd上:将下面这些内容追加到 /etc/sysctl 文件的末尾(或者,您可以使用 sysctl -w 命令来填加这些内容):
kern.maxfiles=65536
kern.maxfilesperproc=32768
在solaris上:将下面的内容加入 /etc/system 文件后reboot:
set rlim_fd_max=0x8000
set rlim_fd_cur=0x8000
提要
我们的真实环境测试显示,操作系统最好与最差的性能差距达到了75%,linux拥有比排在第二位的solaris还要高出35%的卓越性能。更重要的是,异步应用程序比基于进程的应用程序平均快12倍,比多线程的应用程序快35%。如果磁盘i/o占用了应用程序运行时间的主要部分,那么在linux和windows 2000上的磁盘i/o任务的处理速度要比solaris快10倍以上,比freebsd也要快出6倍之多!
如果您正在评估一个网络应用软件,并且它的最终性能对您来说至关重要,那么软件的体系结构将是一个关键的评估准则(或者说,你应该选择多线程或异步结构)。
文章出处:http://www.sysadminmag.com/articles/2001/0107/0107a/0107a.htm
英文名:which os is fastest for high-performance network applications?