Username: Password:

扼杀IIS服务器性能的十条规则
来源:作者: 发布时间:2007-11-09 05:14:02

下面的每一条戒律都将有效地影响代码的性能和可伸缩性。换句话说,尽可能不要照着戒律去做!下面,我将解释怎样破坏他们以便提高性能和可伸缩性。

1、应该分配和释放多个对象

您应该尽量避免过量分配内存,因为内存分配可能是代价高昂的。释放内存块可能更昂贵,因为大多数分配算符总是企图连接临近的已释放的内存块成为更大的块。直到Windows NT? 4.0 service pack 4.0,在多线程处理中,系统堆通常都运行得很糟。堆被一个全局锁保护,并且在多处理器系统上是不可扩展的。

2.不应该考虑使用处理器高速缓存

大多数人都知道由虚拟内存子系统导致的hard 页错误代价很高,最好避免。但是许多人认为其他内存访问方法没有什么区分。自从80486以后,这一观点就不对了。现代的CPUs比RAM要快得多,RAM至少需要两级内存缓存 ,高速L1 缓存能保存8KB数据和8KB指令,而较慢的L2 缓存能保存几百KB的数据和代码,这些数据和代码混合在一起。L1 缓存中内存区域的一个引用需要一个时钟周期,L2 缓存的引用需要4到7个时钟周期,而主内存的引用需要许多个处理器时钟周期。后一数字不久将会超过100个时钟周期。在许多方面,缓存像一个小型的,高速的,虚拟内存系统。

至于和缓存有关的基本内存单元不是字节而是缓存列。Pentium 缓存列有32个字节宽。Alpha 缓存列有64个字节宽。这意味着在L1 缓存中只有512个slot给代码和数据。假如多个数据一起使用(时间位置)而并不存储在一起(空间位置),性能会很差。数组的空间位置很好,而相互连接的列表和其他基于指针的数据结构的位置往往很差。

把数据打包到同一个缓存列中通常会有利于提高性能,但是他也会破坏多处理器系统的性能。内存子系统很难协调处理器间的缓存。假如一个被任何处理器使用的只读数据,和一个由一个处理器使用并频繁更新的数据共享一个缓存 列,那么缓存将会花费很长时间更新这个缓存列的拷贝。这个Ping-Pong高速游戏通常被称为"缓存 sloshing"。假如只读数据在一个不同的缓存 列中,就能够避免sloshing。

对代码进行空间优化比进行速度优化效率更高。代码越少,代码所占的页也越少,这样需要的运行配置和产生的页错误也会更少,同时占据的缓存 列也会更少。然而,某些核心函数应该进行速度优化。能够利用profiler去识别这些函数。

3.决不要缓存频繁使用的数据。

软件缓存能够被各种应用程式使用。当一个计算代价很高时,您会保存结果的一个拷贝。这是个典型的时空折中方法:牺牲一些存储空间以节省时间。假如做得好,这种方法可能很有效。

您必须正确地进行缓存。假如缓存了错误数据,就会浪费存储空间。假如缓存得太多,其他操作能够使用的内存将会很少。假如缓存得太少,效率又会很低,因为您必须重新计算被缓存 遗漏的数据。假如将时间敏感数据缓存得时间过长,这些数据将会过时。一般,服务器更关心的是速度而不是空间,所以他们要比桌面系统进行更多的缓存。一定要定期去除不用的缓存,否则将会有运行配置问题。

4.应该创建多个线程,越多越好。

调整服务器中起作用的线程数目是很重要的。假如线程是I/O-bound的,将会花费很多时间用来等待I/O的完成-一个被阻塞的线程就是个不做任何有用工作的线程。加入额外的线程能够增加通量,但是加入过多的线程将会降低服务器的性能,因为上下文交换将会成为一个重大的overhead。上下文交换速度应该低的原因有三个:上下文交换是单纯的overhead,对应用程式的工作没有任何益处;上下文交换用尽了宝贵的时钟周期;最糟的是,上下文交换将处理器的缓存填满了没用的数据,替换这些数据是代价高昂的。

有很多事情是依靠您的线程化结构的。每个客户端一个线程是绝对不合适的。因为对于大量用户端,他的扩展性不好。上下文交换变得难以忍受,Windows NT用尽了资源。线程池模型会工作得更好,在这种方法中一个工人线程池将处理一条请求列,因为Windows 2000提供了相应的APIs,如QueueUserWorkItem。

5.应该对数据结构使用全局锁

使数据线程安全的最简单方法是把他套上一把大锁。为简单起见,任何的东西都用同一把锁。这种方法会有一个问题:序列化。为了得到锁,每一个要处理数据的线程都必须排队等候。假如线程被一把锁阻塞,他没有在做任何有用的事。当服务器的负载较轻时,这个问题并不常见,因为一次可能只有一个线程需要锁。在负载很重的情况下,对锁的激烈争夺可能就会成为一个大问题。

设想在多车道高速公路上发生了一个意外事故,这条高速公路上的任何车辆都被转向一条狭窄的道路。假如车辆很少,这一转换对交通流的速率的影响能够忽略。假如车辆很多,当车辆慢慢并入那条单通道时,交通阻塞会延伸几英里。

有几种技术能够减少锁竞争。

? 不要过分保护,也就是说,不是很必要不要锁住数据。只有需要时才去持有锁,而且时间不要过长。不要在大段代码周围或频繁执行的代码中没必要地使用锁,这一点很重要。

