如何增强ASP程序性能(3)

使用这个指示的一个很好的解释是在框架结构中Session创建了一个有趣的问题。ASP确保在一个时刻只有一个来自Session的请求被执行,这就确保了如果浏览器为单个用户请求多个页面时,只有一个ASP请求在那时能够接受Session,如此就避免了存取Session对象时的多线程问题。很不幸,在框架结构中的所有页面将按照连续的顺序显示出来,一个接一个,而不是同时,所以用户为了看到整个框架必须要等很长时间。规则是:如果一定的框架页面没有使用Session,就一定要告诉ASP直接使用@EnableSessionState=False

  除了使用Session对象,还有许多其他管理会话状态的选择。对于小数量的状态(小于4KB),我们通常建议使用cookie、查询字符串变量以及表单隐藏域。对于象购物车一样的大数量数据,后台数据库是最合适的选择。

技巧7:将代码装入COM对象中

  如果要编写很多VBScript或者JScript,为了提个性能,可以将代码编写成COM对象并且编译使用。编译代码基本上比解释性代码运行快许多,编译组件对象可通过“early binding”存取其他COM对象,这比在脚本中调用组件要有效。

这么做有许多优点:


COM
对象有益于从商业规则中独立出表达式规则
COM
对象使代码重用变为可能
许多开发者发现用VBC++或者Visual J++编写程序,比ASP更容易调试
  COM对象也有缺点,包括初始开发时间和对不同编程技巧的需要。注意将少量ASP代码做成COM对象组件不会有好处,反而可能导致性能的损失,从而失去了编译代码的优势。怎样组合使用ASP脚本和COM对象达到最佳性能是一个测试的问题。我们注意到微软公司已经大规模在Windows 2000/IIS 5.0上提高了脚本与ADO的性能,由此,随着IIS5.0版本的引进,减少了编译代码的性能优势。

技巧8:使用Option Explicit

  要在ASP文件中使用Option Explicit定义,并且放置到ASP文件的头部,从而强迫开发者在使用前声明所有的变量。许多程序员都认为这在应用程序调试时非常有用,因为它避免了产生错误类型变量以及偶然创建新变量的可能。

也许更重要的是,声明的变量要大大快于非声明变量。

技巧9:拷贝经常使用的数据到脚本变量中

  在ASP中存取COM对象时,应该拷贝经常使用的对象数据到脚本变量中,这样就减少了对COM对象的方法调用。这些调用要比存取脚本变量相对来说费时费力。当存取CollectionDictionary对象时,使用这项技巧也减少了昂贵的查找操作。

  通常,如果要不止一次地存取对象数据,就应将数据放入脚本变量中,对象数据主要也就是Request变量(表单和查询字符串变量)。比如,站点要传递一个叫做UserID的查询字符串变量,假设它将在一个特殊页面被引用12次,那么不需要调用RequestUserID12次,只要在ASP页面的头部分配给UserID一个变量,然后在页面中使用它,这样做就节省了11COM方法的调用。

  实际中,存取COM属性或方法是很昂贵的,下面的例子展示了通用代码:

Foo.bar.blah.baz = Foo.bar.blah.qaz(1)
If Foo.bar.blah.zaq = Foo.bar.blah.abc Then ' ...

上面的代码执行后,发生以下事情:

1
、变量Foo被当作全局对象
2
、变量bar被当作Foo的一员
3
、变量blah被当作Foo.bar的一员
4
、变量qaz被当作Foo.bar.blah的一员
5
、调用Foo.bar.blah.quaz(1)
6
、再执行步骤13分解baz
7
、分解baz做为Foo.bar.blah的一员
8
、再执行步骤13分解zaq
9
、再执行步骤13一次分解abc

如上所示,这非常没有效率并且很慢。更快的方法是用VBScript编写代码,如下:

Set myobj = Foo.bar.blah ' do the resolution of blah ONCE
Myobj.baz = myobj.qaz(1)
If Myobj.zaq = Myobj.abc Then '...

