手机版

提高ASP的字符串处理性能

时间:2021-11-28 来源:互联网 编辑:宝哥软件园 浏览:

大多数活动服务器页面(ASP)应用程序使用字符串连接来创建呈现给用户的HTML数据。本文比较了几种创建这种HTML数据流的方法,在某些情况下,有些方法在性能上优于其他方法。本文假设您已经对ASP和Visual Basic编程有所了解。

目录

简介ASP设计字符串连接快速解决方案StringBuilder内置方法测试结果总结

简介

编写ASP页面时,开发人员实际创建了一个格式化的文本流,并通过ASP提供的Response对象写入Web客户端。创建这个文本流的方法有很多,你选择的方法会对Web应用的性能和可伸缩性产生很大的影响。很多时候,当我帮助客户优化其Web应用程序的性能时,我发现更有效的方法之一是改变创建HTML流的方式。本文将介绍几种常见的技术,并测试它们对简单ASP页面性能的影响。00-1010许多ASP开发人员遵循良好的软件工程原则,尽可能地模块化他们的代码。这种设计通常使用一些包含文件,其中包含通过格式化页面的特定不连续部分而生成的函数。这些函数的字符串输出(通常是HTML表格代码)可以通过各种组合创建一个完整的页面。一些开发人员改进了这种方法,将这些HTML函数移入Visual Basic COM组件,希望充分利用编译代码提供的额外性能。虽然这种设计方法很好,但是无论实际操作是在ASP包含文件还是Visual Basic COM组件中执行,用于创建组成这些不连续HTML代码组件的字符串的方法都会对网站的性能和可扩展性产生很大的影响。00-1010请看下面WriteHTML函数的代码片段。名为Data的参数只是一个字符串数组,其中包含一些要格式化为表结构的数据(例如,从数据库返回的数据)。函数write HTML(Data)Dim nRep for nRep=0到99 sHTML=sHTML vbcrlf _ ' TRTD '(nRep 1)'/TDTD ' _ Data(0,nRep ) '/TDTD' _ Data(1,Ep)'/tdtd' _ data (2,nrep)'/tdtd' _ data (3,nrep)'/tdtd' _ data (4,nrep)'/tdtd' _ data (5,nRep)'/TD/tr ' next write HTML=HTML end。包含在sHTML变量中的文本被返回给调用代码,然后使用响应被写入客户端。写.当然,这也可以表示为直接嵌入到不包含WriteHTML函数的页面中的类似代码。这段代码的问题在于ASP和Visual Basic使用的字符串数据类型(BSTR或Basic字符串)实际上不能改变长度。这意味着,每当字符串的长度改变时,内存中字符串的原始表示将被破坏,并且包含新字符串数据的新表示将被创建:这将增加分配和解除分配内存的操作。当然,ASP和Visual Basic已经为你解决了这个问题,所以实际成本不会马上显现出来。分配和释放内存需要基本的运行时代码来释放每个特殊的锁,因此需要大量的开销。当字符串变大并且有一大块内存需要快速连续地分配和释放时,这个问题就变得特别明显,就像在连接大字符串时发生的那样。虽然这个问题对单用户环境影响不大,但会导致服务器环境(例如Web服务器上运行的ASP应用)出现严重的性能和可扩展性问题。现在,让我们回到上面的代码片段:在这段代码中应该执行多少个字符串分配操作?答案是16。在这种情况下,操作符“”的每个应用程序都会导致变量sHTML所指向的字符串被销毁并重新创建。如前所述,字符串分配的开销非常高,并且随着字符串的增加而增加,因此我们可以改进上面的代码。

ASP 设计

有两种方法可以减轻字符串串联的影响。第一种方法是尽量减少要处理的字符串的大小,第二种方法是尽量减少字符串分配操作的次数。请参见下面显示的WriteHTML代码的修订版。函数write html(Data)Dim nRep for nRep=0到99 sHTML=sHTML(vbcrlf _ ' TRTD '(nRep 1)'/TDTD ' _ Data(0,nRep ) '/TDTD' _ Data(1,Ep)'/tdtd' _ data (2,nrep)'/tdtd' _ data (3,nrep)'/tdtd' _ data (4,nrep)'/tdtd' _ data (5,nRep)'/TD/tr ')next write html=sHTML实际上,这段代码只是在sHTML=sHTML之后的内容外放了圆括号。这实际上是通过更改优先级顺序来减少大多数字符串连接操作中处理的字符串大小。在最初的代码示例中,ASP编译器将查看等号右侧的表达式,并从左到右进行计算。因此,每次迭代需要16个连接操作,这是针对不断增长的sHTML的。在新版本中,我们提示编译器更改操作顺序。现在,它将从左到右,从内括号到外括号计算表达式。这种技术使得每次迭代包含15个连接操作,这些操作针对的是不会增长的较小字符串,只有一个操作针对的是增长的sHTML。图1显示了这种优化方法和标准连接方法在内存使用模式方面的比较。

