I am currently working on a project using Drupal 5.1 for a social networking site. Each page on the development site would take 3-9 seconds to load and on the production environment would take 2-7 seconds. Way too long. I was able to easily increase performance.
- The context
- The project holds around 1.2 million document and allow a number of social networking actions like tag cloud, voting, comments, subscriptions, etc.... We use different views to show the content in different orders and within different timeframes. The page that takes the longest to run is the one that sorts by vote with the largest time period to cover. At first we had the impression that the tag cloud (tagadelic) generation would be the piece that would be the slowest. We were surprised.
- OpCode it!
The very first thing to do is to use a code optimizer like APC. It is free and will have a serious impact on speed. I used the "pecl install APC" to install it. You may need to install a number of other packages like httpd-devel (for apxs) in order to successfully install APC. Refer yourself to a competent Linux administrator if you have trouble installing the required packages. If you are using a shared hosting environment, your options will be limited. In my case, we are using dedicated server were we have root and full control.
- Drupal performance options
The second obvious thing to do is to enable caching within the drupal admin settings (Performance option). You can enable normal and extended caching (for experts only). I was not able to enable the extended caching because some module was incompatible with the extended caching. I did not try to find out why these modules were incompatible but from what I could see it seemed to be caused by the fast that these modules were implementing certain functions (hooks perhaps?) that were causing the extended cache incompatibility.
I also enabled the compressed CSS on that same page. Here, compression is not about zipping but rather about removing all the white spaces (space, tab, new line) and having only one css file that contains all the css file combined. This reduces the number of hits that the web browser has to make and reduce the time it takes for the browser to parse all this css (less white space to parse).
- Profile it!
I installed xdebug on the Linux server and WinCacheGrind on my windows laptop. I enabled profiling, restarted the web server and accessed our list of nodes. The xdebug created an output file that I opened with WinCacheGrind. It showed that most of the time spent was in rendering a specific template file.
- Caching Views Items
We are using the "Views" module to display the list of documents the way we want (sorted, filtered, etc...). I used a custom views template and I had to create a function in template.php which goes thru all nodes and render a template file representing one node in the list of node. The rendering of that template file was the template that was using the most time (from what WinCacheGrind showed me). I concentrated my efforts in finding a way to reduce the time spent rendering that template over and over again.
I decided to cache the HTML itself. Skipping the rendering when the cached version existed. That is only possible when the content you cache does not change on a per user basis. In my case I had an extra bonus since that HTML was also being re-used in other section of the site (from different version of the view). I saved half a second of rendering time with this.
- Test page generation only
At first I tested the page generation time in the browser and felt that the loading of the images, the speed of my computer and other aspect were interfering in my results. I decided to test drupal without loading the images and without the browser to try to render the HTML. I wanted to test how long it took to execute one page. I used the Linux script "GET" that comes with lwp Perl module. Since my site was a private site (login required), I had to make sure I was logged in when testing. I used my browser's settings to view my cookies and copied into my clipboard the name and value of the cookie. I used the following command line to run my test:
time GET -d -h 'Cookie: SESS0987654321abcdef=0a1b2c3d4e5f5g6h7i8j9k' http://localhost/node/
The fact that I preceded the command with "time" allowed me to find out how much time it took for executing the HTTP "GET" request to my script. I felt like something was wrong. It was taking 2.5 seconds to execute on the command line but 7 seconds to load in the browser. I was wondering what could be causing the browser to load slower.
- Watch the logs
While I was running the command line "GET" request, I decided to watch the server access and error log for any hint for further optimizing. I saw in the access log that it was trying to access a CSS file that did not exists, creating a 404 error by drupal itself. Drupal needs to initialize (bootstrap) in order to display the 404 error message. That's an extra overhead that would cause the browser to take more time to load everything. I found out what was causing that error (path misspelling) and corrected the situation. With the 404 gone, we have only "one" drupal bootstrap per page request instead of two.
What are you doing to increase performance of your drupal site?