Chapter 9
Smarty Caching

In this section the PHP scripts will be tuned using the Smarty caching feature (introduced in Section 4.4).

9.1 Considerations

This testing method is different from those described before. Based on the knowledge of the available tools used until now we will modify the scripts to receive the best results.

The tool Smarty also provides compiling and caching functionality. These abilities will be used here.

9.1.1 Caching Page Parts

As already discussed in Section 6.1.2, it is difficult for a dynamic script to report its last modification date since the data is received from a database. Therefore, no caching with Squid is possible.

As we defined earlier the last modification date is the date of the “youngest” part of the page. Furthermore, if for one part of the page no such date can be determined, the last modification date for the whole page is unavailable. This takes proxy servers, such as Squid, out of play.

If we descend a level and move the caching part to the script (for non-static pages), then we cannot do anything useful about the part for which the last modification date is unavailable. We can cache all other parts that provide such a date and, therefore, the caching process can be applied to those parts.

Moving the caching part to the PHP script makes it more vulnerable to bugs regarding the delivery of outdated (stale) information. Therefore, there has to be taken special care of the implementation.

When speaking about caching in this field we usually mean a combination of compilation and caching. This is also the approach that Smarty takes.

The template files that follow their own syntax are transformed into a PHP script on the first loading. From that point on only the compiled template file is accessed as long as the template file is not modified. This can already be considered as a kind of caching.

When enabling the caching feature of Smarty also loops and sections are eliminated and the output of the script (corresponding to the template file) is stored and delivered. This causes another speed increase. To allow to have the programming constructs removed, further care has to be taken which is discussed in the next section.

9.1.2 Database Usage

As demonstrated in Section 7.3, scripts that do not even touch the database (such as skeleton-t.php) run considerably faster. The idea of caching only the parts of the page that allow the detection of a last modification date does not go far enough for that.

The concept of reducing (or even eliminating) database access can be compared to the MySQL query cache. The idea behind the concept is as follows: If the database did not change, the whole application (at least the parts that rely on database selects, commonly unpersonalized pages) can be stored on disk. A database change usually only affects certain parts of the application.

If the application is notified about the modification of a certain part of the page, it can clear the corresponding caches and have the other parts remain in cache.

When done carefully many dynamic pages can be made semi-static. Especially the main page (index.php) is worth the effort as it is typically the most frequently accessed page in a web application.

The parallelity to the MySQL query cache is evident (the data from SQL queries could be equally stored and received from the query cache). If it is turned on, the additional effort (modifying existing scripts) seems useless.

It depends on the complexity of post-processing data whether this is true. At the index page of Bandnews.org, for example, a news item is put together from many tables (combining band name, genres, and news data) and requires additional processing (beautifying URIs, search keyword highlighting). If there was no post-processing, the MySQL query cache suffices indeed.

From its first generation the news item stays about the same1 . If stored at that point, the database is not needed at all to display it and therefore is not even invoked.

For notifying the application there exist several concepts. For example:

It is important to mention that this method needs additional effort and consideration for the administrative part of the site. All parts where we expect changes to an application need to be aware of the caching aspect and need to act accordingly. This can be done by centralizing the modification code (duplicated code should be eliminated anyway), e.g. by glueing together database call and cache clearance in one function.

9.2 Preparation

To easily install the use of the Smarty cache the author proposes to use a function for including files. The function is called load and introduces the following convention (with $file as a file to be included; without extension):

We can determine stale documents either by using the last modification date of the included file or by choosing the second possibility (see previous section): if the cache file does not exist (i.e. if it has been deleted), the cached copy is built.


Listing 9.1: The load function in inc/setup.inc.php
 
218function load($page, $cacheid = ’’, $load_php = true) { 
219  global $smarty; 
220 
221  if ($load_php && !$smarty->is_cached($page . ’.tpl’, $cacheid, $_SESSION[’language’])) { 
222     include(ROOT_PATH . inc/’ . $page . ’.inc.php’); 
223  } 
224 
225  if ($page == newsitem’) { 
226     include_once(ROOT_PATH . inc/genfunc.inc.php’); 
227     $source = $smarty->fetch($page . ’.tpl’, $cacheid, $_SESSION[’language’]); 
228     $source = fixtime($source); 
229     echo $source; 
230  } else { 
231     $smarty->display($page . ’.tpl’, $cacheid, $_SESSION[’language’]); 
232  } 
233}

The load function (see Listing 9.1) is quite a universal function but still adapted for Bandnews.org. For example, there is a special branch for the news items that dynamically replaces absolute time (e.g. “March 3, 2005 3:20 p.m.”) with relative time (e.g. “1 hour 3 minutes ago”).

It also provides support for a caching ID: a template is often displayed with varying parameters in different contexts. This can be handled with caching IDs. Internally such an ID represents a directory in the cache. Even sub-directories can be specified using the pipe character (|) as separator. If $cacheid is specified carefully, (only) related cached files are placed in the same directory and can easily be deleted to invalidate the cache.

An important point is that the associated include file is only loaded if there is no cached copy available. This behaviour can eliminate database calls or expensive execution of other PHP code. If the load function was used (or the corresponding part, using the $smarty->is_cached method), the PHP code is still executed and Smarty does not even touch the generated contents.

Listing 9.2 shows a modified version of the presentation skeleton. The last modification code (see page §, listing 6.2) from pres-skel-t.php was not reused as it is taken for sure that no last modification date could be determined anyway.


Listing 9.2: The adapted presentation skeleton – pres-skel-n.php
 
1<?php 
2require(’inc/setup.inc.php’); 
3 
4load(’header’); 
5load(’menu’); 
6load(’sidebar’); 
7 
8load(’footer’); 
9?>

9.3 Results

The benchmarks show another large improvement in speed and let the pages be generated really fast. MySQL query cache and APC were also activated for this test.

The skeleton (skeleton-t.php, see Figure 9.1) is once again the only script to lose speed. Since the request rates are very high this cannot really be felt, but it is still important to our analysis: the activation of the Smarty Cache seems to add some overhead, and there is the previously observed speed losses due tto memory occupied by the MySQL query cache.

All other scripts show high gains (compare with Table 9.1, page §):



Figure 9.1: Smarty Caching benchmark: skeleton-t.php
PIC



Figure 9.2: Smarty Caching benchmark: pres-skel-n.php
PIC



Figure 9.3: Smarty Caching benchmark: index.php
PIC



Figure 9.4: Smarty Caching benchmark: links.php
PIC

The results only profit mainly from APC. The MySQL query cache is only used for filling the cache and dynamic fields if the script needs any at all.



Table 9.1: Smarty Caching Benchmarking results (Requests per second)
SC off
SC on
Concurrent requests
File 10 100 10 100





skeleton-t.php 386.24263.13328.72228.06
pres-skel-n.php57.20 54.76 158.88137.22
index.php 20.16 17.15 61.80 57.65
links.php 10.06 10.04 133.48117.44

9.4 Conclusions for Smarty Caching

This section shows the power a well written PHP script has. When using the cache carefully and managing the correct clearance of the cache whenever a manipulation happens, there is another enourmous speed-up possible. This is especially true for the pages which could not really be tuned by external means, index.php and links.php.

An important point of this form of caching is the fact that only a part of the previously used caching methods is active. For example, the MySQL query cache is only used for generating and filling the cache (this applies to the tested pages).