Thursday, November 26, 2009

Checking PHP script performance with Xdebug

Xdebug [http://www.xdebug.org/] is a useful tool to debug PHP scripts. An interesting feature is the script profiling.

If the option is enabled, Xdebug will be able to trace and save information (time, details) about all the functions/methods called in the script (CLI or Apache).

The aim of the profiling is mainly recognizing bottlenecks or simply what parts of the code that are slow.

In order to analyze the log file created, use KCacheGrind or WinCacheGrind (see screenshots below).

Setup and docs at [http://www.xdebug.org/docs/profiler].

Configuration for Wamp (PHP 5.3)

#php.ini
[xdebug]
zend_extension=c:/wamp/bin/php/php5.3.0/ext/php_xdebug-2.0.5-5.3-vc6.dll
xdebug.profiler_enable = 1
xdebug.profiler_output_dir=C:/wamp/www/profile/
xdebug.remote_enable=on
xdebug.remote_handler=dbgp
xdebug.remote_host=localhost
xdebug.remote_port=9000
xdebug.remote_mode=req

Screenshots


Thursday, November 19, 2009

Profiling MySQL

To analyze the db server usage in a complex PHP application, the first step is to profile the db server.
There are lots of tools to profile, but I think it's very easy to make a customized code to save the data really needed.
The idea is save information about some queries in the production environment (about 1% of the queries is usually enough, depending on the traffic).


MySQL profiling

Hoping there is a class used to manage queries (or at least mysqli class), it doesn't take long to replace a function that manages the queries with something similar to the following code (written in a simple way to show the idea):


class DB extends mysqli {
...
function query ($q) {

$start = microtime(1);
$this->query($q);
$wtime = microtime(1) - $start;
#save 1% of the queries + info
if ( rand(0,100)<1>query("INSERT DELAYED INTO `ProfileData` (q,wtime,created,...) ($q,$wtime, NOW(), ...) ");
}

}
...
}


What other info to save ? some ideas:
  • client IP
  • other $_SERVER info: user-agent, request_method, etc...
  • PHP backtrace (to understand which line launched the query)
  • web server load
  • mysql server load
  • ...
How to analyze results making queries on the `ProfileData` table.
example: queries grouping by query and showing the average time of the queries. In this way, you can find what queries are the slowest ones.



-- select the slowest queries (average time) in the last 24 h
-- exclusion of the queries executed only once to exclude missing sql cache
SELECT `q`,AVG(`wtime) as "medium time", COUNT(`id`) as "occurences"
FROM `ProfileData`
WHERE `created` > DATE_ADD(NOW(), INTERVAL -1 DAY)
GROUP BY `q`
HAVING COUNT(`id`) > 2
ORDER BY AVG(`wtime) DESC

Simple effective PHP debugging + backtracking

During PHP debugging I often need to debug complex data. Is not always possibile to use Xdebug and the IDE debugging features with MVC frameworks, and also some arrays/object are too big and unhandy for FirePHP.

A valid solution might be a traditional "print_r"/"var_dump" + "exit"
Two problems:
1) accidental commits to the staging/production environment.
2) it takes time to understand where they are placed in the code, also because of the "exit".

Solutions:
Make a function to debug that
1) use a (external) constant (define) that define what is the environment and return without debugging and exiting if the environment is not the localhost one.
2) print the backtrace to easily find and remove the "breakpoints"

Code:

function pd($var, $useVarDump=false, $exit=true){

if (IS_PRODUCTION_ENV) return;
echo '<pre>';
if ($useVarDump) var_dump($var); else print_r($var);
echo "\n\nBACKTRACE:";
print_r(array_slice( debug_backtrace(false),1) ;
echo '</pre>';
if ($exit) exit;

}

MySQL dump importing

Today I realized that "mysqlimport" is not working as expected on Wamp environment.
A working way to import a sql/dump file is to use the "mysql" executable

#localhost
mysql --u root -p --user=root --force [DBNAME] < [FILE.SQL]

Monday, November 16, 2009

PHP 5.3

I've just read this pdf from I.A.'s blog about PHP 5.3 performances.
My comments:

Performances
What I really consider good is the performance increasing (5/10%) that include a smarter behaviour with require/inclusion, smaller binary size and better stack performance.

Features
- Namespaces are OK, but not really necessary. A good code can be written also without them.
- I think the best feature is the late static binding. It was the only big lack about the PHP OO.
- Also closures are sometimes useful to write a clearer code. I've tested their performances (*) and it seems there are no decreasing using them, that's cool.
(*) array_map(function ($n){return($n * $n * $n);}, array(1, 2, 3, 4, 5)); #
- "goto": its utility (especially in a OO language!) doesn't make any sense to me. Very bad code readability with it.
- MySQLInd sounds very interesting to have better performances with MySQL. Client side query cache, written for PHP (not C/C++), performance statistics for bottle-neck analysis [read here]... wow ! I'll probably test it soon although (according to some blog posts) the performance increasing seems not very high.
- Hundreds of bug fixing and improvements, including Directory iterator and date functions => well done PHP community !

Tuesday, November 3, 2009

How to optimize PHP applications

There are lots of advices on the web about how to speed up PHP applications.
The best reading I've found are written by Ilia Alshanetsky [blog]:

PHP & PERFORMANCE [pdf]
By: Ilia Alshanetsky

Common Optimization Mistakes [pdf]
PHP Quebec 2009

Enjoy !
 

PHP and tips|PHP