Optimizing your web server: Part 4b – XDebug Profiler

November 23rd, 2009 by Crazy No comments »

Here we are, part 4b. By using XDebug, a pecl extension, our php code can be analyzed pinpointing the slower functions.

Installing XDebug is easy:

pecl install xdebug

Now, you’ll have to enable it in the php.ini file:

extension=xdebug.so
xdebug.profiler_enable=1
xdebug.profiler_output_name=profiler-%s-%t.cache
xdebug.profiler_output_dir=/tmp/xdebug

Don’t forget to create the directory and give apache enough permissions to write in it.

So after restarting apache you’ll see new files showing up under ‘/tmp/xdebug/’.
These are, as the XDebug documentation says ‘cachegrind compatible files’.

As I’m using putty on windows to connect to my webserver and the webserver does not run a GUI, I’ll be using wincachegrind to display the information.

To make it easy for myself, I use winscp and make it so that the cachegrind files are opened with WinCacheGrind.
Setting this up goes beyond the scope of this post and greatly depends on your setup/way of working.

Anyway, up till now we managed to install XDebug, enabling the extension, generate the cachegrind files and now opening them with WinCacheGrind.

Upon opening such a file in wingrind, you’ll see something similar to this:

main(click to enlarge)

As this blog post was made while I was profiling one of my own sites, I’ll use a real world example.
In the right hand side, the {main} takes 89ms to process.

Let us expand this to see what function is taking the most time.
functions

When looking at the functons that take the most time. I noticed, as you can see in the screenshot above, the Component->startup and the beforeFilter both take 13ms.
The component is the cakephp cookie class, it seems the startup of this class takes 12ms. To be exact, it’s the decrypting of the cookie that takes 11ms.
The same is true for the beforeFilter, in that method I read the cookie, taking 12ms, because it decrypts again.

cookie

After seeing this, I changed the code so that on the first hit on the site the information in the cookie is is copied into the session, as this was super fast(less then 1ms). Saving me 2 times 12ms.

That comes down to 27% faster!

To put things even more in context. This was a base class, all my pages inherited from that class, so it saved me those 24ms on EACH request.

Imagine doing that to all pages, or one of those heavy visited pages on your own site.
Worth the 10 minutes it takes to install XDebug with WinCacheGrind, no?

Optimizing your web server: Part 1 – Gzip
Optimizing your web server: Part 2 – Keep Alives
Optimizing your web server: Part 3 – Opcode Caching
Optimizing your web server: Part 4a – PHP
Optimizing your web server: Part 4b – XDebug Profiler

Optimizing your web server: Part 4a – PHP

November 23rd, 2009 by Crazy No comments »

Let me start by saying, I won’t bore you with the list of what I call stupid optimizations.

That’s why I call this 4a, I recommend skipping 4a and go directly to 4b :)

When I first when on my quest to optimize my code, I was disappointed at what I found.
Let me give you a short list of ‘tips’ you find when searching for ‘php optimize code’.

Some have a little value, others are crap.
The results will state things like this:

  • echo vs print
  • pass objects by reference
  • remove comments from your code
  • use single quotes
  • error suppression with @
  • use assosiative arrays
  • use for loops with the count before the loop
  • unset your variables

Now, seriously, the echo vs print again??, yes, totally worthless. If I want to make my site faster, there are better ways then to replace print with echo, for the amazing 0.001 second.

The pass objects by reference, this one is true, but only IF you know what you’re doing. For base types even if you pass by reference it won’t make a difference. Only use it for objects and even then, you have to watch out, in many cases you’ll actually slow your script down by forcing a copy when it wouldn’t otherwise be made.

Removing comments from your code. Well, I had a good laugh with this one. If you’re going to do that, then you might want to install an opcode cacher, see my other blog posts about them. They don’t cache this, so instead, document your code!, as much as possible. More (good)comment makes it readable 5 to 10 years from now, not to mention causing less issues/bugs in the process.

Use single quotes. This is true, if you’re interested in that soooo little difference. Personally I made a habit of using the single quotes, but that’s just so that I don’t have to press the shift key to type them(Yea, I’m lazy).

Error suppression with @. It is slow, yes, but I never use the @. When writing your code, error handling is part of it. You should not suppress them, you should fix them!. And if errors do happen, then use a try/catch and handle the error, do NOT ignore errors.

Use assosiative arrays, I hear that using [5] is 7 times slower then ['me'], haven’t run any benchmarks, but might be a good idea to use this when having big arrays.

Use for loops with the count before the loop, when using for($i=0; $i< count(array); $i++) the count(array) is executed every time. It’s useful to put this outside of the for loop. But then again, you could count backwards, $i=count(array);0<=$i; $i–, or something similar.

The last one on the list, unset your variables, is only if you use var’s with allot of data in them, or when using big objects.
You won’t win anything if you unset ALL your variables, your script would be spending more time unsetting them then actually keeping them in memory.
This is more the case when doing large queries on a database for example. Unsetting a couple of MB of data might be a good idea :)
If my memory serves me right, php6 will implement garbage collection, making this unnecessary.

So, this post was about optimizing your php code right?, well, instead of doing all those ridicules things listed above, we’ll do it the right way. A profiler!, see where your code is slow, and try and make it faster on those places!.
Optimizing your code that way is WAY more efficient then doing all those small silly things.

Don’t get me wrong, if you like coding in those small optimizations, go ahead. I just find it stupid to go and change those things afterwards.

So on to part 4b :) , where I’ll be using XDebug to find the slow spots in the php code.

Optimizing your web server: Part 1 – Gzip
Optimizing your web server: Part 2 – Keep Alives
Optimizing your web server: Part 3 – Opcode Caching
Optimizing your web server: Part 4a – PHP
Optimizing your web server: Part 4b – XDebug Profiler