? 对数据进行分割,使他能够用一套单独的锁保护。例如,一个符号表能够按标识符的第一个字母分割,这样在修改名字以Q开头的符号的值时,就不会去读名字以H开头的符号的值。

? 使用APIs的Interlocked 系列(InterlockedIncrement,InterlockedCompareExchangePointer等)自动修改数据而无需锁。

? 当数据不是经常被修改时能够使用多读者/单作者(multi-reader/single-writer)锁。您将获得更好的并发性,尽管锁操作的代价将更高并且您可能会冒饿死作者的危险。

? 在关键部分使用循环计数器。参见Windows NT 4.0 service pack 3中的SetCriticalSectionSpinCount API。

? 假如您不能得到锁,使用TryEnterCriticalSection并做一些其他的有用的工作。

高竞争导致serialization,serialization导致降低CPU的利用率,这促使用户加入更多的线程,结果事情变得更糟。

6.不必注意多处理器机器

您的代码在多处理器系统上比在单处理器系统上运行得还要糟,这可能是件令人恶心的事。一个很自然的想法是,在一个N维系统上运行N次会更好。性能很差的原因是竞争:锁竞争,总线竞争,和/或缓存列竞争。处理器都在是争夺共享资源的任何权,而不是做更多的工作。

假如您一定要编写多线程应用程式的话,您应该在多处理器盒上对您的应用程式进行强度测试和性能测试。单处理器系统通过时间分片地执行线程而提供一个并发性的假象。多处理器盒具备真正的并发性,竞争环境和竞争更容易发生。

7.应该始终使用模块化调用;他们很有趣。

利用同步模块化调用来执行I/O操作对大多数桌面应用程式来说是合适的。但是,他们不是使用服务器上的CPU(s)的好方法。I/O操作要花费上百万个时钟周期来完成,这些时钟周期本来能够被更好地利用。利用异步I/O您能得到显著提高的用户请求率和I/O通量,但是增加了额外的复杂性。

假如您有需要花费很长时间的模块化调用或I/O操作,您应该考调拨多少资源给他们。您想使用任何的线程还是有个限制?一般地,使用有限的几个线程要好些。构建一个小的线程池和队列,利用队列来安排线程的工作完成模块化调用。这样,其他线程就能够拾取和处理您的非模块化的请求。

8.不要进行测量

当您能够测量您所谈论的事情并用数字表达他时,这就表示您对他有了一定的了解;但是假如您不能用数字表达时,您的知识是贫瘠的不能令人满意的;这可能是知识的开始,但这时您简直不可能将您的思想提高到科学的水平。

- Lord Kelvin (William Thomson)

假如不测量您就不能了解应用程式的特性。您在黑暗中摸索,一半是靠猜测。假如不识别性能问题,您就不能做任何改进或做出工作量计划。

测量包括黑匣子测量和profiling。黑匣子测量的意思是收集由性能计数器(内存使用,上下文交换,CPU利用等)和外部检测工具(通量,反映时间等)所显示的数据。为了profile您的代码,您编译代码的一个工具版,然后在各种条件下运行他,并收集关于执行时间和过程调用频率的统计数据。

测量假如不用于分析的话就一点用都没有。测量将不但告诉您有问题,而且甚至能帮助您找到问题发生在哪,但他不能告诉您为什么会有问题。对问题进行分析以便您能正确地改正他们。要从根本上解决问题而不是停留在表面现象。

当您进行改变后,要重新测量。您要知道您的改变是否有效。改变也可能会暴露其他性能问题,测量-分析-改正-再测量的循环就会重新开始。您也必须要有规律地进行测量,以便发现性能衰退问题。

9.应该使用单一用户,单一请求的测试方法。

书写ASP和ISAPI应用程式的一个通病是只用一个浏览器去测试应用程式。当他们在Internet上应用他们的程式时,他们才发现他们的应用程式不能处理高负载,并且通量和反应时间另人可怜。

用一个浏览器测试是必要的但是不够的。假如浏览器反应得不够快,您就知道您有麻烦了。但即使他在使用一个浏览器时很快,您也不知道他处理负载的能力怎样。假如十几个用户同时请求会发生什么事?一百个呢?您的应用程式能容忍什么样的通量?他能提供什么样的反应时间?在轻载时这些数字会怎样?中等负载呢?重载呢?在多处理器机器上您的应用程式会怎样?对您的应用程式进行强度测试,这对于找出bugs发现性能问题来说是基本的。

类似的负载测试考虑适用于任何的服务器应用程式。

10.不应使用实际环境。

人们往往只在几个特定的,人工的环境(如下benchmarks)下调整应用程式。选择和实际情况相对应的各种情况,并为针对各种操作进行优化,这一点很重要。假如您不这样做,您的用户和评论家一定会这样做,并且他们将依此来评判您的应用程式的好坏。

喜欢本文,那就收藏到:

    Del.icio.us Google书签 Digg Live Bookmark Technorati Furl Yahoo书签 Facebook 百度搜藏 新浪ViVi 365Key网摘 天极网摘 和讯网摘 博拉网 POCO网摘 添加到饭否 QQ书签 Digbuzz我挖网
相关评论  我也要评论
还没有关于此文章的相关评论!
  • 昵称: (为空则显示guest)
  • 评论分数: ★ ★ ★★★ ★★★★ ★★★★★
  • 评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
  • 导航
    赞助商
    文章类别
    订阅