图1:标准连接和括号连接之间的内存使用模式比较。在某些情况下,括号的使用会对性能和可伸缩性产生非常显著的影响,这将在后面进一步解释。

字符串连接

我们找到了一个快速解决字符串连接问题的方法。在大多数情况下,这种方法可以实现性能和投资之间的最佳平衡。但是,如果我们想进一步提高构建大字符串的性能,就需要采用第二种方法,即减少字符串分配操作的次数。为此,您需要使用StringBuilder。StringBuilder是一个类,它维护可配置的stringbuffer区域,管理插入到此缓冲区中的新文本片段,并且仅当文本长度超过stringbuffer区域的长度时才重新分配字符串。微软。NET框架提供了这样一个类(系统。Text.StringBuilder),建议在这种环境下的所有字符串连接操作中使用。在ASP和传统的Visual Basic环境下,我们无法访问这个类,所以需要自己创建。以下是使用Visual Basic 6.0创建的StringBuilder类的示例(为简洁起见,省略了错误处理代码)。选项Explicit '默认缓冲区初始大小和增长系数private const def _ initialsize只要=1000 private const def _ growth只要=1000 '缓冲区大小和增长Private m_nInitialSize As。LongPrivate m_nGrowth As Long '缓冲区和缓冲区计数器private m _ stext As string private m _ nsize As Long private m _ NPOs As Long private subclass _ initialize()。设置大小和增长的默认值m _ ninitialsize=def _ initialsize m _ ngrowth=def _ growth '初始化缓冲区InitBufferEnd Sub '设置公共sub init的初始大小和增长数量(byval initialsize为long)。byval growth as long)如果initialsize为0,则m _ ninitialsize=initialsize如果growth为0,则m _ ngrowth=growth end Sub ' initialize buffer Private Sub InitBuffer()。M _ size=-1 m _ npos=1 end sub '增加缓冲区私有sub增长(可选最小长度为长)。初始化缓冲区(如有必要)如果m _ nsize=-1,则m _ nsize=m _ ninitialsize m _ stext=space $(m _ ninitialsize)。否则“只要n growth=IIF(m _ ngrowth minimigrowth,m _ ngrowth,minirowth)m _ size=m _ size ngrowth m _ stext=m _ stextspace $(ngrowth)endi fend Sub就行了”将缓冲区大小调整为当前使用的大小Private Sub Shrink()。如果_ nsize m _ npos,则m _ nsize=m _ NPOs-1m _ stext=rtrim $(m _ stext)endi fend sub '添加单个文本字符串private sub append internal(byval text as string)。if(m_nPos Len(Text))m _ nSize Then Grow Len(Text)Mid $(m _ Stext,m _ NPOs,Len(Text))=Text m _ NPOs=m _ NPOs Len(Text)end sub '添加一些文本字符串public subappend(param array Text())dim narg只要narg=0到ubound (text)。追加内部CSTR(文本(narg))下一个nargend sub '返回当前字符串数据并调整缓冲区大小公共函数ToString()。如果m _ npos为0,则收缩为字符串=m _ stexteelse to string=' end ifend function '清除缓冲区并重新初始化公共子清除()。这个类使用的基本原理是在类级别使用变量(m_sText)作为stringbuffer区域,并使用Space$函数用空格字符填充这个缓冲区,以将其设置为特定的大小。如果要将更多文本与现有文本连接起来,请在检查缓冲区是否足够大以容纳新文本后,使用Mid$函数将文本插入正确的位置。ToString函数将返回当前存储在缓冲区中的文本,并调整缓冲区的大小以适应该文本的正确长度。

使用StringBuilder的ASP代码如下:函数write html(data)dim OSB dim nre pset OSB=server . createobject(' StringBuilder VB . StringBuilder ')。为nrep=0到99 OSB初始化缓冲区OSB.init15000,7500 .追加' trtd ',(nrep1),'/tdtd ',_ data (0,nrep),'/tdtd ',_ data (1,nrep),'/tdd '。_ Data(2,nRep),'/TDTD ',_ Data(3,nRep),'/TDTD ',_ Data(4,nRep),'/TDTD ',_ Data(5,nRep),/TD/tr ' next write html=OSB . tostring()设置OSB=nothing end函数。使用StringBuilder需要一定的开销,因为每次使用这个类时,都必须创建它的一个实例,在创建类的第一个实例时,必须加载包含这个类的DLL。对StringBuilder实例的额外方法调用也需要开销。使用括号“”方法时,StringBuilder的执行方式取决于许多因素,包括连接数、要构建的字符串的大小以及所选StringBuilder stringbuffer区域的初始化参数的性能。请注意,在大多数情况下,估计缓冲区中所需的空间比让它保持增长要好得多。

