Tuesday, February 03, 2009

How to reduce load and response time in PHP in five minutes

Request time is proportional to server load. If the application response time is big so will be the server's load. To reduce server load, reduce the wait time in the application's response time to serve the request. Below are some steps that I took to remove 1.8 ms overhead on every request to my web server farm.

Tools Needed:

Use vi to look at your include path in php.init.
Next use top to find which apache process is consuming the most cpu resources.
Use strace -p [TOP HTTPD PROCESS] -T (-T is for deltas).

In my example the include path is

lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000033>
lstat("/usr/share", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000034>
lstat("/usr/share/pear", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000034>
lstat("/usr/share/pear/ams", 0x7fbfff1690) = -1 ENOENT (No such file or directory) <0.000033>
open("/usr/share/pear/ams/include/FreqCapInfo.php", O_RDONLY) = -1 ENOENT (No such file or directory) <0.000036>
lstat("/usr", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000037>
lstat("/usr/lib64", {st_mode=S_IFDIR|0755, st_size=65536, ...}) = 0 <0.000034>
lstat("/usr/lib64/php", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000034>
lstat("/usr/lib64/php/pear", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000038>
lstat("/usr/lib64/php/pear/ams", 0x7fbfff1690) = -1 ENOENT (No such file or directory) <0.000037>
open("/usr/lib64/php/pear/ams/include/FreqCapInfo.php", O_RDONLY) = -1 ENOENT (No such file or directory) <0.000038>
open("/var/www/html/ams/include/FreqCapInfo.php", O_RDONLY) = 24 <0.000043>
fstat(24, {st_mode=S_IFREG|0775, st_size=6707, ...}) = 0 <0.000031>
stat("./ams/include/FreqCapInfo.php", 0x7fbfff4778) = -1 ENOENT (No such file or directory) <0.000037>
stat("/usr/share/pear/ams/include/FreqCapInfo.php", 0x7fbfff4778) = -1 ENOENT (No such file or directory) <0.000038>
stat("/usr/lib64/php/pear/ams/include/FreqCapInfo.php", 0x7fbfff4778) = -1 ENOENT (No such file or directory) <0.000036>
stat("/var/www/html/ams/include/FreqCapInfo.php", {st_mode=S_IFREG|0775, st_size=6707, ...}) = 0 <0.000040>
close(24) = 0 <0.000033>
mlock(0x552b876be0, 24) = 0 <0.000092>
mlock(0x552b8df910, 10624) = 0 <0.000038>
munlock(0x552b876be0, 24) = 0 <0.000036>
munlock(0x552b8df910, 10624) = 0 <0.000032>
mlock(0x552b876be0, 24) = 0 <0.001707>
mlock(0x552b8df910, 10624) = 0 <0.000009>
munlock(0x552b876be0, 24) = 0 <0.000007>
munlock(0x552b8df910, 10624) = 0 <0.000007>

Looking at the strace, 15 unneeded system calls are made on every request, each request roughly takes 30 micro seconds, for a total of a few ms wasted on every request. False positives are adding overhead to ever requests since the include path is not optimized. A Bloom Filter in Shared Memory would be perfect for for this part of PHP-but that's besides the point.

So, change your include path

For my example I changed the include path to the above. In my environment we don't do many PEAR loads, so it makes sense to use our directories 1st.

By doing this 15 erroneous system calls have been removed. Note: If you use the php feature __autoload make sure to protect your calls with file exist or you may be doing a require once on a file that is in a different directory which is a PHP fatal Error.

UPDATED: Some more goodies to reduce load on PHP boxes:

For PHP 5.2 there is a nice new feature that you can play with

realpath_cache_size. This is a directive native to PHP by default it's set to 16K, this means that PHP expects there to be very few files, but big files. In most environments I have been in this is not the case. The case is there are a lot of files, a lot of directories thus 16K is not enough.

I've tested a few values but 128K seems to be my sweet spot.

realpath_cache_size = 128K


Anonymous said...

this is my test result:
lstat64("/data1", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000006>
lstat64("/data1/lighttpd", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000006>
lstat64("/data1/lighttpd/htdocs", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000010>
lstat64("/data1/lighttpd/htdocs/space", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 <0.000007>
lstat64("/data1/lighttpd/htdocs/space/utility", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0 <0.000007>
lstat64("/data1/lighttpd/htdocs/space/utility/formatter", {st_mode=S_IFDIR|0777, st_size=4096, ...}) = 0 <0.000007>
lstat64("/data1/lighttpd/htdocs/space/utility/formatter/JsonFormatter.php", {st_mode=S_IFREG|0777, st_size=517, ...}) = 0 <0.000010>

each syscall costs no more than 10 us, should I change it to absolute path?

Dathan said...

Your wasting only 60 us, I wouldn't bother. If you can't reduce the pageload by a average of a millisecond it may not be worth it.

wikiplugs said...
This comment has been removed by a blog administrator.