Personal tools
Support

Get Help

Join our chat rooms or support forums if you have more specific questions.

Plone Training
Learn how to design, build, and deploy a website in Plone through one of the numerous Plone training sessions around the world.
Find Plone training…
 
Document Actions

3.1. Performance

Up one level
Some general guidelines about coding for performance

Here's a little fact we should admit up front: Plone is slow. Not as slow as it used to be, not as slow that it's useless, but neither Zope nor Plone have ever been sold on speed. In general, Plone is a content production system, not a content delivery system, and has been written for features and ease-of-use — not for blazing speed. (We use caching to improve speed, or separate delivery platforms altogether.)

This has less to do with the design of the software (or the capabilities of the people involved) than with the trade-offs that have been made for usability and flexibility. However, it is important to think about performance when writing code for Plone core.

Profiling

To find performance bottlenecks, you need to use a profiler. The PTProfiler is very useful for finding out which expressions in the page templates are using up the most CPU time. An alternative profiler is the Call profiler. See also Dieter Mauer's Zope Profiler.

Waking up objects

If there is one axiomatic law of performance in Zope and Plone it is this: Do not wake up objects unless you need to. "Waking up" objects refers to pulling them out of the ZODB - traversing to them, loading them into memory. This is an expensive operation. The old navigation tree in Plone 2.0 used to wake up each and every object it displayed. Sometimes it could wake up each object in a BTreeFolder (aka "Large Folder") with 1,000 children, even if it only rendered a single one of them. This was bad.

In general, we prefer to use catalog queries to find objects. This has been possible since the introduction of ExtendedPathIndex in Plone 2.1. The getFolderContents script uses a catalog query. The navigation tree uses a catalog query. The portlets all use catalog queries.

Essentially, if you find yourself using objectValues() or contentValues() you need to think long and hard about whether you should be using a catalog query instead. The following pattern is the most common use of a path search:

  portal_catalog = getToolByName(self, 'portal_catalog')
  results = portal_catalog.searchResults(path = {'query' : '/'.join(self.getPhysicalPath()),
                                                 'depth' : 1})
  for brain in results:
    ...

Catalog queries themselves are not the fastest to set up, so if you are expecting only one or two items, traversal may be better. For the breadcrumbs in Plone 2.5, for example, we use traversal up the acquisition parent chain (by using aq_parent, which returns full objects) instead of a catalog query, because the parent objects are quite likely to be in the ZODB cache already, and because setting up a catalog query for what is most likely going to be 1-5 results is sub-optimal.

Actions

CMF actions are used to drive things like the tabs in the editable border, the site actions (the sitemap, etc.), the personal bar and many other parts of the Plone UI. Unfortunately, actions are also slow, because each may be associated with a TALES expression that is used to determine whether the action should be displayed or not. Each expression requires an expression context to be set up, the expression to be executed, and the result to be evaluated. This is not particularly efficient.

Actions are still used for many of the dynamic parts of the Plone UI, but if you are using them, you should be aware of the performance implications. At the very least, avoid complex TALES expressions if possible.

Avoiding 404s

A 404, or a NotFoundException in Zope, is an expensive operation. 404s can occur unnoticed when a page template references and image or stylesheet that does not exist. Be careful about referencing items that may or may not exist. You can code defensively by doing something like:

  <tal:block condition="exists:some-name">
    ...
  </tal:block>

If you are in doubt, go to the error_log and remove NotFoundException from the list of ignored execptions. Re-load your page and watch out for new entries in the error log.

Making pages cacheable

One of the ways in which real-world installations make Plone faster is by using cacheing, for example via CacheFu. It is important that we make pages and elements of pages as easy to cache as possible. Most of the time, this simply boils down to avoiding unnecessarily dynamic elements.

Take the sitemap, for example. Constructing this is an expensive operation, since the sitemap view needs to execute query for all objects matching the current navigation filter settings. It then needs to render these using a recursive page template. Notice that the action pointing to the sitemap is ${portal_url}/sitemap.

At first sight, this is sub-optimal, because the sitemap can't know the context the user was in before clicking the link, and thus can't put a "you are here" marker in the sitemap. But if the path were ${object_url}/sitemap, then the sitemap on /foo would be different from the sitemap at /bar. Any cache for /foo/sitemap would be completely separate from /bar/sitemap even though 98% of the information on those two pages would be the same. Worse, when Google comes to index the site it will sprider a /sitemap for each and every page, which may bring your site to a grinding halt from executing too many queries.

This principle applies to other things too, such as the author page (which is linked to from all the by-lines), and most commonly image src attributes for common icons.

Cache with v attributes

Sometimes you can't avoid executing an expensive operation more than once during a request. In this case, you may want to cache the result. Zope provides a mechanism for simple caching called "volatile" attributes, or _v_ attributes.

Any non-transient object (that is, any object that lives in the ZODB - note that view classes are not included in this group) can store a value in a v attribute. A v attribute may or may not exist at any given point in time! It will only be valid whilst the object is cached in memory, and there is no guarantee it will stay in memory even during a single request cycle. However, it most likely will be, so it pays off to use it.

You need to code defensively whilst using v attributes:

  from Products.CMFPlone.utils import base_hasattr
  ...

  if not base_hasattr(self, '_v_cachedValue'):
    self._v_cachedValue = self.calculateExpensiveValue()
  return self._v_cachedValue

Download size

Nobody likes having an 800k download to get a few lines of text. Plone's UI is by necessity heavy, and there are many stylesheets, images and HTML snippets included when you download even the lightest of Plone pages. The least you can do is to not make this problem any worse. Think carefully about what you are including, especially if it is going to be included on every page. Keep whitespace and superfluous comments to a minimum, and reduce the size of images as much as you can.

by Martin Aspeli last modified April 17, 2006 - 00:26
Contributors: Martin Aspeli, Hanno Schlichting, Whit Morris
All content is copyright Plone Foundation and the individual contributors.

Heavy UI elements

Posted by Alvaro Medina at May 31, 2008 - 05:27
I've always wondered exactly WHY the current UI elements are so many and so heavy.

For any issues with the web site functionality, please file a ticket.

Please consult the policy on plone.org content if you want your content published on this site.

Servers and hosting by