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.
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>';
|
| Iterations | Output Buffering (Seconds) | String Concatenation (Seconds) |
|---|---|---|
| 100,000 | 0.5517 | 1.3757 |
| 150,000 | 1.2933 | 3.1144 |
| 200,000 | 2.2587 | 5.5305 |
| 300,000 | 5.0477 | 12.3593 |
| 400,000 | 8.8702 | 21.8937 |
| 500,000 | 13.7798 | 34.2161 |
| 750,000 | 30.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:
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:
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.
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
30th November, 2008Michael Razzano
I know this is kind of an old post, but oh well… I’m a bit curious where exactly the overhead is… I suspect a good deal of it might actually be the numerous system calls buried in the echo rather than the concatenation itself. “To buffer or not to buffer” is still something people should be thinking about, though, and the performance gain is pretty obvious.
Anyhow, there’s also another benefit to using straight output instead of buffering—failures. If you are running an involved CMS script with lots of modules, it’s probably not a good thing if one module toward the end of the code fails before you output your buffer. If your CMS is based off a database—as most are nowadays—it’s not unusual too have too many connections and have your stream interrupted. As a fellow MAL user, you’ve probably noticed long anime lists not finish rendering due to this occasionally; it’s probably better to get most of their list and see it as it slowly crawls through the display loop than to wait forever looking at nothing only to finally get a blank page from a minor error in the last few lines.
I suppose these errors should be properly handled, but they’re slippery sometimes. Apache might die somewhere while serving the page even if your code is flawless.
In short… for simple sites, always buffer… for big database-driven behemoths with 20+ custom modules per page, buffering is probably more of a death wish than a benefit for the user—even it it’s faster to process as a whole.
Have your say