Blog

Blog: Don’t Forget to Flush

Don't Forget to FlushBy way of my favourite Bulgarian / Canadian / American / Web Ninja Stoyan Stefanov, and Yahoo!’s Exceptional Performance Team I’ve been studying the fine work found in their best practises guide for speeding up websites. As a recluse who prefers hiding behind servers rather than dancing around your web browser’s canvas, I was intrigued with their server side recommendations — however sparse they may be. In particular, flushing generated head content early to speed up overall page delivery and rending time was a technique new to me.

Flushing Chunks

HTTP 1.1 introduced a new mechanism for delivering hypertext that offered greater flexibility with the sending of a response body. Previous incarnations of HTTP only allowed for the demarcation of a complete response body by either specifying the content length or closing the server connection. HTTP 1.1 introduced a mechanism to transfer bodies better suited to dynamically generated content. Chunked transfer encoding allows for the demarcation of individual blocks of a complete message, which can therefore be delivered one piece at a time without knowledge of the complete body size in advance. This provides us with an opportunity to control the delivery of an HTML document piece by piece.

With the generation of dynamically generated content on the fly, we are introducing bottlenecks for content delivery that do not exist with static content. A connection to and querying of a database, complex computations, parsing of user input, etc. slow the server’s ability to deliver content. While the client’s browser is waiting on the server to finish (the next chunk of) content generation, it’s twiddling its thumbs unless we give it something to do.

Should we be able to push out the more easily generated content before the heavy lifting begins, we can occupy the client while it waits for the rest of the body. If that point of departure is the HTML head, the browser can busy itself with interpreting that while the body is being prepared. The HTML head typically contains various includes, such as CSS and Javascript. While the HTML body is being prepared, the browser can busy itself pulling in such assets.

PHP’s flush() function provides us with a tool to manipulate HTTP body delivery. Flush() “tries to push all the output [generated] so far to the user’s browser”. Depending on a web server’s capabilities and configuration flushing in PHP before body generation will result in the web server packaging up a chunk for delivery and pushing it out to the client.

Scenario

In order to play with this idea, a dummy is required for knocking around. The example scenario here is inspired: I want to display pretty colours palettes, but not just any — COLOURlovers top palettes pulled from their API. Since we are looking at the advantages of pushing out the HTML head early, the example requires a few properties:

  • The HTML head should include a few external files (CSS, Javascript) with some weight so we can look at how the browser behaves pulling these assets in.
  • The HTML body should have some (variable but controllable) processing heft so we can look at the relationship of server side document building bottlenecks and overall page delivery.

This is what we have, and this is what it looks like under the hood:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<html>
    <head>
        <script type="text/javascript">
            x = new Date();
            function loaded() {
                diff = new Date().getTime() - x.getTime();
                $("h1").append(" (" + diff + "ms load)");
                $("li").each(function() {
                    var text = $(this).text();
                    $(this).css("background-color", text).attr("title", text).text("");
                });
            }
        </script>
        <title>Random Colour Palettes</title>
        <link rel="stylesheet" href="style.css" type="text/css" media="all" />
        <script type="text/javascript" src="jquery.js"></script>
    </head>
    <body onload="loaded();">
        <h1>Top 20 Colour Palettes</h1>
 
        <?php $colours = simplexml_load_file('top.xml'); ?>
 
        <dl>
        <?php foreach ($colours->palette as $palette) { ?> 
            <dt><a href="<?php echo htmlspecialchars($palette->url, ENT_COMPAT, 'UTF-8') ?>"><?php echo htmlspecialchars($palette->title, ENT_COMPAT, 'UTF-8') ?></a></dt>
            <dd>
                <ol>
                    <?php foreach ($palette->colors->hex as $colour) { ?>
                        <li>#<?php echo htmlspecialchars($colour, ENT_COMPAT, 'UTF-8'); ?></li>
                    <?php } ?>
                </ol>
            </dd>
        <?php } ?>
        </dl>
    </body>
</html>

Besides the Javascript used to benchmark page load time (more on that below), the example:

  1. Pulls in an external style sheet.
  2. Pulls in the un-minified jQuery library (which has some good heft).
  3. Pumps out a list of the colour palettes — from a cached source to avoid benchmarking irregularities associated with API access.
  4. When done loading, a benchmarks is performed and jQuery is put to some good use.

For my testing, and working, environment I’m using Apache 2.2 with mod_php. Nothing fancy about my web server install and configuration. Mileage may vary with other web servers, and web server configurations.

The Benchmarks

To get an understanding of overall page delivery, there are two pieces of data that I collected:

  1. Response time from the server. Specifically, how long did it take the client to start receiving the requested page. This does not mean that the complete HTML document has been received by the client.
  2. Time to complete page load. How long did it take for all the page and page assets to be loaded.

To get the response time from initial request to initial receive, I used Firebug 1.2 for Firefox 3. Firebug’s network monitoring tool apparently does not record the complete request fulfilment time, but only from the point of request to the initial point of reception.

Once the page has been initially received, we record the client time ( x = new Date(); ). The onload event fires at the end of the document loading process allowing us to calculate the time to load the rest of the page including assets.

