Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

ASP Speed Tricks Article 1

Status
Not open for further replies.
I just wanted to announce that I've posted an article I
wrote on techniques for increasing the speed of database
generated ASP pages. If you're interested, please check
it out at:


The article describes nearly a dozen practical methods,
and includes source code and test results. Comments
are welcome.

Thanks,
Shailesh
 
Nicely done. One comment though, you may want to include the table layout and field types for the article. Overall I have to say I like the article and comparisons, though was a little surprised that you didn't have a column in the GetRows comparison table (7) with the optimized printing based on the recordset object for a full comparison between the major three methods.

-Tarwn

[sub]01000111 01101111 01110100 00100000 01000011 01101111 01100110 01100110 01100101 01100101 00111111[/sub]
minilogo.gif alt=tiernok.com
The never-completed website
 
I'm suprised with the section titled "Better Performance with Recordset Field References". I didn't know that the object reference would help.

I'm not sure that your section title "Eliminating Concatenation From the Loop" is accurate. I thought that mulitple response.write statements hampered the server. The long time you see in the first section can easily be overcome with a class...

Class strCat
Private IntCntr
Private strArray()
Private intLength
Public Property Get Length
Length = intLength
End Property
Public Property Let Length( ByVal intLen)
intLength = intLen
IntCntr = 0
Redim strArray(1)
strArray(0) = ""
Redim strArray(intLength)
End Property
Public Property Get Value
Value = Join( strArray,"")
End Property
Private Sub Class_Initialize()
IntCntr = 0
Length = 100000
End Sub
Public function Add( strToAdd)
strArray(IntCntr) = strToAdd
IntCntr = IntCntr + 1
End function
End Class

dim strOut
set strOut = new strCat

strOut.add(&quot;<table>&quot;)
Do While Not objRS.EOF
strOut.add(&quot;<tr><td>&quot; & objRS(&quot;field1&quot;) & &quot;</td>&quot;)
strOut.add(&quot;<&quot;<td>&quot; & objRS(&quot;field2&quot;) & &quot;</td>&quot;)
strOut.add(&quot;<&quot;<td>&quot; & objRS(&quot;field3&quot;) & &quot;</td>&quot;)
strOut.add(&quot;<&quot;<td>&quot; & objRS(&quot;field4&quot;) & &quot;</td></tr>&quot;)
objRS.MoveNext
Loop
strOut.add(&quot;<&quot;</table>&quot;)

response.write strOut.value

Programming today is a race between software engineers striving to build better and bigger idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. - Rick Cook
 
DOH!!!! typos!!!!!

strOut.add(&quot;<table>&quot;)
Do While Not objRS.EOF
strOut.add(&quot;<tr><td>&quot; & objRS(&quot;field1&quot;) & &quot;</td>&quot;)
strOut.add(&quot;<td>&quot; & objRS(&quot;field2&quot;) & &quot;</td>&quot;)
strOut.add(&quot;<td>&quot; & objRS(&quot;field3&quot;) & &quot;</td>&quot;)
strOut.add(&quot;<td>&quot; & objRS(&quot;field4&quot;) & &quot;</td></tr>&quot;)
objRS.MoveNext
Loop
strOut.add(&quot;</table>&quot;)


Programming today is a race between software engineers striving to build better and bigger idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. - Rick Cook
 
Thats interesting, I'm getting longer execution times for multiple Response.Writes.
500,000 loops with concatenation @ 1.2349 seconds
500,000 loops with multiple writes @ 1.2659 seconds

This was based on only a single run, I decided to rerun this in smaller loops and use average over 10 runs:
10 Loops of 50,000 loops of Concatenation: .1172 seconds
10 Loops of 50,000 loops of Muliple Writes: .12819 seconds

This is a 10% difference in favor of concatenation. I'm a little confused now...

[sub]01000111 01101111 01110100 00100000 01000011 01101111 01100110 01100110 01100101 01100101 00111111[/sub]
minilogo.gif alt=tiernok.com
The never-completed website
 
Tarwyn - did you try my class statement? I started using it when concatenating very long HTML strings (over 1 million characters). The server really hated adding 50 characters to 1.2M character string... The class enables the processor to not have to wait for each small addition...

Programming today is a race between software engineers striving to build better and bigger idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. - Rick Cook
 