Optimizing your web server: Part 3 – Opcode Caching

November 22nd, 2009 by Crazy No comments »

In one of my previous blog posts I talked about using APC as an opcode cacher to speed up your site.

I thought it was worth mentioning a second time. This greatly improves the performance.

You can read more about the different opcode cachers and how to configure them here:
http://blog.crazytje.be/php-opcode-caching-why-use-an-opcode-cacher/

Part 4 will talk about optimizing your php code with a profiler(XDebug), expect more soon :)

Optimizing your web server: Part 1 – Gzip
Optimizing your web server: Part 2 – Keep Alives
Optimizing your web server: Part 3 – Opcode Caching
Optimizing your web server: Part 4a – PHP
Optimizing your web server: Part 4b – XDebug Profiler

Optimizing your web server: Part 2 – Keep Alives

November 22nd, 2009 by Crazy No comments »

Once your site begins to grow, it will most likely contain dozens of images, css and js files, and many other requests.
Keep alives allows those requests to happen over the same tcp/ip connection.

So what is keep alive exactly?
As mentioned before, keep alive is an option in the apache config, this was added to the http protocol to solve a huge performance problem.

In the original http protocol a new tcp connection needed to be set up for each request(image, css, html file).
Due to the nature of the tcp protocol it takes a while to set up a connection(3 packets are always send, SYN/SYN-ACK/ACK). If the latency to the server is 200ms, then it takes about 600ms before a connection is set up and data can be requested.

By using keep alives, this can be minimized.
So on almost all of the webservers it gives a performance boost, as sites nowadays exist out of allot of files.

Enabling keep alives is pretty easy.
Apache has 3 settings that involve keep alive:

  • KeepAlive
  • KeepAliveTimeout
  • MaxKeepAliveRequests

The first “KeepAlive” has has two settings, On or Off, obviously we’re going to put in on ‘On’.

The second “KeepAliveTimeout”, is the number of seconds a connection stays open for the client.
Setting this greatly depends on your website. If it loads in less then a second, settings this on 2 or 3 seconds is enough.
I’ve put this on 5(default is 30) in my configuration and the site in question loads under one second.

The third “MaxKeepAliveRequests”, are the maximum number of requests that can be made on an open connection. This can be put on ‘0′ meaning unlimited, or if you really want to put a number, take a look at how many requests your site makes.

Optimizing your web server: Part 1 – Gzip
Optimizing your web server: Part 2 – Keep Alives
Optimizing your web server: Part 3 – Opcode Caching
Optimizing your web server: Part 4a – PHP
Optimizing your web server: Part 4b – XDebug Profiler

Optimizing your web server: Part 1 – Gzip

November 22nd, 2009 by Crazy No comments »

This will be the first blogpost in a compilation of posts about optimizing your webserver.

Everyone likes their website to go fast right?, So did I.

Something that can give you an easy performance boost is gzip compression.

This compresses the data that is being send to the user with mod_deflate(apache).
Because of the way xml and html is structured, it contains allot of repeating data.
So this means it’s the perfect candidate to compress.

By enabling compression your webpages can become up to 80% smaller.
A downside is that it does use a little cpu and memory to do the compression, this scares allot of people, thinking that the machine isn’t powerfull enough to do the gzip compression.
But, this uses almost nothing if configured correctly. Make sure you only compress files that benefit from it, for example xml, html, css, js, … and leave out those that do not, jpg, gif, png, mpg, mov, flv, zip, rar, …

Images and movies are already compressed, so compressing them a second time would be pointless.

An extra advantage that gzip gives is that requests are processed faster(files are smaller), because of this, extra cpu time and memory becomes available faster than before. So the cpu time required to do the compression of the files is won back.

A practical example of http://crazytje.be:

Original Size: 42.91 KB
Gzipped Size: 8.08 KB
Data Savings: 81.17%

So how do we go about configuring gzip?
Normally mod_deflate is already supplied with your apache installation, it only requires you to enable it:

LoadModule deflate_module modules/mod_deflate.so
<Location />
# Insert filter
SetOutputFilter DEFLATE
 
# Netscape 4.x has some problems...
BrowserMatch ^Mozilla/4 gzip-only-text/html
 
# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0[678] no-gzip
 
# MSIE masquerades as Netscape, but it is fine
# BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

# NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
# the above regex won't work. You can use the following
# workaround to get the desired effect:
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
 
    # Don't compress
    SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI dl.php$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI FileDownload$ no-gzip dont-vary
 
    SetEnvIfNoCase Request_URI \.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.pdf$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.avi$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.mov$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.mp3$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.mp4$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.rm$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.plist$ no-gzip dont-vary
 
 
    AddOutputFilterByType DEFLATE text/plain text/xml application/xml application/xhtml+xml text/javascript text/css application/x-javascript
    AddOutputFilterByType DEFLATE application/x-httpd-php
    AddOutputFilterByType DEFLATE application/x-httpd-fastphp
    AddOutputFilterByType DEFLATE application/x-httpd-eruby
    AddOutputFilterByType DEFLATE text/html
 
    SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
 
    # Make sure proxies don't deliver the wrong content
    Header append Vary User-Agent env=!dont-vary
</Location>

I don’t think it requires any explination, depending on the content being served you might want to add some extensions to compress or not to compress.

Optimizing your web server: Part 1 – Gzip
Optimizing your web server: Part 2 – Keep Alives
Optimizing your web server: Part 3 – Opcode Caching
Optimizing your web server: Part 4a – PHP
Optimizing your web server: Part 4b – XDebug Profiler