While playing a bit with Xdebug (a profiling tool for PHP), I've come across something I didn't know and that I frankly found a bit odd : how to use output buffering (PHP function ob_start()) to speed up some stuff.
In a few words, just so you know if this is obvious noob stuff for you and you can skip this article :
-
ob_start();
-
echo a lot of stuff;
-
$result = ob_get_contents();
can be a lot faster than a straight
-
$result = a lot of stuff
Say you have a very complex routine that errrr... prints a string with a lot of 'a'.
Straight and simple code would be :
-
$s = '';
-
for ($i=0; $i <$loops; $i++) $s .= 'a';
Now, with output bufferization you could make it like :
-
$s = '';
-
ob_start();
-
for ($i=0; $i <$loops; $i++) echo 'a';
-
$s = ob_get_contents();
-
ob_end_clean();
Let's benchmark a bit these two snippets :
-
<?php
-
$iterations = 50000;
-
-
function straight($loops = 100) {
-
for ($i=0; $i <$loops; $i++) $s .= 'a';
-
return $s;
-
}
-
-
function buffer($loops = 100) {
-
ob_start();
-
for ($i=0; $i <$loops; $i++) echo 'a';
-
$s = ob_get_contents();
-
ob_end_clean();
-
return $s;
-
}
-
-
$lotsofa1 = straight($iterations);
-
$lotsofa2 = buffer($iterations);
-
?>
Results : on my test machine, straight code took about 60 ms to execute while buffered code took 50 ms. Holy beep, the weirdo syntax is 15% faster !? I guess you save time by manipulating once the result variable, when you get the buffer content, but that was quite surprising for my PHP beginner self.
While I was at it, I pushed it a bit further and tried to think about interesting ways to use this. I thought : maybe it's faster for example to include() a file into a buffer than simply using file_get_content() ?
To test this, I used the following :
-
<?php
-
-
function bench_readfile($file) {
-
ob_start();
-
readfile($file);
-
$s = ob_get_contents();
-
ob_end_clean();
-
return $s;
-
}
-
-
function bench_filegetcontents($file) {
-
return file_get_contents($file);
-
}
-
-
function bench_include($file) {
-
ob_start();
-
include($file);
-
$s = ob_get_contents();
-
ob_end_clean();
-
return $s;
-
}
-
-
$content1 = bench_readfile('file1.txt');
-
$content2 = bench_filegetcontents('file2.txt');
-
$content3 = bench_include('file3.txt');
-
-
?>
Files file1.txt, file2.txt and file3.txt were copies of the same file, but I wanted to be sure that no function would benefit from cached data.
Results : the include() block took 51 ms to execute, readfile() took 7.3 ms and file_get_contents() won it in just 2 ms.
Conclusion : Well, no surprise here. Hopefully built-in function are sometimes optimized enough :Þ
commented, on 10/May/06 at 10:02 am # :
Good to know I'm not alone in this profiling and discovering stuff thing in PHP. It's only now that I actually know what ob_start() is for. All the while I thought ob stands for object. Oh, well.
wrote, on 19/Jun/06 at 2:11 pm # :
hi
Can we pass buffer to the readfile() function
eg. readfile($buffer) //where $buffer is a output buffer got from
$buffer=ob_get_contents();
can it(readfile()) also be used to push contents of buffer to output(brower) any other ...
commented, on 23/Jul/07 at 12:02 am # :
ob_start is great -- it's also a great way to handle exceptions: if something throws an exception while you are filling your buffer, you can print custom error pages, etc.
thanks for the writeup.
commented, on 02/Dec/07 at 7:58 pm # :
In rare cases it can be very bad, especially with shared hosting with limited RAM given for PHP.
Also if script crashes somewhere between ob_start and ob_get_contents there won't be any output at all, so user will see blank page.
but OB still lovely thing :)