8 Ways to Speed Up the Performance of CakePHP Apps
It's a not so well kept secret that CakePHP is slow. What isn't well know is that this is done by design. I could get in a lot of trouble by revealing this, but I'm willing to take that risk. I have records, a paper trail a mile long, showing members of the Cake dev team investing heavily in the stock of Dell, IBM, Cisco and other server companies. We've all heard the expression "hardware is cheap, and programmers are expensive." The Cake team figure out how to monetize that by making a framework that is fast to develop with, but slow to run. They want you to throw more hardware at it. Ingenious, right? Well I'm here to end all of that. Every time you use one of the tips in this article it's one less gold chain on the neck of a Cake developer.
- I assume you're already using the ContainableBehavior and have optimized+indexed your SQL queries.
- I used ab to benchmark each of these changes and compared to the base benchmark, which is the plain app with debug at 0. I'm not including the actually benchmark numbers since they will vary by the application and the machine. Instead I'll include the approximate change in terms of percentage increase.
- No, you can't see my sample app.
1) Set Debug to 0
A no brainer, right? Well there are plenty of posts on the google group that say otherwise. Before even thinking about tuning your Cake app make sure debug is 0.
Here's the difference. For the Cake engine to run it generates two cache sections.
The first is
/tmp/cache/models. In there you'll find a file for every model your system containing the table schema. You know those "DESC table;" queries you see in the query output? That's what there for. Those queries go away when debug is 0.
The seconds cache is
/tmp/cache/persistent. There are a couple different files in there that are used by Cake when running your app. The one that generally causes the most slow down to generate is cake_core_file_map. This file stores the paths to various classes in your app. To build the file Cake does a logical, but still time consuming, search of your directory tree looking for the right file.
So what is the difference between debug 0 and debug >0. Oh, about 2.73517104 years. When debug is >0 the cache lifetime on these files is 10 seconds. Switching debug to 0 pushes the expiration to 999 days.
This actually brings up an important question: if something is a "no brainer," do the people that still don't do it have less then no brain? If people without brains still figured it out, are there people walking around with black holes where there brain would be. Am I in danger of having my brain pulled into it, like light when it's slips past the event horizon?
+80% to 100%
2) Cache your slow queries/web service requests/whatever
The Cake cache lib is a great tool for caching single parts of your application. It handles all the gory work of writing to a file or tying into a memory based caching engine. All you need to do is figure out what to cache.
Let's say you have a query that has been indexed and optimized, but is still too slow. The Cookbook provides an example of how to wrap it with the cache lib so that you don't need to run it every request.
+0% to 1000000% Really depends on your app and what your caching.
3) View Caching
Think of this as entire page caching. The Cookbook covers the basics and since rendering the page still runs through PHP there is some flexibility for maintaining dynamic parts of the page. For example, if you were running a store you could cache the product pages, but still have a block showing the user's shopping cart.
There's a section in the Cookbook mixed in here that covers the various caching engines CakePHP supports. However, at the moment (220.127.116.1104) view caching uses file based caching and is independent of the cache library described in #2 .
+130% to 160%
4) HTML Caching
This one is my own creation. It's based on the same principal of the Super Cache for WordPress. Basically it takes the rendered page and writes it to your webroot as straight HTML. The next time the page is hit your web server can serve it directly without even having to go to PHP.
There are obvious limitations for this, such as no dynamic content on the page, and the cache won't be automatically cleared. Still it's great for things like RSS feeds or something like popurls where the anonymous viewers all get the same page.
~60000% - This isn't hyperbole, that's the real increase.
5) APC (or some other opcode cache)
Wikipedia describes APC as "a free, open source framework that optimizes PHP intermediate code and caches data and compiled code from the PHP bytecode compiler in shared memory." Whatever. It makes shit fast. And you don't have to change any of your code. Fuck yea. Where do I sign up, right?
+25% to 100%
6) Persistent Models
This one isn't mentioned in the Cookbook (I'll add it in the next few days if no one beats me to it. I put it on my todo whiteboard, right below "figure out why putting computers in the clouds is more efficient then their traditional ground based counter parts"). This one is simple to turn on. In your controller (or AppController) add the attribute:
var $persistModel = true;
After a page refresh you'll notice two new files in
/tmp/cache/persistent for each model included in the controller. One is the cache of the model and the other is a cache of the objects in the ClassRegistry. Like view caching mentioned above, this cache can only be saved on the file system.
+0% to 200%
How much this one helps depends on your application. If your controller only has one model and it isn't associated with any others you're not going to see much of a boost. In my demo app there was around 100% increase. There was one model in the controller, which was associated with 3 other models, which had associations of their own.
7) Store The Persistent Cache in APC
To turn enable this you need be using APC and set your "_cake_core_" cache to use APC. In your core.php put:
Cache::config('_cake_core_', array('engine' => 'Apc', 'duration'=> 3600, 'probability'=> 100, ));
This takes the cache files normally stored in
/tmp/cache/persistent (not including the persistent models) and stores them in memory.
This is a hard one to measure. I tried enabling APC without opcode caching to measure just this change, but never found a setting that didn't provide a speed bump over the base setup.
8) Speed Up Reverse Routing
There are two methods for doing this. The first is described in a post by Tim at Debuggable.com. Tim's method only works for certain link types and breaks the reverse routing feature. Mine uses caching and made it to Hollywood week of CakePHP Idol where Nate (the Simon of the core team) called it "clever", but it was ultimately sent home when it forgot the lyrics to Kansas' "Dust in the Wind." Yes, I'm drinking and watching American Idol as I write this.
Like all of these tips, the actual increase depends on your app. If you don't use many custom routes and don't have many links on your page your not going to see much of a benefit.
It's up to you now. Figure out which of these works best for you. Go forth and produce speedy CakePHP apps.
Wait, One More Thing
I'm sure I missed something. Leave your best tips in the comments. If I get enough I'll make a second post and claim credit for myself.