PHP
August 11, 2007
Ladies and Gentleman! Welcome to the PHP Templating Celebrity Deathmatch!
I actually do like the idea behind templating. I know there are varying
arguments about whether or not templating is appropriate for PHP, though those
are not the focus of this entry.
The big idea behind templating is separation of
concerns, that is,
breaking a program into parts that are easily manageable and don’t overlap in
functionality. In an ideal world, templating would provide the added advantage
of allowing a programmer to be a programmer and not a web designer - and
allowing a web designer to be a web designer and not a programmer - by keeping
the logic underlying the presentation layer to a minimum. However, I have never
found this to be true in any project I’ve worked on in my professional career.
One of the big benefits, as far as I see, is that it makes code much easier to
read. This may not be true for everyone, but I would much rather be confronted
with smooth, separated templated code rather than a jumbled PHP mess. It’s
easier to read and far, far easier to adapt and change.
While I was attending OSCON a few
weeks ago, I heard mention of a new PHP templating engine that was written in C
and native compiled into a PHP extension. This would make it much, much faster
than anything written in PHP itself - in theory. This project, called Blitz, was
making some pretty grand claims on their website, so I wanted to put them to the
test - at least a small timing test.
In this test, I am going to be comparing Smarty (the
most widely used PHP templating engine and an official PHP project),
Blitz (a new templating engine
currently under very active development that is native compiled as a PHP
extension), and standard PHP includes.
For the purposes of this test, I wrote a quick timing function that uses
microtime() to record how much time has elapsed between each call of
mark_time(). The code is available in the accompanying project.
A Note About The Tests
These are not meant to be exhastive tests by any means. These tests are just
designed to give you 5,000 foot overview of the current state of PHP templating.
They only evaluate page generation time and not other metrics such as CPU load,
IO load, or memory usage. Furthermore, I selected three scenarios that I have
commonly used in templating; there may be some scenarios that I haven’t tested
where one method may outperform others. And, as with any benchmarking, they are
dependent on my system - YMMV.
Test 1: Instantiation
This is a simple test that determines how much time it takes to power up the
templating engine and get it loaded into memory for PHP to use. For the purposes
of this test, we will just be comparing Smarty and Blitz, as there is no need
for instantiation with a standard PHP include. We’ll start with Smarty first.
smarty_instantiation.php
<?php
echo mark_time()."<br>";
include "Smarty.class.php";
$smarty = new Smarty;
echo mark_time()."<br>";
?>
Smarty’s instantiation time was 0.0058109760284424 or 0.005 seconds in human
terms.
blitz_instantiation.php:
<?php
echo mark_time()."<br>";
$blitz = new Blitz;
echo mark_time()."<br>";
?>
Blitz’s instantiation time was 3.0994415283203E-5, or 0.00003 seconds in human
terms.
It may not seem like a big difference, but this is one area where having Blitz
as a PHP extension makes a huge difference over Smarty being written in PHP
and included. Because PHP must traverse the include_path to find Smarty.class
before including it, it causes PHP to be slowed down before it can even
instantate the Smarty object. To be fair, I decided to run a second test again
with the include out of the timing mark.
smarty_instantiation2.php
<?php
echo mark_time()."<br>";
$smarty = new Smarty;
echo mark_time()."<br>";
?>
Even without having to search the include_path for Smarty, it still took
6.5088272094727E-5, or 0.00007 seconds to instantate the Smarty object - almost
twice as long as it took to instantate the Blitz object. However, this is not a
realistic scenario in any way - there is no way that PHP can have saved any time
and still have access to the Smarty object!
Winner: Blitz
Test 2: Simple Template Rendering
In this test, we will be comparing simple template rendering in Blitz, Smarty
and PHP includes. In this test we will create a simple HTML template with two
variables that need to be replaced, then render and display them to the user
using each engine or, in the case of PHP, straight PHP. So, let’s get started!
We’ll run Blitz first, since it won the previous test.
blitz_simple_render.php
<?php
echo mark_time()."<br>";
$blitz = new Blitz('blitz_simple_render.tpl');
echo $blitz->parse(array(
'title' => "Blitz Test!",
'body' => "Blah foo! I'm a body!"
));
echo mark_time()."<br>";
?>
Blitz took an impressive 0.00011801719665527, or 0.0001 to render a simple HTML
document with two replaces. Smarty’s next:
smarty_simple_render.php
<?php
echo mark_time()."<br>";
include "Smarty.class.php";
$smarty = new Smarty;
$smarty->assign('title',"Smarty Test!");
$smarty->assign('body',"Blah foo! I'm a body!");
$smarty->display('smarty_simple_render.tpl');
echo mark_time()."<br>";
?>
Because Smarty is a compiling engine (it compiles the templates to PHP and
caches them), the first run is always the most costly - in this case, an
atrocious 0.058284997940063 or 0.06. Even on subsequent runs, 0.0065691471099854
or 0.007, again much slower than Blitz. Finally, standard PHP includes:
php_simple_render.php
<?php
echo mark_time()."<br>";
$title="PHP Test!";
$body="Blah foo! I'm a body!";
include "php_simple_render.tpl.php";
echo mark_time()."<br>";
?>
Surprisingly, standard PHP includes took 0.00030016899108887, or 0.0003 seconds,
much faster than Smarty, but three times as slow as Blitz. Once again, this
likely has to do with PHP having to traverse the include_path before finding the
appropriate file. If you specify the _absolute path on the filesystem _to the
file above, the time took becomes 0.00010490417480469, or 0.0001, roughly equal
to Blitz on any given run. However, because Blitz is able to parse the template
with a minimum of fuss whereas I have to explicitly specify the filesystem path
for PHP to get equal performance, this round also goes to Blitz.
Winner: Blitz
Test 3: Complex Templating
In this case, we are going to be doing complex templating. This test includes
three template-based includes, one foreach loop over an array, and a large array
of generated data. Just for the curious, the generation of the data is not going
to be counted towards the timing. In this case, we have generated a 10,000 item
array and are going to have each engine iterate over it.
blitz_complex_render.php
<?php
echo mark_time()."<br>";
$blitz = new Blitz('blitz_complex_render.tpl');
foreach($arr as $array) {
$blitz->block('master_loop',array(
'id' => $array['id'],
'id1' => $array['id+1']
));
}
echo $blitz->parse(array(
'title' => "Blitz Complex Render text"
));
echo mark_time()."<br>";
?>
Blitz ran the test in 0.072134971618652, or 0.07 seconds, not too shabby
considering it had to iterate over a 10,000 item multidimensional array.
smarty_complex_render.php
<?php
echo mark_time()."<br>";
include "Smarty.class.php";
$smarty=new Smarty();
$smarty->assign('title',"Smarty Complex Render test");
$smarty->assign('arr',$arr);
$smarty->display('smarty_complex_render.tpl');
echo mark_time()."<br>";
?>
Again, because Smarty is a compiling engine, the first run is always the most
expensive - in this case, a whopping 0.31642484664917, or 0.3 seconds.
Subsequent runs fell in the range of 0.099456838607788, or 0.1 seconds, three
times as fast as the first run but still slower than Blitz. Finally, standard
PHP includes:
php_complex_render.php
<?php
echo mark_time()."<br>";
include "php_complex_render.tpl.php";
echo mark_time()."<br>";
?>
In this test, raw PHP includes came in at 0.055343866348267, or 0.06, the
fastest of all and yet just a small bit faster than Blitz.
Winner: PHP
Conclusion
Blitz won two of the three tests and came in a close second in the last. Of
course, one could argue that PHP “won” the first test since there was no need to
be tested on instantiation.
Considering the short amount of time Blitz has been under active development,
its sheer speed is rather amazing. From a templating standpoint, Blitz is the
fastest unless you are willing to jump through lots of little hoops to make
standard PHP includes work for you, and even at that point, the performance as
far as total page generation time goes is roughly equal, though native PHP may
have a slight advantage.
However, unfortunately, the very strength of Blitz (it being written in C and
compiled into a PHP extension) is its greatest weakness. Because so many
websites are served off shared hosts without the ability of users to use
external extensions, most of the community will never have the ability to take
advantage of Blitz. Only those with access to the machine, or more specifically
the php.ini file, will have the ability to use Blitz unless it were to be merged
into the PHP tree. Even in the best case, considering how many shared hosts are
still running PHP4, I wouldn’t expect to see anything like this soon, if ever.
Perversely, the very weakness of Smarty (that it is written in PHP and included)
is its strength, for the reasons above. Smarty is the slowest templating engine
tested, however because it is just PHP, it can be included and run like any
other PHP script - meaning all the people on shared hosting can make use of it
with a minimum of fuss. And in Smarty’s defense, there are many features (such
as template variable modifiers) Smarty has that are simply not available in
Blitz. These features come with the tradeoff of a massive loss in speed. It was
honestly surprising to me how slow it was.
Ultimately, it is the decision of the programmer as to what is the right method
to use. If you want the advantages of templating as far as seperation of
concerns and ease of maintenance and you have the ability, Blitz is probably a
good choice for you. If you still want the ease of maintenance and separation of
concerns provided by templating and are willing to make the tradeoff for a
massive loss of speed, Smarty is a possibility also. If sheer pure speed is your
primary concern and you’re not willing to make any kind of tradeoffs, going with
raw PHP is probably your best option provided you fine tune it a bit to get the
absolute best performance out of it.
Read More