Section 14.9. Optimizing Code


14.9. Optimizing Code

This section covers techniques for finding miscellaneous optimizations, including micro-benchmarks, rewriting PHP code in C, and writing procedural versus object-oriented code.

14.9.1. Micro-Benchmarks

Often, you may find yourself wondering which approach is the fastest. For example, which is fasterstr_replace() or preg_replace()for a simple replacement? You can find the answer to many of these questions by writing a little micro-benchmark that measures exactly what you are looking for.

The following example is a library file (ubm.php) to run micro-benchmarks, followed by an example benchmark that tells you which is faster:

 <?php register_shutdown_function('micro_benchmark_summary'); $ubm_timing = array(); function micro_benchmark($label, $impl_func, $iterations = 1) {     global $ubm_timing;     print "benchmarking `$label'...";     flush();     $start = current_usercpu_rusage();     call_user_func($impl_func, $iterations);     $ubm_timing[$label] = current_usercpu_rusage() - $start;     print "<br />\n";     return $ubm_timing[$label]; } function micro_benchmark_summary() {     global $ubm_timing;     if (empty($ubm_timing)) {         return;     }     arsort($ubm_timing);     reset($ubm_timing);     $slowest = current($ubm_timing);     end($ubm_timing);     print "<h2>And the winner is: ";     print key($ubm_timing) . "</h2>\n";     print "<table border=1>\n <tr>\n  <td>&nbsp;</td>\n";     foreach ($ubm_timing as $label => $usercpu) {         print "  <th>$label</th>\n";     }     print " </tr>\n";     $ubm_timing_copy = $ubm_timing;     foreach ($ubm_timing_copy as $label => $usercpu) {         print " <tr>\n  <td><b>$label</b><br />";         printf("%.3fs</td>\n", $usercpu);         foreach ($ubm_timing as $label2 => $usercpu2) {             $percent = (($usercpu2 / $usercpu) - 1) * 100;             if ($percent > 0) {                 printf("<td>%.3fs<br />%.1f%% slower",                        $usercpu2, $percent);             } elseif ($percent < 0) {                 printf("<td>%.3fs<br />%.1f%% faster",                        $usercpu2, -$percent);             } else {                 print "<td>&nbsp;";             }             print "</td>\n";         }         print " </tr>\n";     }     print "</table>\n"; } function current_usercpu_rusage() {     $ru = getrusage();     return $ru['ru_utime.tv_sec']         + ($ru['ru_utime.tv_usec'] / 1000000.0); } 

Note

This benchmark library uses the geTRusage() function for measuring consumed CPU cycles. The resolution of the measurements from getrusage() depends on your system setup, but is usually 1/100th of a second (1/1000th of a second on FreeBSD).

This is a potential source of error, so make sure you run your micro-benchmark several times with similar results before accepting the outcome.


Here is the str_replace() versus preg_replace() micro-benchmark:

 <?php require 'ubm.php'; $str = "This string is not modified"; $loops = 1000000; micro_benchmark('str_replace',  'bm_str_replace',  $loops); micro_benchmark('preg_replace', 'bm_preg_replace', $loops); function bm_str_replace($loops) {     global $str;     for ($i = 0; $i < $loops; $i++) {         str_replace("is not", "has been", $str);     } } function bm_preg_replace($loops) {     global $str;     for ($i = 0; $i < $loops; $i++) {         preg_replace("/is not/", "has been", $str);     } } 

The output from this example appears in Figure 14.15.

Figure 14.15. Output from replace micro-benchmark. The percentages in each cell tell you how much faster or slower the previous test was compared to the test to the left.


According to this micro-benchmark, str_replace() is only 20 percent faster than preg_replace() for simple string substitutions.

Micro-benchmarks are best suited for operations that require little or no I/O activity. After you start performing I/O from benchmarks, your results may be skewed; other processes that involve reading or writing to disk may slow down your test, or a database query that is cached in memory could inflate the speed of your benchmark.

It is a good idea to measure several times and verify that you receive similar results each time. If not, what you are doing is not well-suited for a micro-benchmark, or the machine you are running it on could be running with loads that affects the benchmark.

Tip

Don't throw away your micro-benchmarks! Keep and organize them somewhere, so you can run them all again later to see if a function was optimized (or broken!) in a new PHP release.


14.9.2. Rewrite in C

Sometimes, it is just not possible to optimize a piece of PHP code. The code is as fast as it possibly can be in PHP, but it may still be a bottleneck. This is the time to wield your axe, chop it to bits, and rewrite it in C as a PHP extension. If you have some C skills, it's not that hard. Consult Chapter 15, "An Introduction to Writing PHP Extensions," for examples.

14.9.3. OO Versus Procedural Code

PHP has the advantage of not forcing a particular coding style. You can write 100 percent procedural code, or you can go all object-oriented. Most likely, you are going to end up writing code that is somewhere in between procedural and object-oriented, because most of the functionality provided by PHP's bundled extensions is procedural, while PEAR offers OOP interfaces.

From a performance point of view, procedural code is slightly faster. The following example shows another micro-benchmark that compares the performance difference between regular function calls and method calls:

 <?php require 'ubm.php'; class Adder {     function add2($a, $b) { return $a + $b; }     function add3($a, $b, $c) { return $a + $b; } } function adder_add2($a, $b) { return $a + $b; } function adder_add3($a, $b) { return $a + $b; } function run_oo_bm2($count) {     $adder = new Adder;     for ($i = 0; $i < $count; $i++) $adder->add2(5, 7); } function run_oo_bm3($count) {     $adder = new Adder;     for ($i = 0; $i < $count; $i++) $adder->add2(5, 7, 9); } function run_proc_bm2($count) {     for ($i = 0; $i < $count; $i++) adder_add2(5, 7); } function run_proc_bm3($count) {     for ($i = 0; $i < $count; $i++) adder_add3(5, 7, 9); } $loops = 1000000; micro_benchmark("proc_2_args", "run_proc_bm2", $loops); micro_benchmark("proc_3_args", "run_proc_bm3", $loops); micro_benchmark("oo_2_args", "run_oo_bm2", $loops); micro_benchmark("oo_3_args", "run_oo_bm3", $loops); 

Figure 14.16 shows the result.

Figure 14.16. Performance comparison of method and function calls with two or three parameters.


Here, function calls are 1112 percent faster than method calls with both two and three arguments.

Keep in mind that this micro-benchmark only measures the overhead caused by the actual function call (looking up the function/method name, passing parameters, returning a value).

This will be a performance factor if your code has many small functions, which makes the call overhead account for a larger portion of the total execution time.



    PHP 5 Power Programming
    PHP 5 Power Programming
    ISBN: 013147149X
    EAN: 2147483647
    Year: 2003
    Pages: 240

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net