In Joomla! we have a rendering tool called JLayout
which allows us to prioritise folders to search for a specific layout. An external class/template engine that receives data and renders it.
In the last months we discovered that using JLayout
we were able to cover all those parts of Joomla! that are still not overridable. While that's a great advantage as tool I was fearing the performance loss in using such way of render layouts compared to the hardcoded html .= "".
As JLayout checks if a layout file exists in the list of include paths I was mainly worried about being doing too many filesystem accesses. In my first contribution to JLayout I did some basic benchmarking so I was sure that the system was ok. But now we are using layouts everywhere and expanding it so what will be the real impact when the system is heavily load of layouts?
My first step was to improve the great Joomla debug console to be able to track layouts file accesses:
Joomla has always of <20 layouts per view. So 0,33s for 1000 layouts was something reasonable but when doing my tests with a single layout I realised that makes no sense searching for a file 1000 times when the include paths are the same (you already searched on them the same layout). So I added a cache system (it's turned off in the previous capture) to avoid that duplicated file searches:
Which is better. Of course pages are not a single layout rendered 1000 times but the cache system is going to help for sure. I wanted to measure its benefits so I tweaked debug console again to show the detail of layouts shown and the times they were rendered in the current view.
This is an example in the default blog view:
The non-cached version loads in an average of 0.004 seconds. One milisecond improvement is not a lot but it will help on views where layouts are repeated. For example, we are introducing layouts on form fields. A form with duplicated field types (like forms with various "text" fields) will use the new cache system.
As I had to create timers on the renderer I asked myself "why not track other information like the total rendering time and the partial rendering time for each layout?". It's cool to know how much time takes you to render each part of your website. So I added the rendering time tracking system:
Rendering all the layouts in the blog page takes ~0,03s. For example here we can ask ourselves why block.php takes so much time compared to other layouts. Answer is easy it's the main info layout and contains 7 sublayouts calls inside. We are starting to make use of this!
Next thing was test the rendering times in views with a lot of layouts. I used my first test: render an article tag layout x times. It's important to notice that this was done creating always a new instance of the class.
This is the sample code:
<?php
for ($j = 0; $j < $maxRenders; $j++)
{
$layout = new JLayoutFile('joomla.content.tags');
$layout->render($this->item->tags->itemTags);
}
?>
Where I could change the $maxRenders
value. My first try was with 1000 items:
2.6 seconds in rendering 1000 times the same layout. I was not sure if it was too much so I needed to compare it with the old way of doing things (embed the tag rendering in the view template itself):
<?php $startTime = microtime(true); ?>
<?php ob_start(); ?>
<?php for ($j = 0; $j < $maxRenders; $j++) :?>
<div class="tags">
<?php foreach ($this->item->tags->itemTags as $i => $tag) : ?>
<?php if (in_array($tag->access, JAccess::getAuthorisedViewLevels(JFactory::getUser()->get('id')))) : ?>
<?php $tagParams = new JRegistry($tag->params); ?>
<?php $link_class = $tagParams->get('tag_link_class', 'label label-info'); ?>
<span class="tag-<?php echo $tag->tag_id; ?> tag-list<?php echo $i ?>">
<a href="/<?php echo JRoute::_(TagsHelperRoute::getTagRoute($tag->tag_id . '-' . $tag->alias)) ?>" class="<?php echo $link_class; ?>">
<?php echo $this->escape($tag->title); ?>
</a>
</span>
<?php endif; ?>
<?php endforeach; ?>
</div>
<?php endfor; ?>
<?php ob_end_clean(); ?>
<?php $timeEmbed = (microtime(true) - $startTime); ?>
And finally I also wanted to know if really the file loading was affecting so I added the old way to use subtemplate in the main template. That is basically when you include the external file in the current file (without an external class like JLayout). Sample code in the view would be something like:
// Start capturing output into a buffer
ob_start();// Include the requested template filename in the local scope
// (this will execute the view logic).
include $this->_template;// Done with the requested template; get the buffer and
// clear it.
$this->_output = ob_get_contents();
ob_end_clean();
You are embeding a remote file but you avoid to create an instance of a class & passing it vars.... The code for benchmarking with the call to the loadTemplate function is:
<?php $startTime = microtime(true); ?>
<?php ob_start(); ?>
<?php for ($j = 0; $j < $maxRenders; $j++) :?>
<?php $this->loadTemplate('tags'); ?>
<?php endfor; ?>
<?php ob_end_clean(); ?>
<?php $loadTemplate = (microtime(true) - $startTime); ?>
So we are testing 3 rendering methods:
- Embed code in the main template
- Use an external subtemplate and include it directly on the main template (
loadTemplate()
) - Call an external renderer that receives de data and include paths and decides which layout to render (
JLayout
)
The results of my benchmarks are in the next chart. Each point is an average of 50 real measures:
Ironically the access to the file system is not a problem (both JLayout & loadTemplate access external files). It's the external class access and external rendering what really increases the page load on huge amount of data. As a general rule we can say that each +100 layouts addition increments 0.1s the page loading.
Conclusion
Using a rendering engine like JLayout can kill my page? I don't think so. As I told you the number of layout loaded in Joomla are always less than 20. Even loading all the layouts inside the Joomla layouts folder (87 files) in the same view our page load only would be increased in less than 0.1s.
So JLayout results are acceptable if you consider the benefits of the template engine:
- Reuse layouts
- Override/prioritise layouts
- Get layout loading stats (hard with embed code and easy with an external library)
Of course we have to watch of the number of layouts loaded (now is easier with the layouts debug) but we are in really good numbers.
For Joomla I'd keep the plan of using layouts to render all the pending stuff (plugins, fields, etc.). We can ignore things like the javascript code inside JHtml
calls because even not being overridable we can directly skip the call itself. If I have a field that calls JHtml::_('bootstrap.tooltip');
I can directly override it and then replace/remove the JHtml
call.