To figure out how much time it took for the complete page and assets to be loaded we can sum the response time from the server and our onload time (Response + Onload).

I did this benchmark several times taking averages, and using various configurations. Here is what I found:

Not To Flush, Or To Flush

Running the above scenario with no modifications (IE: no flush()ing) give us our baseline world view:

Straight Up - No Flush

Straight Up - No Flush

By adding a sleep() call for 1 and then 2 seconds right after the HTML head will allow us to artificially inflate the processing time for creating the body.

1
2
3
4
5
6
7
8
9
<html>
    <head>
        <!-- ... -->
    </head>
    <?php sleep(1); ?>
    <body onload="loaded();">
        <!-- ... -->
    </body>
</html>

By doing this, we can compare response and onload time in more processing heavy scenarios:

Straight Up - No Flush + Sleep

Straight Up - No Flush + Sleep

Benchmarks here show that onload time is roughly fixed regardless of the processing time to create and initially deliver the requested page. Flipping the request and onload times in the bar graph show this more clearly:

Straight Up - No Flush + Sleep (Reversed)

Straight Up - No Flush + Sleep (Reversed)

Onto the meat. Now let’s flush right before body generation like this:

1
2
3
4
5
6
7
8
9
10
11
12
<html>
    <head>
        <!-- ... -->
    </head>
    <?php 
        flush();
        sleep(1); 
    ?>
    <body onload="loaded();">
        <!-- ... -->
    </body>
</html>

And the benchmarks look like this:

Straight Up & Flush

Straight Up & Flush

Right out of the gate, we see little change of our base page total delivery time. However, with the flush() we are able to control and fix the initial response time from the server. As we grow the time required for the server to generate the body, we see some considerable savings. Specifically, a fixed saving of approximately 1 second.

By flushing early we are able to run a portion of server page processing in parallel with client processing, where previously client processing ran following server processing. This is a considerable efficiency in overall client and server page processing.

Caveats

While the general benefits of flushing content early, mileage may vary.

Tipping Point
Determining where to flush will depend on where your page transitions from simple data output to expensive processing and the client side "value" of the content considered for flushing early. If your head is expensive to generate, or contains no meat for the browser to work through, the benefits are lost.
Content Compression
Compressing content before delivery is another important performance strategy. However, server side compression may not mix with flushing content. mod_gzip for Apache 1.x does not support chunking, requiring chunked content to be dechunked before it can be compressed. mod_deflate for Apache 2+ does not suffer from such limitations.
Pooling
Like mod_gzip, any layer between the generated content and the client that pools data will undermine the value of early flushing. PHP output buffering and page caching (including reverse proxies) are examples of practices which may interfere with flushing.
Nested Templates
A common approach to data templating is to wrap a body template in a site template, for example Zend_Layout. While this approach has several advantages for maintaining consistent layout, it will undermine most if not all of the value around flushing early.

Tags: , ,

7 Responses to “Don’t Forget to Flush”

  1. ellisgl says:

    Beware of using flush by itself. Some times you have to make sure that you have enough data output before the browser response. Here’s something I’ve been using with my COMET stuff.

    function RealFlush($Out = “”, $Padding = 4096)
    {
    echo str_pad($Out, $Padding);
    @ob_flush();
    @flush();
    }

  2. Loïc Hoguin says:

    About nested templates, I’m not sure about ZF but you could probably have a header template that you would create and render as soon as you receive the request, and then have a body template that would contain the rest. The HTML header usually doesn’t have too much specific code so it should work out pretty well for most projects.

  3. Mikael says:

    I love the idea, but that is probably not gonna work in most MVC frameworks that implement the ‘two-step’ view way of rendering views.

    As an example with ZF, as Loïc mentionned: if you use Zend_Dojo or Zendx_jQuery in conjunction with Zend_Layout, you don’t build the HEAD content in one single spot in your code. It is done in several places, and this makes it hard to use a flush.

    Example:
    - Take a random, database-heavy page that is included in a default layout (= template). This is where most of the computing time is spent. That page then decides it needs some obscure plugin js file. That page is rendered. So far you can’t send anything to the client.
    - Then the layout decides that it needs a general use JS library. The layout is rendered and it then includes (wraps) the previously rendered page.
    All work is done, the full response (head + body) are computed and can be sent back to the client. Nowhere in this scenario could you do a flush.

    I’d love to hear a solution to this (or maybe I’m plain wrong ?), because this sounds like it could really speed up JS heavy webpages.

  4. Mikael says:

    Whoops, missed the last paragraph… sorry !

  5. [...] this recent post to his blog Michael Caplan looks at a feature of PHP that’s sometimes forgotten when pushing [...]

  6. OnLine says:

    Hi
    For example, if in the HEAD section i have a javascript with an window.onload instruction that selects some html elements from body, then i call flush() before the BODY.
    This will send the HEAD code to browser, but if the BODY isn’t added in that moment, doesn’t it affect the JavaScropt code.
    Or, the window.onload will know and wait till the whole html page is loaded?

  7. On load event handlers will only be processed after the complete page has been loaded. jQuery’s .ready() (http://api.jquery.com/ready/) method is probably a better choice if you are just interacting with DOM nodes.

Leave a Reply

You must be logged in to post a comment.