For coders TYPO3 Tech Corner

Menu: Comparison of techniques

Menu: Comparison of techniques

Mega menus or drop-down menus allow visitors to access the depths of extensive websites in a targeted manner. The performance required to create such a menu is usually rather poor. The reason is obvious: the entire page tree (or at least the first levels of it) must be read out and built up as a tree. And because a menu is different for each page by highlighting the active path or the current page, it has to be regenerated each time.

tl;dr:
Fluid makes templating much easier, but can also cause bottlenecks.
TypoScript is powerful and - when used correctly - can do much more than Fluid.
However, if you correctly remove the data preparation from the templating, then with more thought and knowledge you can tease out even more performance.

How does TYPO3 perform with large menus?

We wanted to find out:And is there a difference between easy-to-maintain Fluid-based menus and classic TypoScript-based menus?

Since the bottleneck is often the database, we focussed on this:
how many SQL queries are needed to render the navigation?

Measuring

The test project was a university website with around 800 menu items in the main menu on 4 levels.


The measurements refer to the following calls (in this order):

  1. Home (not in menu)
  2. Impressum (not in menu)
  3. Fakultät Maschinenbau (Page 1)
  4. Fakultät Wirtschaftsingenieurwesen (Page 2)
  5. Fakultät Wirtschaftsingenieurwesen - Unterseite (Page 2.1)

The cache was completely emptied before each measurement (typo3 cache:flush).

Disclaimer:
Of course, the page cache, StaticFileCache, Varnish & Co, Warming/Crawler would hardly allow the site visitor to call up the pages in this state, but in this comparison we are interested in what is really going on under the bonnet.

Fluid-based menu vs. classic TypoScript menu

To begin with, we compare the two basic approaches: firstly, the drop-down menu is built with Fluid and the MenuProcessor, and secondly in the classic way with TypoScript and the HMENU.

Comparison of the SQL queries required for both versions:

| Page | Fluid-based | TypoScript-based | |------------|---------------|--------------------| | Home | 6094 | 4699 | | Impressum | 1259 | 904 | | Page 1 | 1251 | 895 | | Page 2 | 1251 | 895 | | Page 2.1 | 1250 | 895 |

"But with b13/menus..."

Of course, anyone concerned with the performance of menus will quickly come across the menu extension. This provides DataProcessors or its own cObjects, which read the tree on which the navigation is based more efficiently. However, the extension does not intervene in the pure rendering of the menu.

Comparison of the SQL queries required for both versions:

| Page | Fluid-based | Fluid & b13/menus | |------------|---------------|--------------------| | Home | 6094 | 7436 | | Impressum | 1259 | 1704 | | Page 1 | 1251 | 1695 | | Page 2 | 1251 | 1695 | | Page 2.1 | 1250 | 1694 |

Addendum: Pimped menu extension

We were surprised by the result, which is why we sought dialogue with Benni, the developer of the menu extension. It became clear that in times of TYPO3 v12, the advantage of the extension is probably no longer as noticeable as it was under TYPO3 v9, when the extension was created. The need for the f:link.*-ViewHelper in the template now has a much greater impact on performance.


However, with a little pimping (thanks Benni!) by enriching the MenuItems with the link in the TreeMenuProcessor, the values can be significantly improved.

| Page | Fluid-based | Fluid & b13/menus | Fluid & b13/menus (pimped) | |------------|---------------|--------------------|----------------------------| | Home | 6094 | 7436 | 7436 | | Impressum | 1259 | 1704 | 18 | | Page 1 | 1251 | 1695 | 11 | | Page 2 | 1251 | 1695 | 11 | | Page 2.1 | 1250 | 1694 | 11 |

Wow! Fluid shot out over 1600 SQL queries to generate the 800 links. In other words, compared to the normal MenuProcessor, the database queries for reading/assembling the menu are drastically reduced by "Menus", but the effect is cancelled out by the f:link.*-ViewHelper.

Without this little hack, its use is counterproductive.

Why always start from 0?

At the beginning I wrote that TYPO3 generates new menus for each page because the active path and the current page must always be taken into account. But does this mean that you always have to start at 0?

Let's take a website with four branches (Page1, Page2, Page 3, Page4). Why do branches 2-4 have to be read and rendered again if the visitor only moves to Page1 and its subpages? The active state only moves in this branch - the other three branches remain unchanged. So why shouldn't they be cached? And not just their data structure, but the entire rendered HTML?