如果使用VBScript 5.0或者更高版本,可以用With语句编写:


With Foo.bar.blah
.baz = .qaz(1)
If .zaq = .abc Then '...
...
End With


注意:这个技巧也可以应用在VB编程中。

技巧10:避免再定义数组

  争取不要再定义数组。考虑到性能问题,如果机器的物理内存大小不够,最好按最差情况或者最佳情况设置数组的初始尺寸,需要时再重新定义。

下面的代码展示了DimRedim的使用:

< %
Dim MyArray()
Redim MyArray(2)
MyArray(0) = "hello"
MyArray(1) = "good-bye"
MyArray(2) = "farewell"
...
' some other code where you end up needing more space happens, then ...
Redim Preserve MyArray(5)
MyArray(3) = "more stuff"
MyArray(4) = "even more stuff"
MyArray(5) = "yet more stuff"
% >

  简单地定义数组初始尺寸为合适的大小是非常好的,而不要用Redim加大数组。这么做也许浪费了一些内存(如果没有完全地使用空间),但是赢得了速度。

技巧11:使用Response Buffering

  通过打开“response buffering”可以缓冲一个值得输出的整个页面内容,这将最小化输出到浏览器的数据量,从而提高了整体性能。每一次输出都耗费许多,所以写得越少,效果越好。TCP/IP在发送少量大的数据包时,要比发送大量小的数据包工作效率高,因为它是慢速启动并不断发送的。

  有2种方法打开Response Buffering。首先,可以使用Internet Services Manager为整个应用程序打开response buffering,这是推荐的方式,而且在IIS4.0IIS5.0中,默认状态下,response buffering是打开的。其次,在每一页面上,可以在头部放置如下代码开打开response buffering

< % Response.Buffer = True % >

  这段代码必须在任何数据输出到浏览器前被执行(就是说,在任何html内容显示前和在任何cookie被设置前)。通常情况下,为整个应用程序打开response buffering是很好的方案,这么做后就不用在每个页面头部设置如上的代码。

  关于打开response buffering的一个通用问题是:用户必须要等待整个页面全部产生后,才能看到内容。对于一个长时间运行的页面来说,可以设置Response.Buffer=False关闭缓冲。然后,好的策略是利用Response.Flush方法,它将输出所有已被ASP描述的HTML内容到浏览器。比如,在描述了一个1000行表格的100行后,ASP就可以使用Response.Flush来强迫输出这100行的内容到浏览器,这时用户就可以看到前100行数据,同时其余的行数据正在准备生成。

  注意,关于上面的1000行表格输出的例子,对于一些浏览器器来说,除非遇到< /table >标记,它们不会输出表格的任何内容。如果这样,可以将表格分割成许多含有少量行的多个表格,然后在每一个表格产生后,调用Response.Flush输出。新版的Internet Explorer在整个表格下载后才显示内容,并且,如果定义了表格的列宽度,生成表格的速度将特别快。

关于打开response buffering的另外一个问题是:当生成非常大的页面时,将消耗非常大的服务器内存。

技巧12:批处理单行脚本和Response.Write命令

  VBScript语法< % = expression % >的意思是输出expression的数值。如果response buffering没有打开,每个这样的语句将按照许多小数据包的形式输出数据到浏览器,这将降低程序性能。因此,请使用下面的技巧:替换紧挨着的多个一行表达式调用为一个调用,用Response.Write名称输出。比如,在下面的例子中,对于每行每个字段的输出,只有一个写操作:

<table>
<% For Each fld in rs.Fields %>
<th><% = fld.Name %></th>
<%
Next
While Not rs.EOF
%>
<tr>
<% For Each fld in rs.Fields %>
<td><% = fld.Value %></td>
<% Next
</tr>
<% rs.MoveNext
Wend %>
</table>

下面是更有效率的代码,每行一个输出: