Scott Hanselman

NuGet Package of the Week - Courtesy Flush to flush buffers earlier and optimize time to first byte

November 06, 2014 Comment on this post [24] Posted in ASP.NET | ASP.NET MVC | NuGet | NuGetPOW
Sponsored By

Yes, really. It's got to be the best name for an open source library out there. It's a great double entendre and a great name for this useful little library. Perhaps English isn't your first language, so I'll just say that a courtesy flush gives the next person a fresh bowl. ;)

However, in the computer world "flushing a buffer" means forcing a buffer to be moved along, usually to a file or the network. Rather than holding data, you flush it, and move it along.

Nik from Glimpse has a small NuGet package called Courtesy Flush. He's got a good write-up on his blog.

image

It's a library to enable easier flushing of your buffer in ASP.NET MVC. From their site:

Why Flush Early?

Flushing early can provide performance improvements in web applications and has been a recomended best practice in the web performance community since 2007.

To find out more, check out Nik's blog where he covered the benefits of flushing early in two posts:

It builds on top of ASP.NET ActionFilters, which you can apply as attributes to your methods, or call within controllers.

Let's say that you have some server-side work that's very slow. That slow operation could hold up the rendering of your page until it completes. With a pre-flush like this you can get bytes onto the network and into the user's browser faster.

Here we render some information and get it out fast before we do something that's unavoidably slow.

public ActionResult About()
{
ViewBag.Title = DateTime.Now.Second;
this.FlushHead();

Thread.Sleep(2000);
ViewBag.Message = "Your application description page.";

return View();
}

Let's think about really specifically. It's important to know WHY you would want to do this and what exactly happens in the browser.

If you have a long running, but important process (we are pretending that Thread.Sleep(2000) is important) that takes 2 seconds, no HTML is sent to the browser. It's just waiting. The timeline looks like this:

Here we didn't flush

See that blue line? We waited almost 5 seconds for the /about page, and while we were waiting, no Javascript and no CSS were being loaded. Why not? How could the browser know if it isn't seen the <head> of your HTML?

For an optimization, we could FLUSH the buffers that we have up to this point, putting the HTML that we have so far onto the network.

The Layout.cshtml we have a call to @Html.FlushHead() to get the the _Head.cshtml out and into the hands of the browser. It might look like this:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@if (ViewBag.Description != null)
{
<meta name="description" content="@ViewBag.Description">
}
<title>@ViewBag.Title - My ASP.NET Application</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>

Here's what the SAME page looks like with the <head> flushed out first.

Here we flushed

Look closely at the timeline. Here, I'll do it for you...below shows when we flushed early versus just waiting.

See how when we flushed the head it gave the browser enough information to stat loading some css and javascript we had in the <head?> The whole page took 5 seconds, but when we flush at least we get things going early, while when we don't flush we can't do a thing until the long running task finishes.

See the difference?

See the difference? Now, to be clear, don't blindly go and add optimizations without reading the code and understanding what's going on, but this is a nice tool for our ASP.NET toolbox.

We may see a similar but even more powerful technique in ASP.NET vNext that includes async flushes at multiple points, while updating the model as data is available.


Sponsor: Big thanks to Aspose for sponsoring the feed this week! Working with Files? Aspose.Total for .NET has all the APIs you need to create, manipulate and convert Microsoft Office documents and many other formats in your applications. Start a free trial today.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service
November 06, 2014 14:43
The NuGet package contains a MVC filter which can apply flushing to all responses. I can't find any info on whether this should be applied to all responses or not and what the performance implications of doing that might be (perceived or not).

For those who wanted to know the details of how this 'Flushing' actually works. It sends the HTTP header 'Transfer-Encoding: chunked' instead of 'Content-Length:12345'. This allows MVC to send the head section of your HTML to the browser sooner, thus allowing the CSS to be loaded quicker (Although your CSS should be cached and so this will only affect the first request) and the title of the page to appear in the browser quicker.

Does anyone have real life experiences of using this package?
November 06, 2014 14:44
Maybe a stupid and unrelated question but how do I get those Nuget badges for embedding in my blog pages?
November 06, 2014 15:01
A few other things I've learned:

