August 2007 Posts

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