CHAPTER 14 – OPTIMIZING CODE
This section covers techniques for finding miscellaneous optimizations, includ- ing micro-benchmarks, rewriting PHP code in C, and writing procedural ver- sus object-oriented code.
Micro-Benchmarks Often, you may find yourself wondering which approach is the fastest. For example, which is faster--str_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> </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> "; } 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. Fig. 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 sim- ilar 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 opti- mized (or broken!) in a new PHP release.