提高ASP性能的最佳选择


  ASP开发人员为了在他们的设计 名目中 获得更好的性能和可 扩大性而不停 奋力 。 厄运地是,有许多书籍和站点在这方面提供了很好的 提议 。然而这些 提议的 根底都是从ASP平台工作的 构造上所得出的 论断,对实际 获得的性能的 普及没有量的测量 。由于这些 提议需求更加复杂的编码过程并减低了编码的可读性,开发人员就只能在看不到实际运行 动机的状况下, 径自 衡量为了 普及他们ASP 利用程序的性能是不是值得付出这些代价 。

  本文分为两大 部分,我将介绍一些性能测试 后果,协助开发人员来确定某一特定 行动是不是不只对 将来的 名目来说是值得的,而且 可以对原来的 名目进行更新 。在第一 部分我将回忆一些ASP开发的 根底性问题 。在第二 部分,将 波及一些最优化ADO函数,并将它们的 后果与调用VB COM对象执行 雷同ADO函数的ASP页面进行 比较 。这些 后果很让人开眼界,甚至有些时候是很令人吃惊的 。

  在本文中,我们将 答复以下问题:

  * 将ASP生成的内容写入响应流中最有效的 步骤是什么?

  * 是不是应该开启缓冲器?

  * 是不是应该考量向ASP代码中添加 诠释?

  * 是不是应该为页面明确地设置默许语言?

  * 假如不需求,是不是应该关闭Session 状态?

  * 是不是应该把脚本逻辑放在子程序和函数区中?

  * 使用包括文件有什么影响?

  * 执行 舛误 解决时会施加什么样的负载?

  * 设置一个上下文 解决是不是对性能有影响?

 全部测试都是用Microsoft的Web 利用程序重点工具(WAST)来进行的,这是一个免费的工具, 可以在这里找到 。我用WAST 缔造了一个 方便的test 脚本, 反复调用下面所 形容的ASP页面测试(每个超过70,000次) 。 反响的 工夫基于 均匀最终字节总 工夫(TTLB), 也便是从最初 申请的 工夫到工具从服务器 接纳最终一位数据的 工夫 。我们的测试服务器是一个Pentium 166,内存为196MB,客户机为Pentium 450,内存为256MB 。你 兴许会想这些机器的性能并不算很高级,然而不要忘了,我们并不是要测试服务器的容量,我们只不过要测试服务器每次 解决一个页面所用的 工夫 。测试期间这些机器不做其它工作 。WAST 测试脚本、测试报告以及全部的ASP测试页面都包括在ZIP文件中,你 可以自己进行回忆和测试 。

  将ASP生成的内容写入响应流中最有效的 步骤是什么?

   使用ASP的一个最主要缘由是在服务器上生成动态内容 。所以很显而易见,我们测试的起点是确定将动态内容发送到响应流中的最 合适的 模式 。在多种 取舍中,有两个是最 根本的:一是 使用内联ASP标记,另一个是 使用Response.Write 语句 。

  为测试这些 取舍,我们 缔造了一个 方便的ASP页面,其中定义了一些变量, 而后将它们的值插入表格中 。 固然这个页面很 方便也不是很有用,但它同意我们 拆散并测试一些 径自的问题 。

   使用ASP内联标记

  第一个测试包括 使用内联ASP标记< %= x % >,其中x是一个已赋值的变量 。到当前为止,这个 步骤是最方便执行的,而且它使页面的HTML 部分 维持一种易于阅读和 保护的 格局 。

