Strings vs Output Buffering in PHP

When working with programming project, both large and small, it is a good technique to split data representation and output into separate parts of the program. For website content management systems the section of code that generates the actual output of the page is commonly called a template, theme or skin engine. These all work in the same basic way, they receive a data stream and output HTML code. Now, in PHP there are multiple ways of parsing strings so this means that the data stream can be converted in multiple ways. Which way is the best way?

I did a little bit of research in this area to find out if it was more efficient to let the template engine output the HTML code directly and capture it using output buffering to make a cache of it or to make the engine generate a string that could be cached directly and also sent to the visitor. Here are my findings.

Warning: The benchmark below was conducted on a PHP 4.3/Apache web-server over a year ago. As this version of PHP is now outdated it should be worth noting that the string concatenation algorithms in the newer versions of PHP could have been improved since. Please keep this in mind when reading this article.

The script

<PHP>
 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// So PHP won't kill the script prematurely
set_time_limit(60000);

// Set and get the execution time in seconds
function getExecutionTime($dp = 4, $reset = false)
{
    global $_executionTimer;
    
    if($reset)
    {
        list($usec, $sec) = explode(' ', microtime());
        $_executionTimer = $usec + $sec;
        return true;
    }
    
    list($usec, $sec) = explode(' ', microtime());
    return round(($usec + $sec) - $_executionTimer, $dp);
}

// Get the average of an array
function getAverage($input)
{
    $total = 0;
    
    foreach($input as $value)
        $total += $value;
    
    return round($total / count($input), 4);
}

//======================================================================
// Do benchmark

// Which test are we doing?
//     true  = output buffering
//     false = strings
// As we are testing the execution time of each method relative
// to each other it is safe to "waste" resources during the
// benchmark loop with an unneeded condition.
$test = true;

// How many times will we append the string to the test object?
$times  = 100000;
$append = 'abcdefghijklmnopqrstuvwxyz ';

// Where we dump the results
$results = array();

// Loop three times
for($l = 0; $l < 3; $l++)
{
    // Reset timer
    getExecutionTime(0, true);
    
    // Nullify the string from previous tests
    $string = null;
    
    // Start buffering if needed
    if($test)
        ob_start();
    
    // Do the actual test
    for($i = 0; $i < $times; $i++)
    {
        if($test)
            echo $append;
        else
            $string .= $append;
    }
    
    // Send the result to a string if needed
    if($test)
    {
        $string = ob_get_contents();
        ob_end_clean();
    }
    
    // Retrieve and output the execution time
    $results[$l] = getExecutionTime();
    echo $results[$l].' seconds<br />';
}

// Output the average
echo "<br /><strong>$times iterations: "
   . getAverage($results).' seconds</strong>';

The results

Iterations Output Buffering (Seconds) String Concatenation (Seconds)
100,0000.55171.3757
150,0001.29333.1144
200,0002.25875.5305
300,0005.047712.3593
400,0008.870221.8937
500,00013.779834.2161
750,00030.6121 -

It can easily be seen that output buffering is around twice as efficient over concatenation to a single string in the PHP world. However does this mean that we should stay away from using string concatenation all together? Of course if doesn’t. Take the following code for example:

<PHP>
1
echo 'Hello '.$name.'. How are you?';

By using a similar benchmark script as the one above it has been determined that it is still faster than the separating the single command over several like this:

<PHP>
1
2
3
echo 'Hello ';
echo $name;
echo '. How are you?';

This is because the PHP interpreter needs to execute multiple commands instead of just one. The difference however is very small, somewhere less than 1%, so it doesn’t really matter what method you use but unless you find it easier to write using multiple commands instead of using string concatenation it is still recommended to use the single command.

Two other interesting findings were that the first loop in the benchmarking script was always faster than the ones that follow and it made no difference in execution time whether the appended string was stored in a variable or not.

The conclusion

Think your template systems out before you actually write it. Decide if you actually need the output in a string or if you can get away with an unparsed output. If you need to parse any large amounts of strings make use of output buffering, your choice could mean the difference between visitors getting a pretty website or a “connection timeout” message.

Comments

Now usually this is the place where you can submit your own reactions the the stuff I talked about above but due to time issues and my lazy personality I haven’t actually written the comment system yet. I will eventually get around to finishing it once I get my reader count back up to what it was in the early days but until then feel free to contact me with your response.