Benchmarking Zend Framework Routes

Hanging out in #zftalk on freenode is a great way to learn more about using Zend Framework, if you’re not already there get yourself an IRC client and sign on. Recently, the following question was posed:

How can I create a route with an optional parameter; e.g /search/:foo/:bar, where :bar is optional? Do I need to create two different routes?

Several users correctly suggested specifying a default value for :bar when constructing the route, and it was also mentioned that both standard routes and regular expression routes could be used to solve the problem. Naturally, this lead to a discussion of which type of route would be fastest, with no real numbers to fall back on.

The purpose of this article is to present benchmark numbers for both route types (for the specific application described above) and to describe the methods used to run the benchmark tests. This won’t be a full tutorial on how to use xdebug or kcachegrind, so I won’t go into great details about how to use them, but the full source code for the benchmarks will be available at the end of the article.

Method

In order to profile the different routing options, xdebug was used in profiling mode and the resulting profile data was viewed in kcachegrind. The profiling test uses two URLs, one with the optional parameter and one without. Each router’s match() method is called 10000 with each of the two URLs.

The Standard Route

The first route tested was the standard route, which is instantiated with a string describing the route, and an array of default options. The route accepts two parameters, indicated by :foo and :bar. Since a default value is specified for :bar in the initialization array, the route will match both of our test URLs, using the value ‘mybar’ if no parameter is given for :bar.

$route = new Zend_Controller_Router_Route(
    'search/:foo/:bar'
  , array(
        'controller' => 'search'
      , 'action'     => 'index'
      , 'bar'        => 'mybar'
    )
);

The Regex Route

The next route tested was the regex route, which is instantiated in the same way as the standard route, with the addition of an array that maps parameter positions to value names; this allows the route map the results from an internal preg_match call to the numerically keyed captured parameters.

$route = new Zend_Controller_Router_Route_Regex(
    'search/(\w+)(?:/(\w+))'
  , array(
        'controller' => 'search'
      , 'action'     => 'index'
      , 'bar'        => 'mybar'
    )
  , array(
        'foo' => 1
      , 'bar' => 2
    )
);

The Results

The initial test with 10,000 iterations using the test URL that contained both the foo and bar parameters shows that the standard route is faster than the regex route by about 12%. The image at right is a kcachegrind visualization of the call tree rooted in the function that executes the two tests with a distance limit of 1.

Zend Router Profiling (part one)

The second test of 10,000 iterations used the test URL with did not contain the bar parameter. It shows that the regex route is significantly faster than the standard route by about 48%.

Zend Router Profiling (part two)

Based on these tests, it seems like the best choice depends on how often the optional parameter is expected to be provided. If the answer is less than about 60% of the time, the regex route would seem like the best choice; otherwise the regex route will be faster. Keep in mind that this is all based on a specific route, and your situation may vary so if performance is an issue some custom tests may be in order.

If you are interested in reproducing these benchmarks, you can download the benchmarking scripts and customize them as needed.

Chris Abernethy
PHP Wrangler, MySQL DBA, Linux SysAdmin and all around computer guy, developing LAMP applications since Slackware came on 10 floppy disks.

Got something to say? Go for it!