Geeks With Blogs
Mostly working... Adventures in Coding

Recently I looked at a performance problem when transferring image data from a custom HttpListener to web browser clients.

In the end it came down to one change I had done which was writing the image data to a memory stream first – this was required so I could add the right content-length header to the output based on the actual image size which some of my clients required. Before this change I had directly written the image to the output stream when it was generated but the modified code looked something like this:
 
using (var someImage = GenerateImageResource())
{
    using (MemoryStream ms = new MemoryStream())
    {
        someImage.WriteTo(ms, ImageFormat.Jpeg);
        response.ContentLength64 = ms.Length;
        ms.CopyTo(outputStream);
        outputStream.Flush();
        outputStream.Close();
    }
}
 
This looked like a straightforward change and initial testing proved it to provide the same performance as the original code – but only on Windows 7. When deployed on Window 2008 Server the performance dropped by a factor of 10 compared to directly writing the image output to the stream.
 
What happened here?
 
 
 
A closer look at what MemoryStream.CopyTo() actually does behind the scenes (using the free JustDecompile)  produced  the expected output – besides a bunch of error checking it was copying all the image data by reading it into a fixed size buffer and copying it to the output stream in chunks:
 
private void InternalCopyTo(Stream destination, int bufferSize)
{
     int num = 0;
    byte[] numArray = new byte[bufferSize];
 
    while (this.Read(numArray, 0, (int)numArray.Length) != 0)
    {
        destination.Write(numArray, 0, num);
    }
}
 
The drop off in performance can only mean that the target stream is handling the chunked transfer differently from writing the full array right away (which is what Image.Save() does internally). Diving into the depth of writing into the output stream of a HttpListenerResponse revealed many special cases involving IIS and gets quite complicated - but the point is this could and apparently is OS and platform dependent.
 
Switching to the optimized WriteTo() method in MemoryStream which is essentially equivalent to stream.Write(buffer,0, length) fixes the performance problem:
 
 
using (var someImage = GenerateImageResource())
{
    using (MemoryStream ms = new MemoryStream())
    {
        someImage.WriteTo(ms, ImageFormat.Jpeg);
        response.ContentLength64 = ms.Length;
        ms.WriteTo(outputStream);
        outputStream.Flush();
        outputStream.Close();
    }
}
 
 
Small change – big impact. If you have access to the full data you intend to send, pass it in one go – so the underlying stream can optimize how it sends data, this is especially important for a network stream.
Posted on Sunday, October 23, 2011 12:20 AM performance , C# , .NET | Back to top


Comments on this post: Writing to an HttpListenerResponse output stream: Small Change - Big Impact

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © mknapp | Powered by: GeeksWithBlogs.net