Last post Oct 20, 2011 11:30 AM by desertfoxaz
Oct 18, 2011 02:25 PM|desertfoxaz|LINK
I have an ASP.NET 3.5 application which uses HttpHandlers to retrieve images to be displayed on a web page. Our users have complained that this is too slow so I've put some tracing in place to track the time from the start to the end of the ProcessRequest
method in my handler using System.Diagnostics.Stopwatch.
According to this my handlers are taking only about 100 milliseconds yet when the images (there are four on the page) are rendered on the page they appear to be very slow to load. Basically it's taking about 2 seconds for all four images to display on the
Looking at the IIS logs however, my handlers are showing a significantly longer time (sometimes up to 1600 milliseconds) to process a request. While the code that grabs the image seems to be very quick, something seems to be adding a delay within the requests
since we're seeing this discrepancy between my timing and the IIS logs.
Bascially, my handler code looks like this:
public void ProcessRequest(HttpContext context)
Stopwatch stopwatch = new Stopwatch();
byte imageBytes = File.ReadAllBytes(...); HttpResponse response = context.Response; response.AppendHeader("Content-Length", imageBytes.Length.ToString());
response.AppendHeader("Content-Disposition", string.Format("inline; fileName=image.jpg"));
response.ContentType = "image/jpg";
double elasped = stopwatch.Elapsed.TotalMilliseconds;
Debug.WriteLine("elasped time (" + queryString["pos"] + "): " + elasped.ToString());
I am starting to wonder if any of the HttpModules loaded for the application are adding overhead. We are currently using FormsAuthentication and the System.Web.Handlers.ScriptModule but when I look at the site in IIS I see a bunch of inherited modules. However,
if I use a <clear /> element I can't debug, and if I try to run without debugging I get an error screen saying
136: <modules> 137: <clear /> 138: <remove name="ScriptModule" />
Does this argument seem valid? How can I remove the modules without having to do them one-by-one?
Oct 20, 2011 03:12 AM|rstrahl|LINK
It's possible that modules are the cause for your delay, but understand that loading images from code compared to letting IIS natively serve and cache images is always going to be slower than that native mechanism (which doesn't hit manage code and in many
cases uses the IIS cache).
Also you shouldn't load files to memory especially if the images are sizable. If possible use Response.TransmitFile() which is very efficient as IIS actually writes out the file internally and handles caching etc almos the same way as a file accessed directly.
Unfortunately TransmitFile works only in your virtual path tree and not with files loaded from arbitrary paths on disk.
If that's not possible read the file using a read-only stream and stream it directly to the Response.OutputStream. Reading into memory first then serving is fairly slow. The loading may also run you into file locks if multiple requests are trying to read
the same file at the same time.
If the problem is really slow modules holding up the requests: Another option is that you can actually serve the images from a Module in the early part of a request. You could potentially implement a BeginRequest module handler, serve your image and then
HttpApplication.EndRequest() to terminate the request at that point. That way it would serve early in the pipeline and not fire most of the subsequent modules.
Hope this helps,
+++ Rick ---
Oct 20, 2011 11:30 AM|desertfoxaz|LINK
I had tried removing the modules one-by-one in the web.config but it seems most of the ones listed in IIS (including the inherited ones) were needed for the application to run.
Yesterday I had determined that the problem was that my handlers were implementing IRequiresSessionState. I am using session state, but only to read data, not to write to it. I discovered that there is an altenate interface, IReadOnlySessionState and once
I started using that performance improved significantly. It had always appeared that the images were being retrieved sequentially instead of in parallel and this turned out to be due to the implementation of the IRequiresSessionState interface. Unfortunately
this behavior isn't mentioned in the MSDN documentation so I had to Google it to find out this was the problem and why it was.