![]() ![]() ![]() ![]() 圖 1:標準連接與加括號連接在內存使用模式方面的比較 在特定情況下,使用括號可以對性能和可縮放性產生十分顯著的影響,后文將對此進行進一步的說明。 StringBuilder 我們已經找到了解決字符串連接問題的快捷方法,在多數情況下,此方法可以達到性能和投入的最佳平衡。但是,如果要進一步提高構建大型字符串的性能,需要采用第二種方法,即減少字符串分配操作的數目。為此,需要使用 StringBuilder。StringBuilder 是一個類,用于維護可配置的字符串緩沖區,管理插入到此緩沖區的新文本片斷,并僅在文本長度超出字符串緩沖區長度時對字符串進行重新分配。Microsoft .NET 框架免費提供了這樣一個類 (System.Text.StringBuilder),并建議在該環境下進行的所有字符串連接操作中使用它。在 ASP 和傳統的 Visual Basic 環境中,我們無法訪問此類,因此需要自行創建。下面是使用 Visual Basic 6.0 創建的 StringBuilder 類示例(為簡潔起見,省略了錯誤處理代碼)。 Option Explicit' 默認的緩沖區初始大小和增長系數Private Const DEF_INITIALSIZE As Long = 1000Private Const DEF_GROWTH As Long = 1000' 緩沖區大小和增長Private m_nInitialSize As LongPrivate m_nGrowth As Long' 緩沖區和緩沖區計數器Private m_sText As StringPrivate m_nSize As LongPrivate m_nPos As LongPrivate Sub Class_Initialize() ' 設置大小和增長的默認值 m_nInitialSize = DEF_INITIALSIZE m_nGrowth = DEF_GROWTH ' 初始化緩沖區 InitBufferEnd Sub' 設置初始大小和增長數量Public Sub Init(ByVal InitialSize As Long, ByVal Growth As Long) If InitialSize > 0 Then m_nInitialSize = InitialSize If Growth > 0 Then m_nGrowth = GrowthEnd Sub' 初始化緩沖區Private Sub InitBuffer() m_nSize = -1 m_nPos = 1End Sub' 增大緩沖區Private Sub Grow(Optional MinimimGrowth As Long) ' 初始化緩沖區(如有必要) If m_nSize = -1 Then m_nSize = m_nInitialSize m_sText = Space$(m_nInitialSize) Else ' 只是增長 Dim nGrowth As Long nGrowth = IIf(m_nGrowth > MinimimGrowth, m_nGrowth, MinimimGrowth) m_nSize = m_nSize + nGrowth m_sText = m_sText & Space$(nGrowth) End IfEnd Sub' 將緩沖區大小調整到當前使用的大小Private Sub Shrink() If m_nSize > m_nPos Then m_nSize = m_nPos - 1 m_sText = RTrim$(m_sText) End IfEnd Sub' 添加單個文本字符串Private Sub AppendInternal(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 Sub Append(ParamArray Text()) Dim nArg As Long For nArg = 0 To UBound(Text) AppendInternal CStr(Text(nArg)) Next nArgEnd Sub ' 返回當前字符串數據并調整緩沖區大小Public Function ToString() As String If m_nPos > 0 Then Shrink ToString = m_sText Else ToString = "" End IfEnd Function' 清除緩沖區并重新初始化Public Sub Clear() InitBufferEnd Sub此類中使用的基本原則是,在類級別將變量 (m_sText) 用作字符串緩沖區,并使用 Space$ 函數以空格字符填充此緩沖區以將其設置為特定的大小。如果要將更多文本與現有文本連接在一起,則在檢查緩沖區的大小足以存放新文本后,使用 Mid$ 函數在正確位置插入文本。ToString 函數將返回當前存儲在緩沖區中的文本,并將緩沖區的大小調整為能夠容納此文本的正確長度。使用 StringBuilder 的 ASP 代碼如下所示: Function WriteHTML( Data )Dim oSBDim nRepSet oSB = Server.CreateObject( "StringBuilderVB.StringBuilder" )' 用大小和增長系數初始化緩沖區oSB.Init 15000, 7500For nRep = 0 to 99 oSB.Append "<TR><TD>", (nRep + 1), "</TD><TD>", _ Data( 0, nRep ), "</TD><TD>", _ Data( 1, nRep ), "</TD><TD>", _ Data( 2, nRep ), "</TD><TD>", _ Data( 3, nRep ), "</TD><TD>", _ Data( 4, nRep ), "</TD><TD>", _ Data( 5, nRep ), "</TD></TR>"NextWriteHTML = oSB.ToString()Set oSB = NothingEnd Function 使用 StringBuilder 需要一定的開銷,因為每次使用此類時都必須創建它的實例,并且在創建第一個類實例時必須加載包含此類的 DLL。對 StringBuilder 實例進行額外方法調用時也需要開銷。使用加括號的“&”方法時,StringBuilder 如何執行取決于多個因素,包括連接的數目、要構建的字符串的大小以及選擇的 StringBuilder 字符串緩沖區的初始化參數的性能。請注意,在多數情況下,將緩沖區中所需的空間量估計得略高一些要遠遠好于讓其不斷增長。 內置方法 ASP 包含一種非?旖莸膭摻 HTML 代碼的方法,只需多次調用 Response.Write。Write 函數使用隱式優化的字符串緩沖區,此緩沖區能夠提供非常優秀的性能特性。修改后的 WriteHTML 代碼如下所示: Function WriteHTML( Data )Dim nRepFor nRep = 0 to 99 Response.Write "<TR><TD>" Response.Write (nRep + 1) Response.Write "</TD><TD>" Response.Write Data( 0, nRep ) Response.Write "</TD><TD>" Response.Write Data( 1, nRep ) Response.Write "</TD><TD>" Response.Write Data( 2, nRep ) Response.Write "</TD><TD>" Response.Write Data( 3, nRep ) Response.Write "</TD><TD>" Response.Write Data( 4, nRep ) Response.Write "</TD><TD>" Response.Write Data( 5, nRep ) Response.Write "</TD></TR>"NextEnd Function 雖然這段代碼很可能為我們提供最佳的性能和可縮放性,但在某種程度上已經破壞了封裝,因為現在會將函數內部的代碼直接寫入 Response 流,所以調用代碼喪失了一定程度的控制權。另外,移動此代碼(例如,移入 COM 組件)將變得更加困難,因為此函數與 Response 流存在依賴關系。 測試 上面提到的四種方法分別通過一個簡單的 ASP 頁面(包含一個由虛擬字符串數組提供數據的單個表格)進行了測試。我們使用 Application Center Test® (ACT) 從單個客戶端(Windows® XP Professional,PIII-850MHz,512MB RAM)針對 100Mb/sec 網絡中的單個服務器(Windows 2000 Advanced Server,雙 PIII-1000MHz,256MB RAM)執行了測試。ACT 配置為使用 5 個線程,以模擬 5 個用戶連接至網站時的負載。每個測試都包括 20 秒預熱時間和隨后的 100 秒負載時間,在負載期間創建了盡可能多的請求。 通過更改主表格循環中的重復次數,針對不同數目的連接操作重復運行測試,如 WriteHTML 函數中的代碼片斷所示。運行的每個測試都使用上文提到的四種不同的方法執行。結果 下面的一系列圖表顯示了各種方法對整個應用程序吞吐量的影響,以及 ASP 頁面的響應時間。通過這些圖表,我們可以了解應用程序支持的請求數目,以及用戶等待頁面下載至瀏覽器所需的時間。 表 1:使用的連接方法縮寫的說明 方法縮寫說明 RESP內置 Response.Write 方法CAT標準連接(“&”)方法PCAT加括號的連接(“&”)方法BLDRStringBuilder 方法 在模擬典型 ASP 應用程序工作負荷方面,此測試與實際情況相差甚遠,從表 2 中可以明顯看到,即使重復 420 次,此頁面仍不是特別大,F在很多復雜的 ASP 頁面在這些數字上都是比較高的,設置有可能超出此測試范圍的限制。 表 2:測試示例的頁面大小和連接數目 重復次數連接數目頁面大。ㄒ宰止潪閱挝唬152402,667304804,917457207,167609609,417751,20011,6671201,92018,5391802,88027,8992403,84037,2593004,80046,6193605,76055,9794206,72062,219 ![]() ![]() ![]() ![]() 圖 2:吞吐量結果圖 從圖 2 的圖表中可以看到,正如我們所預期的,多重 Response.Write 方法 (RESP) 在測試的整個重復測試范圍中為我們提供了最佳的吞吐量。但令人驚訝的是,標準字符串連接方法 (CAT) 的下降如此巨大,而加括號的方法 (PCAT) 在重復執行 300 多次時性能依舊要好很多。在大約重復 220 次之處,字符串緩存帶來的性能提高超過了 StringBuilder 方法 (BLDR) 固有的開銷,在這一點以上,在此 ASP 頁面中使用 StringBuilder 所需的額外開銷是值得的。 ![]() ![]() ![]() ![]() 圖 3:響應時間結果圖 ![]() ![]() ![]() ![]() 圖 4:省略 CAT 的響應時間結果圖 圖 3 和圖 4 中的圖表顯示了按“到第一字節的時間”測量的響應時間(以毫秒為單位)。因為標準字符串連接方法 (CAT) 的響應時間增加過快,所以又提供了未包括此方法的圖表(圖 4),以便分析其他方法之間的差異。有一點值得注意,多重 Response.Write 方法 (RESP) 和 StringBuilder 方法 (BLDR) 隨重復次數的增加呈現一種近似線性的增長,而標準連接方法 (CAT) 和加括號的方法 (PCAT) 則在超過一定的閾值之后開始迅速增加。 小結 本文著重講述了如何在 ASP 環境中應用不同的字符串構建技術,這些內容同樣適用于所有使用 Visual Basic 代碼創建大型字符串的方案,例如手動創建 XML 文檔。以下原則可以幫助您確定哪種方法最適合您的需要。 首先嘗試加括號的“&”方法,尤其是在處理現有代碼時。這種方法對代碼結構的影響微乎其微,但您會發現應用程序的性能將顯著增強,甚至會超出預定目標。 在不破壞所需的封裝級別的情況下使用 Response.Write。使用此方法,可以避免不必要的內存內字符串處理,從而提供最佳的性能。 使用 StringBuilder 構建真正大型或連接數目較多的字符串。 盡管您可能未看到本文所示的這種性能增長,但我已在真實的 ASP Web 應用程序中使用了這些技巧,只需要很少的額外投入就可以在性能和可縮放性方面獲得很大的提高。(出處:Viphot) |
溫馨提示:喜歡本站的話,請收藏一下本站!