< % OPTION EXPLICIT

  Dim FirstName

  Dim LastName

  Dim MiddleInitial

  Dim Address

  Dim City

  Dim State

  Dim PhoneNumber

  Dim FaxNumber

  Dim EMail

  Dim BirthDate

  FirstName = "John"

  MiddleInitial = "Q"

  LastName = "Public"

  Address = "100 Main Street"

  City = "New York"

  State = "NY"

  PhoneNumber = "1-212-555-1234"

  FaxNumber = "1-212-555-1234"

  EMail = "john@public.com"

  BirthDate = "1/1/1950"

  % >

  < HTML >

  < HEAD >

  < TITLE >Response Test< / TITLE >

  < /HEAD >

  < BODY >

  < H1 >Response Test< /H1 >

  < TABLE >

  < tr >< td >< b >First Name:< /b >< /td >< td >< %= FirstName % >< /td >< /tr >

  < tr >< td >< b >Middle Initial:< /b >< /td >< td >< %= MiddleInitial % >< /td >< /tr >

  < tr >< td >< b >Last Name:< /b >< /td >< td >< %= LastName % >< /td >< /tr >

  < tr >< td >< b >Address:< /b >< /td >< td >< %= Address % >< /td >< /tr >

  < tr >< td >< b >City:< /b >< /td >< td >< %= City % >< /td >< /tr >

  < tr >< td >< b >State:< /b >< /td >< td >< %= State % >< /td >< /tr >

  < tr >< td >< b >Phone Number:< /b >< /td >< td >< %= PhoneNumber % >< /td >< /tr >

  < tr >< td >< b >Fax Number:< /b >< /td >< td >< %= FaxNumber % >< /td >< /tr >

  < tr >< td >< b >EMail:< /b >< /td >< td >< %= EMail % >< /td >< /tr >

  < tr >< td >< b >Birth Date:< /b >< /td >< td >< %= BirthDate % >< /td >< /tr >

  < /TABLE >

  < /BODY >

  < /HTML >

  /app1/response1.asp的 完全代码

  以往的最佳( 反响速度) = 8.28 msec/page

  在HTML的每一行 使用Response.Write 语句

  许多 比较好的学习文档 提议幸免 使用前面的那种 步骤 。其主要理由是,在输出页面和 解决页面施加 反响 工夫的过程中,假如web 服务器只能在发送纯HTML和 解决脚本中间进行转换,就会 产生一种被称为上下文转换的问题 。大 部分程序员一听到这里,他们的第一 反响便是将原始的HTML的每一行都包装在Response.Write函数中 。

  …

  Response.Write("< html >")

  Response.Write("< head >")

  Response.Write(" < title >Response Test< /title >")

  Response.Write("< /head >")

  Response.Write("< body >")

  Response.Write("< h1 >Response Test< /h1 >")

  Response.Write("< table >")

  Response.Write("< tr >< td >< b >First Name:< /b >< /td >< td >" & FirstName & "< /td >< /tr >")

  Response.Write("< tr >< td >< b >Middle Initial:< /b >< /td >< td >" & MiddleInitial & "< /td >< /tr >")

  … <

  /app1/response2.asp的片段

  以往的最佳( 反响速度) = 8.28 msec/page

   反响 工夫 = 8.08 msec/page

  差= -0.20 msec (削减 2.4%)

  我们 可以看到, 使用这种 步骤与 使用内联标记的 步骤相比在性能上 获得的收益十分小,这 兴许是由于页面给服务器装载了一大堆小的函数调用 。这种 步骤最大的缺陷是,由于现在HTML都嵌入脚本中,所以脚本代码变得更加 漫长,更加难以阅读和 保护 。

   使用包装函数

  当我们试图 使用Response.Write 语句这种 步骤时,最令人灰心的发现可能便是Response.Write 函数不能在每行的结尾处 搁置一个CRLF  。 因此,当你从阅读器中阅读源代码时, 原来 安排得十分好的HTML,现在成了没有 完毕的一行 。我想,你的下一个发现可能会更令你恐惧:在Response 对象中没有其姊妹函数Writeln  。所以,一个很显而易见的 反响便是为Response.Write 函数 缔造一个包装函数,以便给每一行都附加一个CRLF  。

 …

  writeCR("< tr >< td >< b >First Name:< /b >< /td >< td >" & FirstName & "< /td >< /tr >")

  …

  SUB writeCR(str)

  Response.Write(str & vbCRLF)

  END SUB

  /app1/response4.asp的片段

  以往的最佳( 反响速度)= 8.08 msec/page

   反响 工夫= 10.11 msec/page

  差 = +2.03 msec (添加 25.1%)

  固然,由于这种 步骤有效地使函数调用次数加倍,其对性能的影响也很显而易见, 因此要不惜 所有代价幸免 。 存在 奚落 象征的是CRLF也向 反响流中为每行添加了2个字节,而这是阅读器不需求出现到页面上的 。 格局化良好的HTML所做的 所有便是让你的竞争者更方便阅读你的HTML源代码并 了解你的设计 。

  将延续的Response.Write 衔接到一个 径自语句中

  不考量我们前面用包装函数进行的测试,下一个合乎逻辑的步骤便是从 径自的Response.Write 语句中提 存入全部的字符串,将它们衔接到一个 径自语句中,这样就削减了函数调用的次数,极大地 普及了页面的性能 。

  …

  Response.Write("< html >" & _

  "< head >" & _

  "< title >Response Test< /title >" & _

  "< /head >" & _

  "< body >" & _

  "< h1 >Response Test< /h1 >" & _

  "< table >" & _

  "< tr >< td >< b >First Name:< /b >< /td >< td >" & FirstName & "< /td >< /tr >" & _

  …

  "< tr >< td >< b >Birth Date:< /b >< /td >< td >" & BirthDate & "< /td >< /tr >" & _

  "< /table >" & _

  "< /body >" & _

  "< /html >")

  /app1/response3.asp的片段

  以往的最佳( 反响速度)= 8.08 msec/page

   反响 工夫 = 7.05 msec/page

  差 = -1.03 msec (削减12.7%)

  当前,这是最优化的配置 。

  将延续的Response.Write 衔接到一个 径自语句中,在每行结尾处添加一个CRLF

  考量到那些要求他们的源代码从阅读器中看要很 单纯的人,我用vbCRLF 常量在前面测试中每行的结尾处插入了一些回车, 而后再一次运行 。  

  …

  Response.Write("< html >" & vbCRLF & _

  "< head >" & vbCRLF & _

  " < title >Response Test< /title >" & vbCRLF & _

  "< /head >" & vbCRLF & _

  …

  /app1/response5.asp的片段

  前面的最佳( 反响速度)= 7.05 msec/page

   反响 工夫= 7.63 msec/page

  差 = +0.58 msec (添加 8.5%)

  运行的 后果在性能上有丝毫减低,这 兴许是由于额外的串联和添加的字符量 。

  回忆和观测

  从前面有关ASP输出的测试中 可以得出一些 规定:

  * 幸免内联ASP的过多 使用 。

  * 总是将延续Response.Write 语句衔接进一个 径自语句内 。

  * 永远不要在Response.Write 四周 使用包装函数来附加CRLF 。

  * 假如必须 格局化HTML输出,直接在Response.Write 语句内附加CRLF 。

是不是应该开启缓冲器?

  通过脚本程序启动缓冲器

  在ASP脚本的顶部包括Response.Buffer=True ,IIS就会将页面的内容缓存 。

  < % OPTION EXPLICIT

  Response.Buffer = true

  Dim FirstName

  …

  /app1/buffer__1.asp的片段

  以往的最佳( 反响 工夫)= 7.05 msec/page

   反响 工夫 = 6.08 msec/page

  差= -0.97 msec (减低13.7%)

  性能得到了极大 普及 。然而等等,还能有更好的 。

  通过服务器配置启动缓冲器

   固然在IIS 5.0中缓冲器是被默许启动的,然而在IIS 4.0中还必须手动来启动它 。这时要找到站点的Properties 对话框,在那里,从Home Directory 标签中 取舍配置按钮 。 而后在"App options"下 取舍"enable buffering"  。关于这个测试,Response.Buffer 语句从脚本中被移走了 。

  以往的最佳= 7.05 msec/page

   反响 工夫 = 5.57 msec/page

  差= -1.48 msec (减低 21.0%)

  当前,这是我们所得到的最快 反响了,比我们以往最好状况下的 反响 工夫还要减低21% 。从现在开始,我们以后的测试都要把这个 反响 工夫作为基准值 。

  回忆及观测

  缓冲器是 普及性能的好 步骤,所以把缓冲器设置成服务器的默许值很有必要 。假如由于某些缘由,页面不能正确地使缓冲器运行, 惟独求Response.Buffer=False 命令即可 。缓冲器的一个缺陷是在整个页面 解决完之前,消费者从服务器看不到任何东西 。 因此,在复杂页面的 解决期间,偶而调用一次Response.Flush 来更新消费者是个好 主张 。

  现在在我们的 规定中又添加了一条:总是通过服务器设置开启缓冲器 。

是不是应该考量向ASP代码中添加 诠释?

  大 部分HTML开发人员都晓得包括HTML 诠释不是个好 主张,首先会添加传输数据的规模,其次它们只不过向别的开发人员提供有关你页面组织的信息 。然而ASP页面上的 诠释又如何呢?它们 向来不离开服务器,但也 确切要添加页面的规模, 因此必须用ASP进行分解 。

  在这次的测试中,我们添加20条 诠释,每条有80个字符,总共有1600个字符 。

  < % OPTION EXPLICIT

  '-------------------------------------------------------------------------------

  … 20 lines …

  '-------------------------------------------------------------------------------

    Dim FirstName

  …

  /app2/comment_1.asp片段

  基准= 5.57 msec/page

   反响 工夫= 5.58 msec/page

  差 = +0.01 msec (添加 0.1%)

  测试的 后果是惊人的 。 固然 诠释 几乎相当于文件 本身的两倍,然而它们的存在并没有给 反响 工夫带来很大的影响 。所以说我们 可以遵照以下 规定:

   惟独 使用适度,ASP 诠释对性能的影响很小或 根本没有影响 。

是不是应该为页面明确地设置默许语言?

  IIS 解决VBScript是默许的设置,然而我看到,在大多数例子中还是用< %@LANGUAGE=VBSCRIPT% >申明将语言明确地设置为VBScript  。我们的下一个测试将 测验这个申明的存在对性能有什么影响 。

  < %@ LANGUAGE=VBSCRIPT % >

  < % OPTION EXPLICIT

  Dim FirstName

  …

  /app2/language1.asp片段 。

  基准值= 5.57 msec/page

   反响 工夫= 5.64 msec/page

  差= +0.07 msec (添加1.2%)

   可以看到,包括了语言的申明对性能有一个轻微的影响 。 因此:

  * 设置服务器的默许语言配置以与站点上 使用的语言相匹配 。

  * 除非你 使用非默许语言,不要设置语言申明 。

   假如不需求,是不是应该关闭Session 状态?

  幸免 使用IIS的Session上下文有许多理由,那些已经 可以独立成为一篇文章 。我们现在试图 答复的问题是当页面不需求时,关闭Session上下文是不是对性能 普及有所协助 。从 实际上讲应该是 确定的,由于这样一来就不需求用页面例示Session上下文了 。

  同缓冲器一样,Session状态也有两种配置 步骤:通过脚本和通过服务器设置 。

  通过脚本关闭Session上下文

  关于这个测试,要关闭页面中的Session上下文,我添加一个Session状态申明 。

  < %@ ENABLESESSIONSTATE = FALSE % >

  < % OPTION EXPLICIT

  Dim FirstName

  …

  /app2/session_1.asp片段 。

  基准值= 5.57 msec/page

   反响 工夫= 5.46 msec/page

  差= -0.11 msec (减低2.0%)

  只通过这样一个小小的 奋力就得到了不错的 普及 。现在看看第二 部分 。

  通过服务器配置关闭Session 上下文

  要在服务器上关闭Session 上下文,请到站点的Properties 对话框 。在Home Directory 标签上 取舍Configuration 按钮 。 而后在"App options"下 取缔"enable session state" 的 取舍 。我们在没有ENABLESESSIONSTATE 申明的状况下运行测试 。

  基准值 = 5.57 msec/page

   反响 工夫= 5.14 msec/page

  差= -0.43 msec (减低7.7%)

  这是性能的又一个卓著 普及 。所以,我们的 规定应是:在不需求的状况下,总是在页面或 利用程序的水平上关闭Session状态 。

   使用Option Explicit 会使性能有 本质转变吗?

  在一个ASP页面的顶部设置Option Explicit 以要求全部的变量在 使用之前都要在页面上进行申明 。这有两个缘由 。首先 利用程序 可以更快地 解决变量的存取 。其次,这样 可以 预防我们无意中错用变量的名字 。在这个测试中我们移走Option Explicit 引用和变量的Dim 申明 。

  基准值 = 5.57 msec/page

   反响 工夫= 6.12 msec/page

  差 = +0.55 msec (9.8% 添加)、

   只管有一些代码行从页面中去掉了, 反响 工夫却依旧添加了 。所以 只管 使用Option explicit 有时候费 工夫,然而在性能上却有很卓著的 动机 。 因此我们又 可以添加一条 规定:在VBScript中总是 使用Option explicit 。

是不是应该把脚本逻辑放在子程序和函数区?

  用函数和子程序来组织和治理代码是一个很好的 步骤,特殊是当一个代码区在页面中 频繁 使用的状况 。缺陷是要在系统上添加一个做 雷同工作的额外函数调用 。子程序和函数的另一个问题是变量的 规模 。从 实际上说,在一个函数区内指定变量更有效 。现在我们看看这两个方面如何 产生作用 。

  将Response.Write 语句移入子程序

  这个测试只不过将Response.Write 语句移入一个子程序区内 。

  …

  CALL writeTable()

  SUB writeTable()

  Response.Write("< html >" & _

    "< head >" & _

  …

    "< tr >< td >< b >EMail:< /b >< /td >< td >" & EMail & "< /td >< /tr >" & _

  "< tr >< td >< b >Birth Date:< /b >< /td >< td >" & BirthDate & "< /td >< /tr >" & _

  "< /table >" & _

  "< /body >" & _

    "< /html >")

  END SUB

  /app2/function1.asp片段

  基准值= 5.57 msec/page

   反响 工夫= 6.02 msec/page

  差 = +0.45 msec (8.1% 添加)

  同 意料中一样,子程序调用给页面带来了额外的 累赘 。

  将全部脚本移入子程序中

  在这个测试中,Response.write 语句与变量申明都移入一个子程序区中 。

  < % OPTION EXPLICIT

  CALL writeTable()

  SUB writeTable()

  Dim FirstName

  …

    Dim BirthDate

  FirstName = "John"

  …

  BirthDate = "1/1/1950"

  Response.Write("< html >" & _

  "< head >" & _

  " < title >Response Test< /title >" & _

  "< /head >" & _

  "< body >" & _

  "< h1 >Response Test< /h1 >" & _

  "< table >" & _

  "< tr >< td >< b >First Name:< /b >< /td >< td >" & FirstName & "< /td >< /tr >" & _

    …

  "< tr >< td >< b >Birth Date:< /b >< /td >< td >" & BirthDate & "< /td >< /tr >" & _

  "< /table >" & _

  "< /body >" & _

  "< /html >")

  END SUB

  /app2/function2.asp片段

  基准值= 5.57 msec/page

   反响 工夫= 5.22 msec/page

  差 = -0.35 msec (6.3% 减低)

  十分 乏味! 只管将变量移到函数 规模内添加了额外的函数调用,但实际上却 普及了性能 。我们又 可以添加以下 规定:

  * 在一个页面上,假如代码要 使用一次以上,就将代码封入函数区 。

  * 适当时候,将变量申明移到函数 规模内 。

使用包括文件有什么影响?

  ASP编程的一个主要 性能便是包括来自其它页面的代码 。通过这项 性能,程序员 可以在多个页面上共享函数,使代码更易于 保护 。缺陷在于服务器必须从多个 起源组装页面 。以下是 使用Include文件的两个测试 。

   使用内联代码的Include 文件

  在这个测试中,有一小段代码被移到一个Include 文件中:

  < % OPTION EXPLICIT

  Dim FirstName

    …

  Dim BirthDate

  FirstName = "John"

  …

  BirthDate = "1/1/1950"

  % >

  < !-- #include file="inc1.asp" -- >

  /app2/include_1.asp片段

  基准值 = 5.57 msec/page

   反响 工夫= 5.93 msec/page

  差 = +0.36 msec (6.5% 添加)

  这不奇怪 。 使用Include 文件 构成了负载 。

  在函数区 使用Include 文件

  在这里,代码都包装在一个Include 文件中的子程序里 。Include 引用是在页面顶部进行的,在ASP脚本的适当位置调用子程序 。

  < % OPTION EXPLICIT

  Dim FirstName

  …

  Dim BirthDate

  FirstName = "John"

  …

  BirthDate = "1/1/1950"

  CALL writeTable()

  % >

  < !-- #include file="inc2.asp" -- >

  /app2/include_2.asp片段

  基准值 = 5.57 msec/page

   反响 工夫= 6.08 msec/page

  差 =+0.51 msec (9.2% 添加)

  这对性能造成的影响比functions调用还大 。 因此:惟独当代码在页面中间共享时才 使用Include 文件 。

执行 舛误 解决时会 构成多大的负载?

  关于全部真正的 利用程序来说, 舛误 解决都是必要的 。这个测试中,通过调用On Error Resume Next函数来调用 舛误句柄 。

  < % OPTION EXPLICIT

  On Error Resume Next

  Dim FirstName

  …

  /app2/error_1.asp片段

  基准值 = 5.57 msec/page

   反响 工夫= 5.67 msec/page

  差= 0.10 msec (1.8% 添加)

  你 可以看到, 舛误句柄带来了代价 。我们 可以提出以下 提议:惟独在会 产生超出测试或操纵 威力之外的状况时才 使用 舛误句柄 。一个最 根本的例子便是 使用存取其它资源,如ADO或FileSystem 对象的COM对象 。

设置一个上下文 解决是不是对性能有影响?

  当 舛误 产生时,在页面上设置一个上下文 解决同意脚本进行反转操作 。这是通过在页面上 使用 解决申明来设置的 。

  < %@ TRANSACTION = REQUIRED % >

  < % OPTION EXPLICIT

  Dim FirstName

  …

  /app2/transact1.asp片段

  基准值 = 5.57 msec/page

   反响 工夫= 13.39 msec/page

  差 = +7.82 msec (140.4% 添加)

  啊!这 实在最 存在戏剧性的 后果 。所以请 留神以下 规定:惟独当两个或更多操作被作为一个单元执行时,才 使用 解决上下文 。

论断

  本文第一 部分的主要之处在于许多小 事件的累积 。为了强调这个问题,我设置了最终一个测试,在其中进行了我们以往曾经测试过的看来无所谓但实际上有坏影响的全部操作 。我包括了许多Response.Write 申明、关闭了缓冲器、设置了默许语言、去掉了Option Explicit 引用并初始化了 舛误句柄 。

  < %@ LANGUAGE=VBSCRIPT % >

  < %

  On Error Resume Next

  FirstName = "John"

  …

  BirthDate = "1/1/1950"

  Response.Write("< html >")

  Response.Write("< head >")

  Response.Write(" < title >Response Test< /title >")

  Response.Write("< /head >")

  Response.Write("< body >")

  Response.Write("< h1 >Response Test< /h1 >")

  Response.Write("< table >")

  Response.Write("< tr >< td >< b >First Name:< /b >< /td >< td >" &_

  "FirstName & "< /td >< /tr >")

  …

  Response.Write("< tr >< td >< b >Birth Date:< /b >< /td >< td >" &_

  " BirthDate & "< /td >< /tr >")

  Response.Write("< /table >")

  Response.Write("< /body >")

  Response.Write("< /html >")

  % >

  /app2/final_1.asp片段

  基准值 = 5.57 msec/page

   反响 工夫 = 8.85 msec/page

  差 = +3.28 msec (58.9% 添加)

  听起来可能很显而易见,然而 了解更主要,那便是我们 搁置在页面上的代码会对性能有影响 。页面上的小 变迁有时会大大地添加 反响 工夫 。

规定归纳

  * 幸免内联ASP的过多 使用 。

  * 总是将延续Response.Write 语句衔接进一个 径自语句内 。

  * 永远不要在Response.Write 四周 使用包装函数以附加CRLF 。

  * 假如必须 格局化HTML输出,直接在Response.Write 语句内附加CRLF 。

  * 总是通过服务器设置开启缓冲器 。

  * 惟独 使用适度,ASP 诠释对性能的影响很小或 根本没有影响 。

  * 设置服务器的默许语言配置以与站点上 使用的语言相匹配 。

  * 除非你 使用非默许语言,不要设置语言申明 。

  * 在VBScript中总是 使用Option explicit  。

  * 在不需求的状况下,总是在页面或 利用程序的水平上关闭Session状态 。

  * 惟独当代码在页面中间共享时才 使用Include 文件 。

  * 在一个页面上,假如代码要 使用一次以上,就将代码封入函数区 。

  * 适当时候,将变量申明移到函数 规模内 。

  * 惟独会 产生超出测试或操纵 威力之外的状况时才 使用 舛误句柄 。

  * 惟独当两个或更多操作被作为一个单元执行时,才 使用上下文 解决 。

  现在回忆一下,有许多问题 可以作为 广泛性的方针:

  * 幸免冗余--不要设置那些默许状态下已经设置的属性 。

  * 制约函数调用的次数 。

  * 缩短代码的 规模 。