Five years ago, I had already dealt with this in more detail and presented a TypoScript-based solution in my blog in the article "Mega-Menü mit Cache optimieren".

What effect does this approach have on our test scenario?

Comparison of the SQL queries required for both versions:

| Page | Fluid-based | TS-based, cached | |------------|---------------|--------------------| | Home | 6094 | 4728 | | Impressum | 1259 | 844 | | Page 1 | 1251 | 837 | | Page 2 | 1251 | 837 | | Page 2.1 | 1250 | 837 |

Is there more possible?

My previous cached TypoScript menu was based on a balanced page tree. In other words, it works well if all (main) navigation items have roughly the same number of subpages and sub-subpages.
In our project, however, one menu item/branch is out of line: "Fakultäten". On level 2 there are the individual faculties, levels 3 & 4 then the entire faculty subpages. This means that approx. 2/3 of all menu items are located below this one menu item. The structure is therefore far from a balanced tree.

The previous approach was to split the menu, configure an HMENU for each main navigation point and cache each of these.

But why only cache at level 1?
When browsing through the TypoScript Reference, it more or less quickly becomes clear: within an HMENU, there are not really any options for caching parts of the menu. Unless we think more complicated.... We had already cached the HMENU using the first-level function on level 1. So if level 2 of a tree was also an HMENU again, but rendered as a menu item within the HMENU of level 1...

Yes, there was something else!

| Page | Fluid-based | TS-based, 2 Caches | |------------|---------------|----------------------| | Home | 6094 | 5206 | | Impressum | 1259 | 56 | | Page 1 | 1251 | 116 | | Page 2 | 1251 | 113 | | Page 2.1 | 1250 | 116 |

We explain the details (including code snippets) in a separate article: Drop-down menu without load - using multilevel cache

Summary

Comparison of all tested menu variants with regard to the SQL queries required in each case:

| Page | Fluid-based | Fluid & b13/menus | TypoScript-based | TS-based, cached | TS-based, 2 Caches | Fluid & b13/menus (pimped) | |------------|---------------|-------------------|--------------------|--------------------|----------------------|----------------------------| | Home | 6094 | 7436 | 4699 | 4728 | 5206 | 7436 | | Impressum | 1259 | 1704 | 904 | 844 | 56 | 18 | | Page 1 | 1251 | 1695 | 895 | 837 | 116 | 11 | | Page 2 | 1251 | 1695 | 895 | 837 | 113 | 11 | | Page 2.1 | 1250 | 1694 | 895 | 837 | 116 | 11 |

99% less database transactions

As always with performance optimisation, you have to take a close look at the specific problem and the framework conditions. Not every more performant solution is also more comprehensible/beautiful. It's always a question of weighing things up. But let's be honest: 99% fewer SQL queries from the second page view within the website? That's worth it. With over 800 pages, this means that we save around one million queries (before the page cache takes effect)!

You can't believe that?

Don't believe any statistics that you haven't falsified yourself, as the saying goes. Understandable. That's why we'd like to invite you to take a look at the comparison yourself, optimise it, check other measurement criteria,... - and to talk, post and discuss about it.

There is a test project in the Git repository with

  • TYPO3 v12 as a basis
  • over 1000 pages (about 800 of them in the main navigation)
  • 5 (plus 2) menu variants with the same content
  • ready-to-run DDEV environment
  • preconfigured xhprof and XHGui
  • activated MySQL report extension

Even more figures from the comparison

In this article, we have only scratched the surface of the comparison and focussed on the number of SQL queries. However, other values also play a role in performance and load, and it always depends on the scenario and the software/hardware setup.


In "Menu comparison: numbers, numbers, numbers" we present many more numbers in comparison.

Solution for your project

No matter which solution you fancy: find a solution that suits your project!


As already mentioned several times: every project is different, has its own special features - and its own stumbling blocks. Therefore, there is no one best solution, but rather this comparison is intended to give you food for thought as to what options are available.

If you are looking for support in analysing and solving your performance problems, you are of course welcome to contact us ;-)

Back

"Code faster, look at the time" - does this sound familiar to you?

How about time and respect for code quality? Working in a team? Automated tests?

Join us