1. Chunked HTTP responses like this will be slightly larger, which makes sense as you are sending two HTTP responses rather than one. The following Stack Overflow question suggests this difference is in the region of 1-2%.
2. I'm not sure how caching will be affected by this. Presumably the browser caches both responses?
November 06, 2014 15:11
@Rehan: Caching shouldn't be affected as you're still sending one logical ASP.NET response.
November 06, 2014 15:13
... I should say *HTTP response*, which the browser will cache according to the usual parameters
November 06, 2014 15:31
Very interesting.
Thank you for sharing.
November 06, 2014 16:05
Now, to be clear, don't blindly go [snip]


Yeah, right. Not only will the retards copy your code verbatim without looking into the issue in any way, but when asked "why?" they'll reply "but Hanselman said so on his blog!".

I've lost faith in the developer "community" at large.

Sorry for the cynical perspective, your blog is great and I will definitely evaluate this technique before I implement it in any of my sites.
November 06, 2014 17:24
I tried this a few months ago on one of our slower web applications. Although the page was visually complete sooner, because the css at the top can be loader sooner, the perceived performance felt slower, because you are looking at an empty page for longer.

When I say empty, I mean the site header was loaded, but there is nothing interesting for the user to look at. Without flushing, as long as there is visual feedback for the user that a navigation is about to happen, it feels faster because there is less time spent looking at nothing.
Of course this is a little subjective. I do agree with Scott and the comments here, you need to review this for your system and decide what will be better / be perceived by your users.
November 06, 2014 23:55
I'm curious: Does async affect this at all? If my action method is async, will a flush still happen? Will the result be different depending on whether I flush before or after an await?
November 07, 2014 9:38
@Rehan - Examples and documentation on the Global filter can be seen in my blog post. Using the global filter makes sense in certain scenarios, particularly when all pages in the site follow a similar pattern.

As far as browser caching is concerned, nothing changes. The browser does still treats flushed content as one resource, and HTTP caching works the same as your used to.

@Stuart - You are correct. Always "Measure Twice, Cut Once" to make sure any optimization technique makes sense for your application. In your scenario, it sounds like flushing early is shedding light on a bigger performance problem that you may want to address as well.

@Daniel - This does not affect async at all. The two techniques are 100% compatible.
November 07, 2014 9:40
@Stuart - PS: You may want to check out my Full Stack Web Performance video for tips to improve your other performance issues.
November 08, 2014 1:24
How does this work with MVC areas e.g where you have one main masterpage with a header and therein sections that would be rendered "in" from another are layout page? What I want to do is to call an area with its layout page and add the less/scripts into the header in the master page with e.g section and then plush the head.

It did't seem to be possible to use sections in the _Head.cshtml with Flush without getting a compile error (.net 4.0/MVC 4)
November 08, 2014 2:44
It seems to me that if you are doing something that takes that long to render the page then you should refactor it to show the rest of the page and then use a Javascript AJAX call to pull in the slow-moving content after the fact. This will give the best experience.
November 10, 2014 5:10
Wow I wish I'd seen this a couple of years ago. Is there a flushing capability in Web Forms? I have a couple of legacy application I could speed up.
November 10, 2014 18:57
Thanks for the article, I'll definitely experiment with it!

And I spotted a typo: "a recomended best practice".
November 10, 2014 21:34
I think a "courtesy flush" is more about trying to minimize the stink out of respect for anyone else unlucky enough to be in the bathroom when you're blowing it up. Be kind to your neighbors in adjacent stalls. :)
November 11, 2014 16:17
I Implemented a similar system a while back...

https://github.com/atlascode/asyncsections

This is slightly different because it flushes the entire static content early then uses javascript to move the dynamic content into the correct position in the dom when it arrives.

This seems like a cool implementation, flushing just the head is definitely simpler to work with :-)
November 11, 2014 18:45
Good tool for the progressive enhancement toolbox. Show something so your user knows you are still there. Do it in a fine grain enough manner and they won't even notice a slowness problem.
November 11, 2014 22:26
@Sturla - CourtesyFlush should work fine with Areas. The code is simply asking the ViewEngine to resolve a partial view called _Head. If you want a different header in your section, you should just be able to add one there. If you run into problems, feel free to open an issue on the GitHub page.
November 12, 2014 14:34
Looking forward to the flushing directive shipping with Razor in ASP.NET vNext!
November 13, 2014 8:01
Interesting info. Thank you for sharing
November 16, 2014 5:22
If you have a long running, but important process (we are pretending that Thread.Sleep(2000) is important) that takes 2 seconds, no HTML is sent to the browser. It's just waiting. The timeline looks like this: Thank for share
November 18, 2014 16:07
Brilliant... !
Xin
December 17, 2014 14:37
Very interesting,thank you

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.