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';[/php]
- Now, with output bufferization you could make it like :
- [php]$s = '';
- ob_start();
- for ($i=0; $i < $loops; $i++) echo 'a';
- $s = ob_get_contents();
- ob_end_clean();[/php]
- Let's benchmark a bit these two snippets :
- [php]<?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 :Þ
Shorter URL
Want to share or tweet this post? Please use this short URL: http://ozh.in/cz
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.
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 …
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.
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 :)
OK, so this is a ~4 year old post, so I feel sort of silly commenting on it, but since you linked to it on Twitter I thought I'd throw my own two cents into the interpretation of the outcome.
In your first example, the reason buffering performs better is not because output buffering in itself is more efficient. in fact, the opposite is generally true, especially when memory consumption is considered.
The reason the first example ran faster with output buffering is because of the way string concatenation works in PHP (and most other languages). Strings aren't mutable, so when you concatenate two strings, a completely new string is created in memory and the contents of the other two strings are copped into the new string. Do this several thousand times and you can start to see the inefficiency that this causes.
Most other languages offer a "string buffering" classes like StringBuffer in Java and StringBuilder in C#. As far as I know, PHP doesn't offer a facility like this, so using output buffering could be the way to go, but only in this specific case (where you're concatenating lots of strings). In general, output buffering will be much slower, as you saw in your other example.
I'd also note that the reason the 'include' method was so much slower in the second example because it was executed in the PHP interpreter context, which goes MUCH slower than simply reading in the bytes (because PHP has to actually evaluate the file to see if there is any code that needs to be executed).
At any rate, those are my thoughts on output buffering. There are certainly places where it is useful, but in general it doesn't improve performance at all.
Will Anderson » hey Will thanks for commenting on this 4 yo post :) Yeah, my interpretation was something similar, but for some reason (at that time at least) I was a bit scared by OB, it seemed to me that would be a slow beast.