En effektiv strängsammanlänkningskomponent
Förord
Det här är en teknisk artikel, riktad till ASP/SQL utvecklare som vill höja hastigheten på sammanlänkning av strängar, såsom SQL-sats strängar.Innehåll
»»
»
»
»
En effektiv seriekomponent för strängar
av Michael Balloni
Jag skrev COM komponenten StrCat.Catter eftersom strängserien i VBScript har en usel prestandakaraktär. Jag fann koden så här:
<%
Dim rs
Set rs = ExecuteSql("SELECT Username FROM Users ORDER BY Username")
Dim some_str
some_str = ""
do until rs.EOF
some_str = some_str & rs(0) & "
" & vbCrLf
rs.MoveNext
loop
Response.Write some_str
%>
Den presterade väldigt uselt. Koden tog lång tid att exekvera, och medan den gjorde det så var processorn enormt sysselsatt. Jag fann artiklar på MSDN som menade att det inte var någon bra idé alls med sådana här koder, och jag har hört att den här typen av sammanlänkning resulterar i en körtid som är proportionell till kvadraten av antalet sammanlänkningar.
Ibland föredrar man en lösning som baseras på att passing strängar, istället för att passing funktionspekare eller funktionsobjekt som kan lägga ut strängar. Låt oss t ex säga att jag har en vanlig rutin som ber användaren att verifiera sin betalningsinformation innan de köper någonting, och att den här rutinen måste skriva ut en summering av vad användaren ska köpa. Du kan då bli frestad att säga:
<%
' Skriv ut din köpesumma...
' ...skriv sen ut ditt verifikationssystem för betalningsinformation.
%>
Men tänk om din verifikationskod för betalningsinformationen behöver några HTML taggar på båda sidor om köpesumman? Då skulle det vara bättre om du passing den köpesumman som en sträng till verifikationskoden för betalningsinformationen. Du skulle kunna slänga ihop något ”objekt som skriver ut köpesummorna när den här metoden anropas” system. Trots att VBScript tillåter ett sånt här system så ska vi hålla saker och ting simpla, och låtsas som vi bara vill lägga in en sträng och vi behöver en effektiv strängsammanlänkning.
Skriv in StrCat.Catter. ”Catter” är en C++ COM komponent med ett väldigt litet gränssnitt. Det kan ofta användas så här:
<%
Dim strcat
Set strcat = Server.CreateObject("StrCat.Catter")
Dim rs
Set rs = ExecuteSql("SELECT Username FROM Users ORDER BY Username")
do until rs.EOF
strcat rs(0) & "
" & vbCrLf
rs.MoveNext
loop
Dim some_str
some_str = strcat.Dump
Set strcat = nothing
Response.Write some_str
%>
Det var ganska lätt, eller hur? Genom att låta sammanlänkningsmetoden vara standardmetoden så behöver du bara säga Catters namn, i det här fallet StrCat, och så fixerar den sig på den sträng du gav den. När du är redo att använda hela strängen så anropar du bara ”Dump” metoden, och så har du den!
Gränssnitt
För de som kan IDL – eller som kan fejka det – så är Catters gränssnitt följande:[id(0), helpstring("Add a string to the end of this. Like str = str & .")]
HRESULT Cat([in] BSTR strToAdd);
[id(1), helpstring("Compute the total string for this.")]
HRESULT Resolve([out, retval] BSTR* pStrOutput);
[id(2), helpstring("Empty the contents of this.")]
HRESULT Reset();
[propget, id(3), helpstring("Get the current length of the total string. Like Len, but much
faster.")]
HRESULT Length([out, retval] int *pVal);
[id(4), helpstring("Add a string to the front of this. Like str = & str.")]
HRESULT Prepend([in] BSTR strToPrepend);
[id(5), helpstring("Surround the current contents with a before and after string. Like str =
HRESULT Surround([in] BSTR strToPrepend, [in] BSTR strToAppend);
[id(6), helpstring("Dump = Resolve then Reset. Returns the total strings, and empties the
contents of this.")]
HRESULT Dump([out, retval] BSTR* pStrOutput);
[id(7), helpstring("Synonym for Cat. Like str = str & .")]
HRESULT Append([in] BSTR strToAdd);
Som du kan se så är det här en huvudsyftlig strängkompileringsmotor. Först så fanns det bara Cat och Resolve. Sen fann jag att jag behövde Length, Prepend och Surround. Jag använder oftast Dump istället för Resolve, eftersom den släpper resurserna lite tidigare. Append och Reset inkluderades för fullständighet.
Användning
Jag använder gärna den här komponenten för sammanlänkning av SQL-sats strängar, både i VB och ASP script. SQL Server konsumerar stora grupper av SQL mycket bättre än många små förfrågningar. Eftersom Catter är så effektiv så kan du tjäna en hel del då storleken på strängarna och antalet sammanlänkningen ökar. Som ett exempel så använder jag den här komponenten till att skapa SQL som markerar ett helt set av databasmodellerade filer och kataloger, och dess underkataloger och underfiler, som private eller public. Om det bara finns ett fåtal filer och kataloger så kan du klara det med en VB strängsammanlänkning. Om det gäller några tusen filer och djupgående kataloger så behöver du den förbättrade prestandan från Catter.
Prestanda
Catter utpresterar en vanlig VB strängsammanlänkning efter flera dussin sammanlänkningar. Här följer en VB testkod som jag använde för att testa Catters prestanda jämfört med VBScript. Du kan även se resultatet av testet längre ner.
<%
'
' Test out CAT'ing.
'
Private Sub TestCat()
Dim test_str As String
test_str = "foo foo bar"
TestCatFor 1, test_str
TestCatFor 5, test_str
TestCatFor 10, test_str
TestCatFor 25, test_str
TestCatFor 50, test_str
TestCatFor 100, test_str
TestCatFor 200, test_str
TestCatFor 500, test_str
TestCatFor 1000, test_str
TestCatFor 2000, test_str
'TestCatFor 5000, test_str
'TestCatFor 10000, test_str
test_str = "asd;fjas;dlfu =8-081254j kuva nfajoiruawior "
TestCatFor 1, test_str
TestCatFor 5, test_str
TestCatFor 10, test_str
TestCatFor 25, test_str
TestCatFor 50, test_str
TestCatFor 100, test_str
TestCatFor 200, test_str
TestCatFor 500, test_str
TestCatFor 1000, test_str
TestCatFor 2000, test_str
'TestCatFor 5000, test_str
'TestCatFor 10000, test_str
test_str = "asd;fjas;dlfu =8-081254j kuva nfajoiruawior asd;fjas;dlfu =8-081254j kuva
nfajoiruawior asd;fjas;dlfu =8-081254j kuva nfajoiruawior asd;fjas;dlfu =8-081254j kuva
nfajoiruawior "
TestCatFor 1, test_str
TestCatFor 5, test_str
TestCatFor 10, test_str
TestCatFor 25, test_str
TestCatFor 50, test_str
TestCatFor 100, test_str
TestCatFor 200, test_str
TestCatFor 500, test_str
TestCatFor 1000, test_str
TestCatFor 2000, test_str
'TestCatFor 5000, test_str
'TestCatFor 10000, test_str
test_str = "asd;fjas;dlfu =8-081254j kuva nfajoiruawior asd;fjas;dlfu =8-081254j kuva
nfajoiruawior asd;fjas;dlfu =8-081254j kuva nfajoiruawior asd;fjas;dlfu =8-081254j kuva
nfajoiruawior asd;fjas;dlfu =8-081254j kuva nfajoiruawior asd;fjas;dlfu =8-081254j kuva
nfajoiruawior asd;fjas;dlfu =8-081254j kuva nfajoiruawior asd;fjas;dlfu =8-081254j kuva
nfajoiruawior asd;fjas;dlfu =8-081254j kuva nfajoiruawior asd;fjas;dlfu =8-081254j kuva
nfajoiruawior asd;fjas;dlfu =8-081254j kuva nfajoiruawior "
TestCatFor 1, test_str
TestCatFor 5, test_str
TestCatFor 10, test_str
TestCatFor 25, test_str
TestCatFor 50, test_str
TestCatFor 100, test_str
TestCatFor 200, test_str
TestCatFor 500, test_str
TestCatFor 1000, test_str
TestCatFor 2000, test_str
'TestCatFor 5000, test_str
'TestCatFor 10000, test_str
End Sub
Private Sub TestCatFor(ByVal iters As Long, ByVal testStr As String)
Dim prof As Object
Set prof = CreateObject("Softwing.Profiler")
Dim i As Long
Dim max_i As Long
max_i = iters
' Go traditional.
prof.ProfileStart
Dim str As String
For i = 0 To max_I
str = str & testStr
Next
Dim slow_ms As Double
slow_ms = prof.ProfileStop
' Go cat.
prof.ProfileStart
Dim strcat As Catter
Set strcat = New Catter
For i = 0 To max_I
strcat testStr
Next
Dim cat_str
cat_str = strcat.Dump
' Clean up the catter.
Set strcat = Nothing
' Stop the clock.
Dim cat_slow_ms As Double
cat_slow_ms = prof.ProfileStop
' Check that CAT did the same as traditional.
Debug.Assert cat_str = str
' Clean up the profiler.
Set prof = Nothing
' Output results.
Debug.Print iters & " Run (" & Len(testStr) & "): " & FormatNumber(slow_ms / 10) & " vs. "
& FormatNumber(cat_slow_ms / 10)
End Sub
%>
Och här följer testresultaten. Alla tider är i millisekunder.
Du kan se att det tar ungefär tre gånger så lång tid för Catter att komma upp i en hastighet för en enda sammanlänkning, och även efter ett dussintal sammanlänkningar så ligger Catter efter. Men när VB väl börjar sakta ner så gör den det så drastiskt, kvadratiskt faktiskt, medan Catter tar det lugnt och nästan håller sig linjär i tal om tid kontra sammanlänkningar. Jag skulle avsky att vara den som måste vänta 25 sekunder för att VB ska länka samman några strängar!
Implementering
Catter är en STL vektor av en STL wstring. Catters implementering anropar reservmetoderna av både vektorn och wstring klasserna. Implementeringen av dessa klasser är designade så att deras exekveringstid häller sig linjär under dessa omständigheter. De klarar av det genom att fördubbla dess allokerade minne då de behöver växa, och på så sätt kan de undvika att göra för många allokeringar och fragmentera gruppen.
Slutsats
När du måsta länka samman mer än ett fåtal dussin strängar så bör du överväga att använda StrCat.Catter. Catters optimerade C++ implementering kan förhindra din sajt från CPU dränkning då folket börjar kräva mer än vad VB kan klara av.Du kan ladda ner källkoden och DLLen för StrCat.Catter på
0 Kommentarer