If i Included the instatiation I got .8641 as the avg of ten runs of 50,000 outputs:
Code:
For j = 1 to 10
	set strOut = new strCat

	setStart()

	For i = 1 to 50000
		strOut.add(&quot;<span style=&quot;&quot;display:none;&quot;&quot;>&quot; & i & &quot;</span>&quot;)
	Next
	Response.Write strOut.value

	setEnd()

'	Response.Write &quot;Python Elapsed: &quot; & elapsed() & &quot;<br>&quot;
	ts(j) = elapsed()
	restart()
Next

setStart sets thestart time, setEnd sets the end time, and restart clears the clocks. (Someone would have asked if I didn't say it :) )
This is using the same exact string as before. Breaking it down into seperate add statements:
Code:
strOut.add(&quot;<span style=&quot;&quot;display:none;&quot;&quot;>&quot;)
strOut.add(i)
strOut.add(&quot;</span>&quot;)
Gave me an average of: 1.75 seconds

note: I had to increase the maximum bounds on the array in your class in order to complete this test as I was adding 150,000 values. I went back to the concatenation example with the bigger array to see if that caused a difference (which is why there isn't a time posted earlier).

10 loops of 50,000 loops of Three .add()'s: 1.75 seconds
10 loops of 50,000 loops of concatenaed .add(): .89839 seconds
10 loops of 50,000 loops of three writes: .12659
10 loops of 50,000 loops of concatenation: .12029


Overall it appears that method may be slower. I did rerun the first tweo sets, and even though they came in closer, the concatenation method was still faster. I'm not sure why the numbers have changed as much as they have, they were pretty consistent the first time through over several runs and seem to be just as consistent this time through.

[sub]01000111 01101111 01110100 00100000 01000011 01101111 01100110 01100110 01100101 01100101 00111111[/sub]
minilogo.gif alt=tiernok.com
The never-completed website
 
I have to apologize, the first set of numbers was heating up the processor, so they weren't good tests. The first concatenation run got a 6 degrees cooler CPU so that may have made a difference.

The following numbers are the third run result to remove any effects of the temperature difference:
3 writes, flush: .12819
concatenation, flush: .1203
3 writes, no flush: .125
3 concats, no flush: .12189

The flush/no flush is whether I had a flush outside of the loops before I output the times. Apparently this makes a minor difference as well, but the difference may be so minor that it means nothing.

Since we are dealing with milliscond differences in times, there is no way to truly say if these numbers mean anything or not. Without being able to run this test in a sterile environment (ie, no extra processes borrowing the CPU for whatever reason, etc) the results cannot be totally trusted.

Then again since we are talking about an average difference of 4 to 8 milliseconds over the output of 150,000 Response.Writes vs 50,000 Response.Writes and 100,000 concatenations, the differences are trivial. I have yet to see a page that even approaches these figures. While the general gain in processing time of recordset data is noticeble and can affect the end load time, I don't see the concatenation vs Response.Write times being a large enough difference to affect the outcome in a real life setting.

Generally in benchmarking recordset methods I find it much more consistent to not write anything at all out during the actual loops. This can be achieved by simply sending the data to a variable. You still get some memory allocation slowdown, but it removes any question of concatenation vs Response.Write.



[sub]01000111 01101111 01110100 00100000 01000011 01101111 01100110 01100110 01100101 01100101 00111111[/sub]
minilogo.gif alt=tiernok.com
The never-completed website
 
Tarwyn -

I never noticed the performance problem until I uploaded to the web host. I think that the string concatenation was using the some common memory area (shared with other sites) while the CLASS seemed to not have the problem. Is that a possibility of why a page that took 20 minutes to load using concatenation took less than 10 seconds when I switched to the CLASS method?

Programming today is a race between software engineers striving to build better and bigger idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning. - Rick Cook
 
Wow, that is a speed increase.
It's possible. I was actually surprised that it took longer to use the class than the concatenation or Response.Write's alone.
Actually, I wouldn't be surprised if that was the case. I have heard a lot of warnings about Response.Write and concatenation being slow, it could be that since I was only doing those operations that I didn't encounter a real slowdown, but if I was doing other operations first the concatenations and Response.Write's would have gotten lower priority, especially across multiple connections.

[sub]01000111 01101111 01110100 00100000 01000011 01101111 01100110 01100110 01100101 01100101 00111111[/sub]
minilogo.gif alt=tiernok.com
The never-completed website
 
I updated the results table as you suggested. I also added a note about the table structure at the beginning of the article, and that it is fully described in the appendix.

I have also seen the class technique for buffering the database output. Read the MSDN article link at the end of this post for an excellent overview. Apparently, the Response.write, when ASP buffering is enabled, buffers into the COM IStream object, which I believe is essentially a byte array pre-allocated by the web server. If you allocate an array to hold the string in ASP, you are duplicating effort, because you could just write it out directly to the Response. Response.write is supposed to be the fastest method, although your test results are curious. In the few minimalistic tests I did, Response.write was fastest.

As for concatenation, remember that there's a difference between accumulative concatenation and non-accumulative concatenation. The former is O(n^2), or exponential in time complexity. (i.e. 4 times slower when printing 2 times more records) The latter form of concatenation is just slow because it allocates and copies memory each time. Response.write, on the other hand, just appends to a pre-allocated array.

I know that moving the mouse, switching between applications, network traffic, and moving around windows can all affect test results. But, I'm highly skeptical that the temperature of your CPU affected it's performance. Perhaps you have a laptop? Even then, I'm not aware that the CPU clock fluctuates based on its temperature.

Thanks for your comments.
Shailesh

Improving String Handling Performance in ASP Applications
 
My test results in this instance pretty much agreed with the Response.Write being faster, but I'll have to dig through my files because I think at one point someone had me trying a similar method and it clocking a lower number. Note that the above value for the class was 8 to 10 times higher than Response.Write which actually agrees with your statement.
As far as CPU temp goes, it shouldn't affect the outcome, but I wanted to remove the chance anyway. It could just have easily been an issue with memory deallocation or any of a dozen things that would affect the first run less intensely than the second run. In order to remove this factor I ran each of the tests three times and took the third value. I only mentioned CPU temp because it was easier to run all of the tests at the same temperature than to have a memory usage monitor running as well.

In theory you are correct about accumulative authentication. But we have to remember that the method is affected by the implementation and that we are dealing with two envirnoments.
We are comparing a process (or memory allocation) that is internal to the VBScript, or windows scripting, environment, to an ASP object that belongs to IIS. If both objects existed in the same environment it could be fully expected that they would follow theoretical limits closer, but since they do not exist in the same environments (even if they are accessible from the same envirnoments) then the question becomes a matter of communication + write time.


The problem is that when it comes to output, I have seen benchmarks done from just about every direction that each show better results from one method and worse for another. Concatenation, Response.Write, Interleaved text, etc

In fact, a good example is interleaved text. Microsoft also agrees that this is a bad way of doing things and that it is ineeficient to escape and return to the script multiple times. I have benchmarks that agre with that. But James Greenwood submitted an article to 4GuysFromRolla showing benchmarks of interleaved text, standard response.Write, and With Response statements here: His results showed that
Code:
For n = 1 To 500000
     %>.<%
Next

is faster than

For n = 1 To 500000
      Response.Write &quot;.&quot;
Next

This directly disagrees with Microsoft's documentation on increasing performance in scripts, found here:

Now whether this is a versioning issue or an issue of the installation and setup I don't know. In either case I find it rare that the method of writing makes a great difference in the final outcome because generally the differences in timing can only be seen under extreme circumstances, such as looping through 50,000 or more print statements.

---------
Note of clarification for my above script, I didn't notice it at the time, but the output contains &quot;Python Elapsed&quot;. The reason behind that is because I used time modules from python in order to get a greater degree of accusracy in the timing. VBScript doesn't inherently support millisecond timings and while it is possible to get these measurements without resorting to the Python or JScript time objects, I decided the amount of time was less important than the difference in the times, and in this case the amount of time also included how long I was going to sit here and write extra code to force VBScript to do millisecond measurements without resorting to outside components or the Timer object.


[sub]01000111 01101111 01110100 00100000 01000011 01101111 01100110 01100110 01100101 01100101 00111111[/sub]
minilogo.gif alt=tiernok.com
The never-completed website
 
I agree that the benefits of a technique often are not substantial enough or even existent in practice. The article at 4guysfromrolla was comparing context switching, not concatenation versus Response.write. In that case, the performance is certainly implementation dependent, leading to the conflicting reports. I want to show that concatenation is fundamentally different than using Response.write, regardless of implementation.

When printing &quot;abc&quot; and &quot;def&quot;, one has a choice of either:

Code:
Response.write &quot;abc&quot; & &quot;def&quot;
or
Code:
Response.write &quot;abc&quot;
Response.write &quot;def&quot;

In this example, &quot;abc&quot; is a 6 byte memory structure, and &quot;def&quot; is a 6 byte memory structure (Unicode strings). To concatenate the strings, a new 12 byte memory structure must be allocated. Then, &quot;abc&quot; and &quot;def&quot; must be copied into the new memory, and the old memory deallocated. Finally, to print the text, the 12 bytes is copied again to the memory of the Response buffer.

While the allocation and deallocation may be fairly quick, the copying has to be done twice because of the concatenation. On the other hand, with two Response.write calls, no extra allocation or deallocation must be done. Also, the data is copied exactly once, rather than twice. (It's possible that the Response buffer is too small, and it had to be dynamically resized, but I'm assuming the server is configured to have an adequate initial size and efficient resizing policy.) With accumulative concatenation, a longer and longer string is being recopied on each iteration, which is highly inefficient.

I applied this technique to a non-theoretical page for one of my clients, and it made about a 5-10% difference in speed. This page typically printed several hundred lengthy records. It's not something to write home about, but it does help on a page that needed to be as fast as possible.

For example, I changed:
Code:
Response.write &quot;<td id=d&quot; & varNum & &quot;c>&quot; & journalnumber
Response.write &quot;</td></tr>&quot; & vbCrLf
To:
Code:
Response.write &quot;<td id=d&quot;
Response.write varNum
Response.write &quot;c>&quot;
Response.write journalnumber
Response.write &quot;</td>&quot; 
Response.write  &quot;</tr>&quot;
Response.write vbCrLf

The technique sacrifices readability for maximum performance. I have implemented stream buffers in C, and the append method is very fast for the same reasons why I'm assuming Response.write is very fast. Unfortunately, we can't see the source code for Response.write, so there's no way to say with absolute certainty why it is faster, and everyone has to do their own testing.

Anyway, can you tell me how to use the Python timer from ASP? The VBScript built-in timer has rather low resolution.

Thanks,
Shailesh
 
On the original issue:
The problem is in the implementation though. The Response object belong to a seperate process than the variable in the scripting. That object may have wrappers that allow the scripting to access it's methods, but it still actually exists in another environment. This may even be the reason for the difference in our results. I'm running IIS at default settings in the hopes that people's hosts that I load my sites onto will have either the same settings or more optimized settings (hopefully not less optimized settings). I don't know enough about how IIS instantiates Response, Request, etc objects and allocates memory for them, but it could be that certain optimization act directly on those objects. If thats the case then the Response.Write method is suddenly going to become much quicker than concatenation inside the scripting environment.

What would be interesting to see is a comparison of using Response.Write from a variety of scripting languages to see if they all have the same level of connection with the ASP objects or if it is language dependant. I'm willing to bet this is going to be language dependant.

Python:
You can use PythonScript as your scripting language just as you would use JScript or PerlScript. You'll need to download python from python.org first. I believe they include instructions on how to set up IIS to allow this as a ascripting language as well. Then you can use the script runat=server trick to have a standard ASP page that also uses functions from another language. While this may cost some unknown amount of overhead to call into another scripting engine, when doing benchmarks like these it is the comparitive value, not the actual value that counts, so that little overhead shouldn't make a difference as it should always be the same.

Here is a JScript Example:
I wasn't actually getting good results with this, but it showed me how I was going to do it with Python.

Here is the full code I used for one of the tests:
Code:
<%
Dim avgd, strOut
Dim ts(10)

For j = 1 to 10
	setStart()

	For i = 1 to 50000
		Response.Write &quot;<span style=&quot;&quot;display:none;&quot;&quot;>&quot; 
		Response.Write i 
		Response.Write &quot;</span>&quot;
	Next
	setEnd()

'	Response.Write &quot;Python Elapsed: &quot; & elapsed() & &quot;<br>&quot;
	ts(j) = elapsed()
	restart()
Next

For j = 1 to 10
	avgd = avgd + cDbl(ts(j))
	Response.Write &quot;time[&quot; & j & &quot;]: &quot; & ts(j) & &quot;<br>&quot;
Next
avgd = avgd/10
Response.Write &quot;10 loops: &quot; & avgd


%>
<SCRIPT LANGUAGE=&quot;Python&quot; RUNAT=&quot;Server&quot;>
#Small Script to use for benchmarking
from time import time

tim = []

def restart():
	tim.pop()
	tim.pop()

def setStart():
	tim.append(time())

def setEnd():
	tim.append(time())

def elapsed():
	diff = tim[1] - tim[0]
	stri = diff
	return str(diff)

</script>

I am currently running with Python 2.2 on my server right now and using the time method (returns a decimal value for current seconds to three places). Apparently version 2.3 has a few new objects, such as the timer object, that give even greater system time precision using c coded objects. I noticed that the python times seemed to have slightly less than true microsecond resolution, but that may have just been the results of me seeing the same functions run so many times on end. I'm currently writing a script to see what kind of precision I can get out of it.

[sub]01000111 01101111 01110100 00100000 01000011 01101111 01100110 01100110 01100101 01100101 00111111[/sub]
minilogo.gif alt=tiernok.com
The never-completed website
 
excellent post Tarwn

____________________________________________________
[sub]Python????? The other programming language you never thought of!
thread333-584700[/sub]
onpnt2.gif
 
Ok, the resolution is only about .016 seconds. So this may not be the best way to measure milliseconds, though it looks like if we were looking for microsecond resolution we could be pretty happy. That means any of the answers above could have been +- .008 milliseconds.

Since I was already messing with timing in python, I checked out the Response.Write times for it.
Set 1: 2.6015 seconds
Set 2: 7.014 seconds
Set 3: 1.051 seconds - unbuffered output
Set 4: 3.589 seconds - unbuffered output
Code:
Each of these was run 10 times and averaged:
Set 1: Response.Write with concatenation (ASP/Python)
Set 2: 3 Response.Write's (ASP/Python)
Set 3: print with concatenation (CGI/Python)
Set 4: 3 prints (CGI/Python)

ASP/Python means using Python instead of VBScript for the ASP
CGI/Python means running Python as a CGI script

Anyways. The results actually helkp to show the difference between the environments for the Response object and the script variables. VBScript is obviously more optimized in using the Response object as we saw from the above examples earlier. But, this only serves to underline that while, theoretically, concatenation should be slower on all systems, the fact that Response.Write is actually making a call outside of the environment to another environment means we have to take the communications between the two environments into account.

Also remember that the above ASP examples were mostly with Buffering on, without buffering the web server is going to be sending out packets with less than full contents and fielding ACKs for every single one, so we have increased network traffic in an unbuffered environment, decreasing efficency. All this does is show even more how much of the slowdown belongs to the communications between our script and the Response object.

When benchmarking in ASP the implementation becomes a very big part of the equation simply because we are talking between two environments. If we were simply writing from vbscript to a buffer it would likely be a differant matter.

Since I am already playing with Python, here's an example of running the script command line rather than as a CGI, this time set 3 and 4 will be pure Python writing to a file:
Code:
Set 3: .2608 seconds
Set 4: .2797 seconds
This should even greater show the difference bewteen doing something internally and calling out to an object from another environment.

Hope I didn't get to carried away on this :)

-Tarwn

[sub]01000111 01101111 01110100 00100000 01000011 01101111 01100110 01100110 01100101 01100101 00111111[/sub]
minilogo.gif alt=tiernok.com
The never-completed website
 
Argh, each of those sets was:
run 10 times and averaged, each run consisted of 50,000 loops

Sorry about that, I'm not as up to speed as I would like to be today..
 
Ok, I understand what your point is now. The trouble with me is I tend to think everyone writes code in the same environment as me (VBScript/ASP3.0). I'll have to try out python; it seems to be very popular. From your test results, I suspect that Python's call out to the Response.write is very slow compared to the time required to do a small concatenation. However, I still believe concatenation behaves the way I described. As a test, you could modify your script to concatenate more times and longer strings. I think at some point, breaking it down into multiple response.write calls would be faster than concatenating.
Try something like:
Code:
Response.write &quot;a&quot;&&quot;b&quot;&&quot;c&quot;&&quot;d&quot;&&quot;e&quot;&&quot;f&quot;&&quot;g&quot;&_
&quot;h&quot;&&quot;j&quot;&&quot;k&quot;&&quot;l&quot;&&quot;m&quot;&&quot;n&quot;&&quot;o&quot;&&quot;p&quot;&&quot;q&quot;&&quot;r&quot;&&quot;s&quot;&_
&quot;t&quot;&&quot;u&quot;&&quot;v&quot;&&quot;w&quot;&&quot;x&quot;&&quot;y&quot;&&quot;z&quot;
versus
Code:
Response.write &quot;abcdefghijklmnopqrstuvwxyz&quot;

10000 iterations in ASP3.0/VBScript takes 0.25 seconds versus 0.02 seconds, respectively.
Shailesh
 
that really doesn't make any sense
Response.write &quot;a&quot;&&quot;b&quot;&&quot;c&quot;&&quot;d&quot;&&quot;e&quot;&&quot;f&quot;&&quot;g&quot;&_
&quot;h&quot;&&quot;j&quot;&&quot;k&quot;&&quot;l&quot;&&quot;m&quot;&&quot;n&quot;&&quot;o&quot;&&quot;p&quot;&&quot;q&quot;&&quot;r&quot;&&quot;s&quot;&_
&quot;t&quot;&&quot;u&quot;&&quot;v&quot;&&quot;w&quot;&&quot;x&quot;&&quot;y&quot;&&quot;z&quot;

versus

Response.write &quot;abcdefghijklmnopqrstuvwxyz&quot;

wouldn't a proper test be
Response.write &quot;a&quot;&&quot;b&quot;&&quot;c&quot;&&quot;d&quot;&&quot;e&quot;&&quot;f&quot;&&quot;g&quot;&_
&quot;h&quot;&&quot;j&quot;&&quot;k&quot;&&quot;l&quot;&&quot;m&quot;&&quot;n&quot;&&quot;o&quot;&&quot;p&quot;&&quot;q&quot;&&quot;r&quot;&&quot;s&quot;&_
&quot;t&quot;&&quot;u&quot;&&quot;v&quot;&&quot;w&quot;&&quot;x&quot;&&quot;y&quot;&&quot;z&quot;

versus

Response.write &quot;a&quot;
Response.write &quot;b&quot;
Response.write &quot;c&quot;
Response.write &quot;d&quot;
Response.write &quot;e&quot;
Response.write &quot;f&quot;
Response.write &quot;g&quot;
Response.write &quot;h&quot;
Response.write &quot;i&quot;
Response.write &quot;j&quot;
Response.write &quot;k&quot;
Response.write &quot;l&quot;
Response.write &quot;m&quot;
Response.write &quot;n&quot;
Response.write &quot;o&quot;
Response.write &quot;p&quot;
Response.write &quot;q&quot;
Response.write &quot;r&quot;
Response.write &quot;s&quot;
Response.write &quot;t&quot;
Response.write &quot;u&quot;
Response.write &quot;v&quot;
Response.write &quot;w&quot;
Response.write &quot;x&quot;
Response.write &quot;y&quot;
Response.write &quot;z&quot;


I just don't see any relavance to gaining knowledge in teh example first noted. That would be like comparing opening a object to not opening a object

____________________________________________________
[sub]Python????? The other programming language you never thought of!
thread333-584700[/sub]
onpnt2.gif
 
It tells you the relationship between the time required by a response.write call and the time required to do about 26 concatenations. In VBScript, the time difference is more than 10 fold in favor of response.write. In Python, it may be different.

Maybe a better example would have been:

Code:
Response.write &quot;&quot;
versus
Code:
TempString = &quot;a&quot;&&quot;b&quot;&&quot;c&quot;&&quot;d&quot;&&quot;e&quot;&&quot;f&quot;&&quot;g&quot;&_
&quot;h&quot;&&quot;j&quot;&&quot;k&quot;&&quot;l&quot;&&quot;m&quot;&&quot;n&quot;&&quot;o&quot;&&quot;p&quot;&&quot;q&quot;&&quot;r&quot;&&quot;s&quot;&_
&quot;t&quot;&&quot;u&quot;&&quot;v&quot;&&quot;w&quot;&&quot;x&quot;&&quot;y&quot;&&quot;z&quot;

Shailesh
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top