loadModel Speed Testing
So the other day in #cakephp chat on irc.freenode.net (which by the way is a great place to hang out and help if you not busy, or just to learn too) , I had a discussion with a person in there about the use of $this->loadModel(); versus var $uses = array();
Their argument was that applying a model into the controllers var $uses would slow down an app as it scaled into a larger configuration. My argument was that it was easier to simply apply it globally to the controller, rather than a per case use, especially if you need it more than a couple of times within a controllers logic actions.
Well, as we both agreed to disagree, it didn't set well with me, as there was no proof either way. So, I set out to prove either my point, or their's as the case may be. I'd let the data tell the tale.
To be honest, I could see thier point. If you have a complex model and you apply it globally, you could essentially bring an app to it's knees. especially if the logic is written poorly. However after running some load tests, I have come to find that in most cases, applying a model globally is faster. How much faster you ask?
Well, first let me explain the testing scenario. I created 2 identical tables in a db. Next, I baked the project, and then did a bake all. The 2 tables are Foos and Bars. My theory is Foos, and thier theory is Bars. I then wrote an interface for the home page, and placed an Ajax script to run each set of tests seperately.
The following is an excerpt from the Bars controller. The only main difference between the Bars controller and the Foos controller is that within each private action i.e. Bars::__one() when FooModel is needed, it's loaded via $this->loadModel('Foo');. Where as, the Foos controller is simply set with BarModel in the $uses var.
<?php class BarsController extends AppController { var $name = 'Bars'; /**
* This is the main test block. It itterates each test phase depending on the repeat setting.
*/ function stress($count = 1, $repeat = 1) { $this->sName = rand(0, 10); for ( $r = 0; $r < $repeat; $r++ ) { $this->__start_timer(); $this->__one($count); $this->__end_timer(); $totalTimeOne[$r] = ($this->iEndTime - $this->iStartTime); } for ( $r = 0; $r < $repeat; $r++ ) { $this->__start_timer(); $this->__two($count); $this->__end_timer(); $totalTimeTwo[$r] = ($this->iEndTime - $this->iStartTime); } for ( $r = 0; $r < $repeat; $r++ ) { $this->__start_timer(); $this->__three($count); $this->__end_timer(); $totalTimeThree[$r] = ($this->iEndTime - $this->iStartTime); } for ( $r = 0; $r < $repeat; $r++ ) { $this->__start_timer(); $this->__four($count); $this->__end_timer(); $totalTimeFour[$r] = ($this->iEndTime - $this->iStartTime); } for ( $r = 0; $r < $repeat; $r++ ) { $this->__start_timer(); $this->__five($count); $this->__end_timer(); $totalTimeFive[$r] = ($this->iEndTime - $this->iStartTime); } for ( $r = 0; $r < $repeat; $r++ ) { $this->__start_timer(); $this->__six($count); $this->__end_timer(); $totalTimeSix[$r] = ($this->iEndTime - $this->iStartTime); } $this->Bar->query('TRUNCATE TABLE `bars` '); $this->Bar->query('TRUNCATE TABLE `foos` '); $this->set( array( 'count' => $count, 'repeat' => $repeat, 'totalTimeOne' => $totalTimeOne, 'totalTimeTwo' => $totalTimeTwo, 'totalTimeThree' => $totalTimeThree, 'totalTimeFour' => $totalTimeFour, 'totalTimeFive' => $totalTimeFive, 'totalTimeSix' => $totalTimeSix ) ); if ($this->RequestHandler->isAjax()) { $this->layout = 'ajax'; } } /**
* Begin Create Section
*/ function __one($count = 1) { for ($i = 0; $i < $count; $i++) { $this->data['Bar']['name'] = $this->sName; $this->data['Bar']['data'] = $this->sModelData; $this->Bar->create(); $this->Bar->save($this->data); } } function __two($count = 1) { for ($i = 0; $i < $count; $i++) { $this->loadModel('Foo'); $this->data['Foo']['name'] = $this->sName; $this->data['Foo']['data'] = $this->sModelData; $this->Foo->create(); $this->Foo->save($this->data); } } /**
* Begin Read Section
*/ function __three($count = 1) { for ($i = 0; $i < $count; $i++) { $this->Bar->findAll(); } } function __four($count = 1) { for ($i = 0; $i < $count; $i++) { $this->loadModel('Foo'); $this->Foo->findAll(); } } /**
* Begin Update Section
*/ function __five($count = 1) { for ($i = 0; $i < $count; $i++) { $finds = $this->Bar->findAll(); foreach($finds as $find) { $this->data[] = $find; $this->data[]['Bar']['name'] = $this->sName . $this->sName; } $this->Bar->save($this->data); } } function __six($count = 1) { for ($i = 0; $i < $count; $i++) { $this->loadModel('Foo'); $finds = $this->Foo->findAll(); foreach($finds as $find) { $this->data[] = $find; $this->data[]['Foo']['name'] = $this->sName . $this->sName; } $this->Foo->save($this->data); } } } ?>
There are some referrences to timers and such that are located within the AppController itself. I will be releasing this project into the public, for people to test it for themselves. Now, for the results. This set of results was a count of 5 and a repeat value of 5 as well.
Bar Stress Test
Total time for the complete run was 8.07537889481 seconds.
Create
| Test One | Test Two |
|---|---|
| 0.096240997314453 | 0.054368972778320 |
Read
| Test Three | Test Four |
|---|---|
| 0.053762197494507 | 0.052159786224365 |
Update
| Test Three | Test Four |
|---|---|
| 1.867883205413818 | 5.950963735580444 |
Foo Stress Test
Total time for the complete run was 7.46506261826 seconds.
Create
| Test One | Test Two |
|---|---|
| 0.057495832443237 | 0.048748731613159 |
Read
| Test Three | Test Four |
|---|---|
| 0.047458887100220 | 0.050731658935547 |
Update
| Test Three | Test Four |
|---|---|
| 1.726682186126709 | 5.533945322036744 |
Part 2 of this posting.


Ceeram Said:
Nice to see it tested, tried them myself as well, only had to adjust the hardcoded urls in home.ctp and had them replaced by 'url: "bars/stress/"+cycles+"/"+repeat,' and 'url: "foos/stress/"+cycles+"/"+repeat,' wow is my ubuntu slow man, running 5 and 5 as well took total time: Bar Stress Test Total time for the complete run was 21.9207077026 seconds. Create Test One Test Two Read Test Three Test Four Update Test Three Test Four Foo Stress Test Total time for the complete run was 21.603720665 seconds. Create Test One Test Two Read Test Three Test Four Update Test Three Test Four as you can see only total time was displayed. I woudl say, there is a slight difference between the two. But the problem with your test is that the slowness created by $uses is the loading of the controller, so you should test loading the controller this many cycles. Also since you keep doing find/save, you cant benefit cached models(which would be in the benefit of $uses btw). To me this is not prove at all that $uses would be better in this case. I wuold suggest to read this blog post: http://www.pseudocoder.com/archives/2009/04/16/one-more-tip-for-speeding-up-cakephp-apps/