快捷的解决方案

ASP包含一个非常快速的创建HTML代码的方法,只需要调用Response。写几次。Write函数使用隐式优化的stringbuffer缓冲区,这可以提供出色的性能特征。修改后的WriteHTML代码如下:函数write html(data)dim nrep for nrep=0到99 Response . write ' trtd ' Response . write(nrep 1)Response . write '/TDTD ' Response。写数据(0,nRep)响应。写下“/TDTD”的回应。写数据(1,nRep)响应。写下“/TDTD”的回应。写数据(2,nRep)响应。写下“/TDTD”的回应。写入数据(3,nRep)响应。写下“/TDTD”的回应。写数据(4,Nrp)响应。写入“/tdtd”响应。writedata (5,nrep)响应。write'/td/tr' nextendfunction虽然这段代码很可能为我们提供了最好的性能和可伸缩性,但它在一定程度上破坏了包,因为函数内部的代码现在将直接写入响应流,所以调用代码失去了一定程度的控制。此外,由于该函数依赖于响应流,因此将该代码(例如,移动到COM组件中)变得更加困难。

StringBuilder

上面提到的四种方法通过一个简单的ASP页面(包含一个表,其中的数据由一个虚拟字符串数组提供)进行了测试。应用程序中心测试(ACT)用于在100兆字节/秒的网络中从单个客户端(Windows XP Professional,PIII-850兆赫兹,512兆内存)测试单个服务器(Windows 2000高级服务器,双PIII-1000兆赫兹,256兆内存)。ACT配置为使用5个线程模拟5个用户连接到网站时的负载。每个测试都包括20秒的预热时间和100秒的加载时间,在此期间会创建尽可能多的请求。通过更改主表循环中的重复次数,测试将针对不同数量的连接操作重复运行,如WriteHTML函数中的代码片段所示。运行的每个测试都使用上面提到的四种不同的方法来执行。00-1010以下一系列图表显示了各种方法对整个应用程序吞吐量和ASP页面响应时间的影响。通过这些图表,我们可以知道应用程序支持的请求数量,以及用户等待页面下载到浏览器所需的时间。表1:所用连接方法的缩写描述RESP内置响应的缩写描述。Write Method CAT标准连接(" ")Method PCAT带括号连接(" ")Method BLDRStringBuilder方法就模拟典型的ASP应用工作负载而言,这个测试与实际情况相差甚远。从表2可以明显看出,即使重复420次,这个页面也不是特别大。现在很多复杂的ASP页面在这些数字上都比较高,设置可能会超出这个测试范围的限制。

表2:测试示例的页面大小和连接数连接数页面大小(以字节为单位)152402、667304804、917457207、167609609、417751、20011、6671201、92018、5391802、88027、88027。

图2:吞吐量结果图从图2中的图可以看出,正如我们所料,多重响应。Write方法(RESP)为我们提供了整个重复测试范围内的最佳吞吐量。然而,令人惊讶的是,标准字符串串联方法(CAT)下降了这么多,而括号内的方法(PCAT)在重复执行300次以上时仍然表现得更好。在大约220次重复时,字符串缓存带来的性能提升超过了StringBuilder方法(BLDR)的固有开销,在此之上,在这个ASP页面中使用StringBuilder所需的额外开销是值得的。

图3:响应时间结果图。

图4:省略的CAT图图3的响应时间结果和图4中的图表显示了通过“到第一个字节的时间”(以毫秒为单位)测量的响应时间。因为标准字符串连接方法(CAT)的响应时间增长太快,所以提供了一个没有该方法的图表(图4)来分析其他方法之间的差异。值得注意的是,多重反应。Write方法(RESP)和StringBuilder方法(BLDR)随着重复次数的增加呈现近似线性增长,而标准连接方法(CAT)和括号方法(PCAT)在超过一定阈值后开始快速增长。

内置方法

本文重点介绍了如何在ASP环境中应用不同的字符串构造技术,该技术也适用于所有使用Visual Basic代码创建大字符串的方案,例如手动创建XML文档。以下原则可以帮助您确定哪种方法最适合您的需求。首先尝试括号“”方法,尤其是在处理现有代码时。这种方法对代码结构影响不大,但是你会发现应用程序的性能会有明显的提升,甚至会超出预期目标。使用响应。在不破坏所需封装级别的情况下写入。通过使用这种方法,可以避免不必要的内存字符串处理,从而提供最佳性能。使用StringBuilder构建非常大或有大量连接的字符串。虽然您可能看不到本文中显示的性能提升,但我已经在真实的ASP Web应用程序中使用了这些技术,并且我几乎不需要额外的投资就可以在性能和可扩展性方面获得很大的提升。

版权声明:提高ASP的字符串处理性能是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。