locked
Poor performance using handlers to load images - possible cause are HttpModules? RRS feed

  • Question

  • User768014394 posted

    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 page.

    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();
    	stopwatch.Start();
    
    	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";
    	response.BinaryWrite(imageBytes);
    
    	stopwatch.Stop();
    
    	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

    HTTP Error 500.19 - Internal Server Error

    The requested page cannot be accessed because the related configuration data for the page is invalid.

    <fieldset><legend>Detailed Error Information</legend>
    Module IIS Web Core
    Notification BeginRequest
    Handler Not yet determined
    Error Code 0x80070021
    Config Error Lock violation
    Config File \\?\c:...\web.config
    Requested URL http://.../Default.aspx
    Physical Path c:\...\Default.aspx
    Logon Method Not yet determined
    Logon User Not yet determined
    </fieldset>
    <fieldset><legend>Config Source</legend>
     136: <modules>  137: <clear /> 138: <remove name="ScriptModule" /> 
    </fieldset>

    Does this argument seem valid?  How can I remove the modules without having to do them one-by-one?

    Tuesday, October 18, 2011 2:25 PM

Answers

  • User1622957740 posted

    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 ---

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, October 20, 2011 3:12 AM
  • User768014394 posted

    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.

     

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, October 20, 2011 11:30 AM

All replies

  • User1622957740 posted

    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 ---

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, October 20, 2011 3:12 AM
  • User768014394 posted

    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.

     

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, October 20, 2011 11:30 AM