| 
  • If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • Introducing Dokkio, a new service from the creators of PBworks. Find and manage the files you've stored in Dropbox, Google Drive, Gmail, Slack, and more. Try it for free today.

View
 

Coding for Performance

Page history last edited by Elizabeth Leddy 10 years ago

General Best Practices

  • If you haven't read yet, its super important to use the catalog as much as possible versus waking up objects. Check out the details by @optilude and if you see anything he has written in general read it - there is a lot of good reasoning there.
  • In an enterprise setting, its common to include an LDAP or Active Directory setup for authentication, group management, etc... If you are experiencing performance lag when logged in as an LDAP user and not when logged in through base acl_users (an easy check) then turn on debugging to see how much your ldap or active directory is getting blasted with calls. First try turning on caching of the users/groups, but be aware that not everything by default is cached. If you are seeing too many calls, don't be afraid to sync your users/groups into the local acl_users folder - it's much faster and easier to maintain anyways. You can still route authentication strictly to your ldap install so you don't have to worry about passwords et al through the PAS configuration.
  • External database connections can help distribute the load very nicely. Make sure to set timeouts appropriately, especially if you have any long live transactions.  If you are on a python less than 2.6, you can't set them explicitly for each transaction so a common pattern is to set the default timeout for all transaction low (i.e. 2 seconds) and then for any potentially long lived transactions temporarily raise the limit then lower again. Keep in mind that changing the timeout is for all sockets in that running process, no matter the protocol
  • Plone is much faster at reading than editing. While it's getting better with every version, rendering an edit template and creating objects take about twice as much time on average than reading. That being said, try to limit the number of transactions going on in the background. A common idiom that is a performance killer is to rock multi-part forms. When the form is doled out over a few pages, the expensive machinery to generate and process that form and its widgets is invoked multiple times. Keep it simple on one page. If you feel like you need multiple pages, maybe its time to rethink the concept anyways. If you really have a simple form thats used a LOT, consider just writing the html yourself instead of invoking call the background machinery. You can save a lot of rendering time that way.
  • If you don't have to worry about section 508, consider working ajax into your interfaces to offset expensive UI pieces, calculations, and even form processing. Using ajax request/responses to py scripts can bypass a lot of "unecessary" built ins like security while also avoiding real server "clicks". It would be a shame to block your UI from loading over something that can be delayed until after loading. The web 2.0 wanks got that right - ajax can be a huge boost for both real and perceived performance.
  • Acquisition is the cats meow, but as soon as you forget about the consequences it comes to bite you in the butt. Suppose you have varnish as a front end cache caching all images. You decide to put "X" images for all delete actions in the folder_contents template that riddles throughout the page. However you just put a relative url href="/x.gif", knowing that acquisition will pick it up no matter where we are in the site. Sweet right? There is a little catch. No each time that gif is invoked, it has a correct url. /mysite/folder1/x.gif and /mysite/folder2/x.gif and on and on and on. While plone knows that this is the same image, your cache, and more importantly your browser, sees these as different entities. So your cache and browser will refetch this image for every new context, wasting space in the cache and bandwidth. If you make one change to a fixed url at the base of the site, i.e. href="/mysite/x.gif" then all that mess goes away. w00t!
  • If you want to link to an object that you need to wake up or maybe even do a catalog query, think twice if you really need to do either. If the object/brain isn't already on hand, think about redirecting to a script that finds the url given certain attributes that you DO have available. For example, to link to an article on 2009-11-02, no need to do the query just to getURL. Just write a script called redirectToArticleByDate that takes a date parameter, and if someone actually clicks the link then it does the query to find the object and then redirects accordingly. This is especially beneficial if you want to be extra "linky", and who doesn't really, but don't want to waste cpu on things that users click 1 in 10 renders. Note that redirects are relatively expensive for mobile users so if your user is likely to click the link, this technique is probably not for you. TODO: this example sucks. need to find a more explicit one.

 

TAL Tips

  • Be careful not to call an object. nocall: is useful. in tal:condition use tal:define="person context/Person" tal:condition="python: person" if "context/Person" could be an actual content object like returned for reference fields. A simple tal:condition="context/Person" will actually call and in the background render the whole page for that object. Do that in a folder_listing style loop and see your page take minutes to render.
  • Use tal:define to calculate values once if they are used multiple times in the same template. Especially useful if doing tal:repeat. In Python you avoid doing anything you can inside a loop, the same is true for TAL. And with templates being security proxied even a simple attribute lookup has a cost.

 

Warnings

  • Use memoize with care and know exactly what you are doing. Randomly sprinkling @memoize calls onto every view you have makes things slower and eats your memory. Using a memoize decorator on a persistent object can lead to db writes on view and hard to debug errors. Generally ask yourself why the same method of a view is called twice during the same request at all. A tal:define in the template usually solves the problem.

Comments (2)

(account deleted) said

at 11:57 am on Dec 19, 2009

other TAL tips:

- use tal:define to calculate values once if they are used multiple times in the same template. Especially useful if doing tal:repeat. In Python you avoid doing anything you can inside a loop, the same is true for TAL. And with templates being security proxied even a simple attribute lookup has a cost.

- be careful not to call an object. nocall: is useful. in tal:condition use tal:define="person context/Person" tal:condition="python: person" if "context/Person" could be an actual content object like returned for reference fields. A simple tal:condition="context/Person" will actually call and in the background render the whole page for that object. Do that in a folder_listing style loop and see your page take minutes to render.

plone.memoize:

- Use with care and know exactly what you are doing. Randomly sprinkling @memoize calls onto every view you have makes things slower and eats your memory. Using a memoize decorator on a persistent object can lead to db writes on view and hard to debug errors. Generally ask yourself why the same method of a view is called twice during the same request at all. A tal:define in the template usually solves the problem.

Elizabeth Leddy said

at 4:45 pm on Dec 22, 2009

Just want to mega emphasize the call an object in tal tip. A LOT of people accidentally do this including myself. An easy fix if you aren't sure is just make sure to prefix with python, i.e. tal:condition="python:maybeAnObject".

You don't have permission to comment on this page.