<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Web:Extend &#187; Optimization</title>
	<atom:link href="http://blog.extend.ws/category/optimization/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.extend.ws</link>
	<description>Web:Extend's Development Blog</description>
	<lastBuildDate>Thu, 16 Sep 2010 20:38:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>RoWO &#8211; Part 2: Simple is Easy</title>
		<link>http://blog.extend.ws/2008/11/06/rowo-part-2-simple-is-easy/</link>
		<comments>http://blog.extend.ws/2008/11/06/rowo-part-2-simple-is-easy/#comments</comments>
		<pubDate>Thu, 06 Nov 2008 11:42:36 +0000</pubDate>
		<dc:creator>Loïc Hoguin</dc:creator>
				<category><![CDATA[Optimization]]></category>
		<category><![CDATA[Simplicity]]></category>
		<category><![CDATA[Web:Extend]]></category>

		<guid isPermaLink="false">http://blog.extend.ws/?p=7</guid>
		<description><![CDATA[About two months ago, Rasmus Lerdorf gave a talk at FrOSCon entitled Simple is Hard. If you don&#8217;t know about it yet I recommend reading the slides before continuing with this article.
Rasmus proved how hard it is to stay simple when writing an application or a library, and explained that the main victims of complexity [...]]]></description>
			<content:encoded><![CDATA[<p>About two months ago, <a href="http://en.wikipedia.org/wiki/Rasmus_Lerdorf">Rasmus Lerdorf</a> gave a talk at FrOSCon entitled <a href="http://talks.php.net/show/froscon08">Simple is Hard</a>. <em>If you don&#8217;t know about it yet I recommend reading the slides before continuing with this article.</em></p>
<p>Rasmus proved how hard it is to stay simple when writing an application or a library, and explained that the main victims of complexity are scalability, performance and security. He used a good number of frameworks to prove his point, showing the overhead in performance for an &#8220;Hello world!&#8221; application along with the graph of all the included files for generating such a simple request. Of course your application will be more than just an &#8220;Hello world!&#8221; but this shows just how much is loaded by the various frameworks for doing such a simple task.</p>
<p>I found the results to be frightening. So I looked at how <em>simple</em> our framework is compared to the others.</p>
<h2>The first day</h2>
<p>After firing siege, I got a very bad result: only around 120 trans/sec when PHP alone did more than 4000 trans/sec. It wasn&#8217;t surprising however, considering no performance optimization was ever done in the project. With the project reaching beta soon, it&#8217;s time to change this and multiply the speed.</p>
<p>You can take a look at the inclued file from that day:</p>
<p><a href="http://blog.extend.ws/wp-content/uploads/2008/11/wee-yesterday.png"><img class="size-medium wp-image-41" title="wee-yesterday" src="http://blog.extend.ws/wp-content/uploads/2008/11/wee-yesterday-300x112.png" alt="Web:Extend's inclued graph, before caching" width="300" height="112" /></a></p>
<h2>Optimizations</h2>
<p>Firing up the XDebug profiler, we could observe this call graph:</p>
<p><a href="http://blog.extend.ws/wp-content/uploads/2008/11/wee-yesterday-callgraph.png"><img class="size-medium wp-image-40" title="wee-yesterday-callgraph" src="http://blog.extend.ws/wp-content/uploads/2008/11/wee-yesterday-callgraph-258x300.png" alt="Web:Extend's call graph, before caching" width="258" height="300" /></a></p>
<p>It&#8217;s easy to see what takes time&#8230; The <code>weeAutoload::addPath</code> method takes 76% of the execution time, and the configuration file loading 12%. Both can be cached and are only subject to change in a development environment or when upgrading, so we can automatically cache them when <code>DEBUG</code> mode is off and ignore the cache otherwise.</p>
<p>The cache method used is pretty simple. The cache files have no expiration time, they&#8217;re just deleted when upgrading an installation to a new version. If something goes wrong, the global constant <code>NO_CACHE</code> will happily ignore the cache files. And in development, cache is ignored as long as you have <code>DEBUG</code> enabled, which you should have all the time when developing.</p>
<p>The first time the script runs the data (an array in both case) is written to the cache file as valid PHP using <a href="http://php.net/var_export">var_export</a>. Subsequent runs will simply <a href="http://php.net/require">require</a> this file to load the cache. To prevent security problems, the file mode is set to 0600, disabling its access to anyone other than the webserver&#8217;s user.</p>
<h2>Today</h2>
<p>Using siege today I get these results:</p>
<pre>essen@karen (0) % siege -c 5 "http://localhost/wee/trunk/" -b -t30s &gt; /tmp/wee-today
** SIEGE 2.66
** Preparing 5 concurrent users for battle.
The server is now under siege...

Lifting the server siege...      done.
Transactions:		       40995 hits
Availability:		      100.00 %
Elapsed time:		       30.16 secs
Data transferred:	        3.44 MB
Response time:		        0.00 secs
Transaction rate:	     1359.25 trans/sec
Throughput:		        0.11 MB/sec
Concurrency:		        4.97
Successful transactions:       40995
Failed transactions:	           0
Longest transaction:	        0.04
Shortest transaction:	        0.00</pre>
<p>Not bad at all, considering all we did really was to cache the autoload and configuration data automatically.</p>
<p>The <em>inclued</em> graph looks like this on the first load:</p>
<p><a href="http://blog.extend.ws/wp-content/uploads/2008/10/wee-inclued-nocache.png"><img class="size-medium wp-image-19" title="wee-inclued-nocache" src="http://blog.extend.ws/wp-content/uploads/2008/10/wee-inclued-nocache-300x98.png" alt="Web:Extend's inclued graph, cache disabled" width="300" height="98" /></a></p>
<p>And like this when the cache files have been generated:</p>
<p><a href="http://blog.extend.ws/wp-content/uploads/2008/10/wee-inclued-cache.png"><img class="size-medium wp-image-23" title="wee-inclued-cache" src="http://blog.extend.ws/wp-content/uploads/2008/10/wee-inclued-cache-300x97.png" alt="Web:Extend's inclued graph, cache enabled" width="300" height="97" /></a></p>
<p>As you can probably guess, we won&#8217;t be able to improve much more on this part of Web:Extend.</p>
<h2>Comparison with other frameworks</h2>
<p>I computed a table with results from a few other frameworks. I used the same method as Rasmus used in his presentation. There&#8217;s probably a few frameworks missing, because they&#8217;re too complex, because they require me to set up a database, or because I missed them. If you would like one added to this table, feel free to give me its name and instructions on how to write an hello world page with it.</p>
<p>For those who really think an &#8220;Hello world!&#8221; proves nothing, I also added an example of Web:Extend with a few queries for comparison. The queries include an INSERT statement recording the hits in a table, along with an UPDATE statement and a SELECT statement both working on a 100K rows table. The UPDATE sets one column of one row to NOW() while the SELECT randomly selects 25 rows and display them in the template. This is not a prepared statement so it can be faster.</p>
<table style="text-align:center" border="1">
<thead>
<tr>
<th>Framework</th>
<th>without APC</th>
<th>with APC, apc.stat=1</th>
<th>with APC, apc.stat=0</th>
<th>with APC, apc.stat=0, % efficiency</th>
</tr>
</thead>
<tbody>
<tr>
<th>PHP</th>
<td>3965.53</td>
<td>4019.18</td>
<td>4018.94</td>
<td>100%</td>
</tr>
<tr>
<th>Akelos</th>
<td>20.28</td>
<td>52.40</td>
<td>52.47</td>
<td>1.3%</td>
</tr>
<tr>
<th>CakePHP</th>
<td>49.26</td>
<td>183.26</td>
<td>181.70</td>
<td>4.5%</td>
</tr>
<tr>
<th>CodeIgniter</th>
<td>224.36</td>
<td>903.99</td>
<td>940.07</td>
<td>23.4%</td>
</tr>
<tr>
<th>Kohana</th>
<td>198.60</td>
<td>596.21</td>
<td>609.00</td>
<td>15.2%</td>
</tr>
<tr style="background:#dfd">
<th>Web:Extend</th>
<td>454.13</td>
<td>1359.25</td>
<td>1344.81</td>
<td>33.5%</td>
</tr>
<tr style="background:#dfd">
<th>Web:Extend w/ queries</th>
<td>223.97</td>
<td>426.77</td>
<td>418.61</td>
<td>N/A</td>
</tr>
<tr>
<th>Zend Framework</th>
<td>53.03</td>
<td>182.11</td>
<td>234.53</td>
<td>5.8%</td>
</tr>
</tbody>
</table>
<p>Tests were done with Ubuntu&#8217;s default PHP configuration file, on Ubuntu.</p>
<p>A few notes about ZF: since it&#8217;s the only framework requiring the setting of include_path, it was tested separately with its library path configured specifically for these tests. The include_path started with the path to the library. Other tests didn&#8217;t have any modification of the configuration.</p>
<p>I was kinda surprised to see Kohana being so slow compared to CodeIgniter. As you might not know Kohana is based off CodeIgniter. Maybe there&#8217;s a reason? If you know something that could make Kohana slower than CodeIgniter, tell me and I can run the test one more time.</p>
<p>As you can see, there&#8217;s a huge difference in performance today between Web:Extend and the other frameworks. Performance-wise, if you&#8217;re running PHP5, Web:Extend is your best bet. If you&#8217;re still running PHP4, CodeIgniter will get the job done.</p>
<h2>Tomorrow</h2>
<p>There&#8217;s still a few things we want to improve in our framework in the next few weeks.</p>
<h3>Lazy-loading of the application&#8217;s modules</h3>
<p>There&#8217;s currently 3 application modules: output, database and sessions. We already do a lazy start of the output module, but not of the 2 others. This means that the application will always connect to the database, even when no query are performed. Or that the application will always start the session, even when you don&#8217;t need it. <em>Unless of course you didn&#8217;t activate these modules in the configuration file at all.</em></p>
<p>With lazy-loading of the modules, we&#8217;ll be able to connect to the database only when <code>weeApp()-&gt;db</code> is called. Or start the session only when <code>weeApp()-&gt;session</code> is called. The modules won&#8217;t be initialized at all if they&#8217;re not required for the current page.</p>
<h3>Send output to browser early</h3>
<p>Currently the framework generates the templates after the request handling ended. Sometimes you might have some heavy operations to do, like logging, saving a search result, doing some maintenance on the database that the user doesn&#8217;t care about. We&#8217;ll make it possible to send the output to the browser as soon as possible and perform these other tasks without making the user wait.</p>
<p>Jayson Minard wrote about this recently at devzone: <a href="http://devzone.zend.com/article/3884-Scaling-Day-By-Day">Do Not Make Users Wait for Things They Do Not Care About</a>.</p>
<p>It might also be interesting to <a href="http://developer.yahoo.com/performance/rules.html#flush">send parts of the page early</a>, like the header of the HTML page.</p>
<h3>Send cache early</h3>
<p>It might be possible to send cached pages directly from the <code>index.php</code> file, which would be really fast but would not be a good choice for all types of applications. We&#8217;re still not decided on this. The pros are that the file would be sent very fast, without loading the framework: it would be almost as fast as fetching an HTML file directly. The cons are that it won&#8217;t be useable for anything other than simple pages, common to every users. This would be a good choice for <em>vitrine</em> websites or any page that doesn&#8217;t require user&#8217;s input. We probably will implement it, since our current cache is already &#8220;restricted&#8221; to common pages. And for other pages the best solution is to use APC or memcache anyway.</p>
<h3>Integrate caching methods semi-transparently in the model</h3>
<p>Allow the developer to cache the results of a query without having to worry about the caching mechanism used (be it APC, memcache or anything else). The framework would choose the best available on the system and provide a standard interface directly in the model classes. For example, instead of <code>$this-&gt;query</code> you could use <code>$this-&gt;queryCache</code> to cache the query automatically, with an optional timeout value passed as parameter. You could also use <code>$this-&gt;deleteCache</code> to delete the cache corresponding to the parameters given. It could be all the cache for the current model, or only part of it. We would of course provide an option to allow you to choose the caching mechanism used in case the framework doesn&#8217;t choose the one you want.</p>
<h3>Automate scaling</h3>
<p>We saw a few days ago how we could analyze all our SELECT queries in one script. This is a functionality offered by our framework today. But this is not enough. We&#8217;re still missing a data generator that can handle relational data generation <em>(if you know a good one we could use, please leave a comment!)</em>; a builtin profiler to test queries and functions; and a tool to create reports based on the data generated by query analyze and overall benchmark. As you already saw, the analyze of the SELECT queries returns a PHP array. This wasn&#8217;t because of laziness, this was because we intend to give you a nice report of these analysis, complete with emphasis on the problematic values (at least for MySQL and PgSQL, anyway).</p>
<h2>Simplicity is a philosophy</h2>
<p>To quote <a href="http://en.wikipedia.org/wiki/Simplicity">wikipedia</a>, <em>Simplicity is the property, condition, or quality of being simple or un-combined. It often denotes beauty, purity or clarity. Simple things are usually easier to explain and understand than complicated ones. Simplicity can mean freedom from hardship, effort or confusion.</em></p>
<p>Code simplicity can be achieved by following a few rules:</p>
<ol>
<li>Define code and naming conventions and stick with it.</li>
<li>Never load what you don&#8217;t need.</li>
<li>Refactoring duplicate code is only worth it if it is used at least 3 times.</li>
<li>Use the <a href="http://php.net/manual/en/book.spl.php">Standard PHP Library</a>.</li>
<li>Use of the right tool for the job. Use SQL for querying databases, XPATH for XML files.</li>
<li>Prefer learning a technology than learning the libraries built around it.</li>
<li>Automatically cache what you can; if you can&#8217;t, provide an easy-to-use interface for caching.</li>
<li>Slow code often means high complexity.</li>
</ol>
<p>And if you ever need guidance in the path of simplicity, ask this <a href="http://en.wikipedia.org/wiki/Mahatma_Gandhi">good man</a>.</p>
<p>Thanks for reading.</p>
<p><em>Disclaimer: If you feel like exploring the framework then have fun, but remember that the documentation is still lacking and that some parts of the API may change before we reach beta. We advise you to wait for the beta release before trying to use it, unless you feel adventurous or just plain curious.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.extend.ws/2008/11/06/rowo-part-2-simple-is-easy/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Report on website optimization &#8211; Part 1</title>
		<link>http://blog.extend.ws/2008/10/11/report-on-website-optimization-part-1/</link>
		<comments>http://blog.extend.ws/2008/10/11/report-on-website-optimization-part-1/#comments</comments>
		<pubDate>Fri, 10 Oct 2008 23:24:29 +0000</pubDate>
		<dc:creator>Loïc Hoguin</dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[Optimization]]></category>
		<category><![CDATA[Web:Extend]]></category>

		<guid isPermaLink="false">http://blog.extend.ws/2008/10/11/report-on-website-optimization-part-1/</guid>
		<description><![CDATA[Sorry for not posting until now. I promise we&#8217;ll update the blog more often from now on.
Today I&#8217;d like to start a series on website optimization. The order in which I will do things is not necessarily the best order to optimize a website. However it will be full of tips and explanations for every [...]]]></description>
			<content:encoded><![CDATA[<p><em>Sorry for not posting until now. I promise we&#8217;ll update the blog more often from now on.</em></p>
<p>Today I&#8217;d like to start a series on website optimization. The order in which I will do things is not necessarily the best order to optimize a website. However it will be full of tips and explanations for every single aspect of website optimization. A better structured article will later be based off this series and put in <a href="http://wee.extend.ws/">Web:Extend</a>&#8217;s <a href="http://wee.extend.ws/wiki/Documentation">documentation</a>. <em>We know, the documentation is pretty empty right now. We intend to fix this before we reach beta.</em></p>
<h2>Context</h2>
<p>I currently have a contract with a client whose website had huge performance issues. Our job was first to port his code to our framework, then optimize both the code and the server configuration. When he contacted me he had peaks of 130 registered users at the same time with loads between 15 and 30. Some pages could take up to 30 seconds to load.</p>
<h2>The rewrite</h2>
<p>So I did just that, port the code to use Web:Extend instead of various <a href="http://pear.php.net/">PEAR</a> and non-pear libraries. At the same time we would remove the biggest problems in the database schema and in the queries. Let me get this straight: the website was slow not because it used PEAR; it was because the queries would sometimes select thousands of rows that would then be filtered by PHP, and because some operations generated a huge number of queries because <a href="http://en.wikipedia.org/wiki/Join_(SQL)">JOINs</a> were not used as often as it should have been. The use of the framework was motivated by the client&#8217;s will to move to PHP5 instead of the now deprecated PHP4, and this framework was chosen because I&#8217;m its main developer and I know it very well.</p>
<p>Once the code was fully ported and tested (fixing at the same time a few bugs in the original code), we put it on the production server. The load reduced to an average of 3 at peak times. That&#8217;s nice, but it can be better. I should point here that any user on the website produce an <a href="http://en.wikipedia.org/wiki/AJAX">AJAX</a> query (producing several SQL queries) every 10 seconds. That means at least 13 queries per second at peak times. It&#8217;s actually more, since that number does not include non-registered users.</p>
<h2>The biggest point of slowness</h2>
<p>The queries were not optimized yet.</p>
<p>We looked into it and decided to first try to find the biggest performance problem. The chat functionality was the biggest problem. Whenever someone posted a new chat message, a noticeable delay would happen. All chat messages since the launch of the website were stored in one table&#8230; containing about 1.5 million rows. The table was big and not properly indexed nor optimized (it&#8217;s still not perfectly optimized but I&#8217;ll get back on this point later). Most chat messages in this table were never used since they were read and long forgotten. We decided to archive the table periodically (deleting wasn&#8217;t an option). A copy of the table was thus created and a cron job would run the PHP code to archive all the read messages sent more than 15 days ago and all unread messages sent more than 3 months ago. This gave us a nice 150 thousand rows on the main table instead of the 1.5 million we started with.</p>
<p>This last point helped a lot; the slowness went away. But this didn&#8217;t fix everything. More queries needed help, and the database schema was still in need of huge changes.</p>
<h2>EXPLAIN ALL QUERIES</h2>
<p><em>This part is very technical. Do not hesitate to post a comment asking for answers or clarification. Thank you.</em></p>
<p>This brings us to the most interesting part of this post. I needed to analyze the <a href="http://en.wikipedia.org/wiki/Select_(SQL)">SELECT queries</a> used by the application. <strong>All</strong> the queries. My first thought was: is there a way to analyze everything in a completely automated way without having to copy/paste the queries or rewrite anything? If such a beast exist, I didn&#8217;t find it. So I wrote it myself.</p>
<p>This website uses <a href="http://mysql.com">MySQL</a>. A good way to check whether your queries are optimized is to run an <a href="http://dev.mysql.com/doc/refman/5.0/en/explain.html">EXPLAIN statement</a>. An EXPLAIN statement is basically just a SELECT query with &#8220;EXPLAIN &#8221; put at the start. Instead of running the query, it will analyze it and return various informations about how MySQL will run it. This basically means that I just have to put &#8220;EXPLAIN &#8221; at the start of all my SQL queries to get a full report on all the queries. And this is basically what I did, but without changing the application&#8217;s code.</p>
<p>In Web:Extend, we used the <a href="http://en.wikipedia.org/wiki/Set_(mathematics)">concept of sets</a> to define the model part of applications. A set contains elements, just like a table contains rows, or like a path contains files, directories or links. You can retrieve either &#8220;exactly 1&#8243; or &#8220;n (with n>=0)&#8221; elements from a set. Let&#8217;s take an example: the <code>users</code> set. This set have one user with nickname <code>"essen"</code>, and 42 users with the <code>"invisible"</code> option activated. In our framework, <code>"essen"</code> is an individual element while the <code>"invisible"</code> users are a subset of the <code>users</code> set. <em>Mathematically, <code>"essen"</code> is both an individual element and a subset, but for the sake of simplicity we consider it to be nothing more than an individual element.</em></p>
<p>Web:Extend defines base classes for both a <a href="http://wee.extend.ws/browser/trunk/wee/model/weeSet.class.php">set</a> and an <a href="http://wee.extend.ws/browser/trunk/wee/model/weeModel.class.php">individual element</a>. All operations performed on a set (e.g. all SELECT statements, along with the UPDATE and DELETE operations potentially affecting n rows) are written in the set class; all operations performed on an individual element (e.g. INSERT, UPDATE, DELETE affecting only 1 row) are written in the element class. When a set returns elements, it creates the correct instance for the element associated with the given set.</p>
<p>You probably understood by now that all SELECT queries are stored in only one type of class, the set classes, and that these classes do not contain any overhead related to the handling of individual elements. In other words, we&#8217;re free to instanciate them at will and call its methods without any worry. Most methods are of the type <em>count</em>, <em>fetch</em> or <em>fetchAll</em>, which respectively count the number of elements in the set (or in a subset), retrieve exactly 1 element and retrieve n elements.</p>
<p>All we have to do now is put an &#8220;EXPLAIN &#8221; at the start of all these queries. Now that we saw how it works, it should be quite easy, right? As long as we resolve the following problems:</p>
<ol>
<li>When fetching exactly one element, the model will return exactly one row and not the result of the query containing all the rows returned by the EXPLAIN query.</li>
<li>When counting rows, the row is fetched from the database and the value returned directly.</li>
</ol>
<p>Let&#8217;s take this code for example:</p>
<pre class="php code">
class demoUsersSet extends weeDbSet
{
	protected $sModel = 'demoUsersModel';

	/**
		@return int The number of users.
	*/

	public function count()
	{
		$a = $this->getDb()->query('
			SELECT COUNT(*) AS c FROM users
		')->fetch();

		return $a['c'];
	}

	/**
		@param $iUserId The user identifier.
		@return demoUsersModel The requested user.
	*/

	public function fetch($iUserId)
	{
		return $this->getDb()->query('
			SELECT * FROM users WHERE user_id=? LIMIT 1
		', $iUserId)->rowClass($this->sModel)->fetch();
	}

	/**
		@return weeDatabaseResults All the users.
	*/

	public function fetchAll()
	{
		return $this->getDb()->query('
			SELECT * FROM users
		')->rowClass($this->sModel);
	}
}
</pre>
<p>We have the <code>array access</code> in <code>count</code> and <a href="http://wee.extend.ws/browser/trunk/wee/db/weeDatabaseResult.class.php#L46">fetch</a> methods to bypass. We are going to bypass them by writing <a href="http://wee.extend.ws/browser/trunk/wee/db/weeDatabaseResult.class.php">weeDatabaseResult</a> child classes that will always return <code>$this</code> for both <code>array access</code> and <code>fetch</code> methods. Let&#8217;s call the resulting class <a href="http://wee.extend.ws/browser/trunk/wee/tests/db/weeExplainSQLResult.class.php">weeExplainSQLResult</a> (no pun intended).</p>
<p>This won&#8217;t be enough, however; we still need to tell the database driver used by the model to return &#8220;EXPLAIN&#8221; result objects instead of normal results. We can do this simply by extending the database driver classes, make the required changes, and tell the model classes to use that driver for their queries. Extending was easy for both <a href="http://wee.extend.ws/browser/trunk/wee/tests/db/weeExplainMySQLDatabase.class.php">MySQL</a> and <a href="http://wee.extend.ws/browser/trunk/wee/tests/db/weeExplainPgSQLDatabase.class.php">PgSQL</a>.</p>
<p>We finished writing the classes that will bypass the execution of all the queries in the model and execute EXPLAIN queries instead.</p>
<h2>All SELECT queries explained</h2>
<p>Let&#8217;s try it!</p>
<pre class="php code">
$oDb = new weeExplainMySQLDatabase(array(
	'host'		=> 'localhost',
	'user'		=> 'wee',
	'password'	=> 'wee',
	'dbname'	=> 'wee_tests',
));

$oSet = new demoUsersSet;
$oSet->setDb($oDb);

foreach ($oSet->count() as $aExplainRow)
	var_dump($aExplainRow);

foreach ($oSet->fetch(42) as $aExplainRow)
	var_dump($aExplainRow);

foreach ($oSet->fetchAll() as $aExplainRow)
	var_dump($aExplainRow);
</pre>
<p>When ran, this code will output this:</p>
<pre class="code">
array(10) {
  ["id"]=>
  string(1) "1"
  ["select_type"]=>
  string(6) "SIMPLE"
  ["table"]=>
  NULL
  ["type"]=>
  NULL
  ["possible_keys"]=>
  NULL
  ["key"]=>
  NULL
  ["key_len"]=>
  NULL
  ["ref"]=>
  NULL
  ["rows"]=>
  NULL
  ["Extra"]=>
  string(28) "Select tables optimized away"
}
array(10) {
  ["id"]=>
  string(1) "1"
  ["select_type"]=>
  string(6) "SIMPLE"
  ["table"]=>
  string(5) "users"
  ["type"]=>
  string(5) "const"
  ["possible_keys"]=>
  string(7) "PRIMARY"
  ["key"]=>
  string(7) "PRIMARY"
  ["key_len"]=>
  string(1) "4"
  ["ref"]=>
  string(5) "const"
  ["rows"]=>
  string(1) "1"
  ["Extra"]=>
  string(0) ""
}
array(10) {
  ["id"]=>
  string(1) "1"
  ["select_type"]=>
  string(6) "SIMPLE"
  ["table"]=>
  string(5) "users"
  ["type"]=>
  string(3) "ALL"
  ["possible_keys"]=>
  NULL
  ["key"]=>
  NULL
  ["key_len"]=>
  NULL
  ["ref"]=>
  NULL
  ["rows"]=>
  string(1) "3"
  ["Extra"]=>
  string(0) ""
}
</pre>
<p>By applying this on all the queries of our application we will get the complete analyze of all our SELECT queries, available for review and optimization to anyone working on the project. These &#8220;EXPLAIN&#8221; classes will allow us to test the results of changes on the database over all the queries rather than looking at them one by one and possibly missing things by making changes that affect a query we already looked at and fixed.</p>
<p>Feel free to use, improve or adapt this method in your projects. If you do, please let us know about it so we can use your improvements.</p>
<h2>Conclusion</h2>
<p>We&#8217;ll take a break from the query optimization in the next post of this series to talk about <a href="http://talks.php.net/show/froscon08/">Simple is Hard</a> and its implications for our framework, our thoughts about it and what are our plans to improve performance as a whole in the framework. <em>I&#8217;ll only say for now that our framework performs really, really well.</em> 3rd part will talk about query optimization and the importance of database design, highlighting errors and explaining good practices.</p>
<p>Thanks for reading, see you soon.</p>
<p><em>Disclaimer: If you feel like exploring the framework then have fun, but remember that the documentation is still lacking and that some parts of the API may change before we reach beta. We advise you to wait for the beta release before trying to use it, unless you feel adventurous or just plain curious.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.extend.ws/2008/10/11/report-on-website-optimization-part-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

