コードの最適化に役立つPHPスクリプトのベンチマークはじめにどのようなソフトウェアも、市場に出て洗練された製品になるまでには、最適化のプロセスを経る必要があります。メモリリークを見つけて製品のパフォーマンスを向上させるのは、多くの作業時間と人的資源を必要とする難しい作業です。最適化という課題においてベンチマークは重要です。個別のコード部品と全体のコードの両方を検証できますし、ベンチマークのレポートや統計データから、実際の実行時のパラメータやパフォーマンスを推測できるからです。PHPではBenchmarkパッケージを使用できます。これはPHPスクリプトや実行する関数のベンチマークに使われるPEARパッケージです。リリースされている最新版は1.2.7(安定版)です。パッケージのダウンロード後、次のようにしてインストールすることができます。 >> pear install Benchmark-1.2.7 PEAR Benchmarkのクラスツリーこのパッケージには、ベンチマーク測定に使用できるいくつかのクラスが含まれています。
Benchmark_TimerクラスBenchmark_Timerクラスには、正確なタイミング情報を返すメソッド一式が含まれています。このクラスで最もよく使われるメソッドのプロトタイプを次に示します。
フィボナッチ数列のベンチマーク(反復の場合)この例では、フィボナッチ数列を反復で求める処理にBenchmark_Timerクラスを適用し、フォーマットされた測定結果を返します。<?php require_once 'Benchmark/Timer.php'; function fibonacci(){ //create an instance of Benchmark_Timer class $timer = new Benchmark_Timer(); //Set "Start" marker $timer->start(); $a=0;$b=1; for ($i = 0; $i < 10; $i++) { $s=$a+$b; //Set the markers fibonacci $timer->setMarker('fibonacci'.$i); $a=$b; $b=$s; } //Set "Stop" marker $timer->stop(); //Returns formatted informations $timer->display(); echo '<pre>'; //Get the profiler info as an associative array $profiling = $timer->getProfiling(); //Display all the information: name, // time, difference between two //consecutive markers and total time print_r($profiling[1]); print_r($profiling[2]); print_r($profiling[3]); echo '</pre>'; } fibonacci(); ?> リスト1も同様のコードですが、こちらはフィボナッチ数列を再帰で求める処理についてのベンチマークテストです。 リスト1 フィボナッチ数列のベンチマーク(再帰の場合)(Fibonacci_recursive_timer.php)
<?php require_once 'Benchmark/Timer.php'; //create an instance of the Benchmark_Timer class $timer = new Benchmark_Timer(); //Set "Start" marker $timer->start(); function fibonacci($n){ if(($n>=0)and($n<2)) {return 1;} else {return fibonacci($n-1)+fibonacci($n-2);} } for ($i = 0; $i < 10; $i++) { //Set the markers fibonacci $timer->setMarker('fibonacci'.$i); fibonacci($i); } //Set "Stop" marker $timer->stop(); //Returns formatted informations $timer->display(); echo '<pre>'; //Get the profiler info as an associative array $profiling = $timer->getProfiling(); //Display all the information: name, time, //difference between two consecutives markers and //total time print_r($profiling[1]); print_r($profiling[2]); print_r($profiling[3]); echo '</pre>'; ?> ベンチマーク結果の出力この反復と再帰によるフィボナッチ数列プログラムは、どちらもフォーマットされたテーブルと連想配列という2つの形式で結果を出力します。テーブル形式の結果には、各マーカーの名前と、そのマーカーに到達するまでの経過時間(Time Index)、前のマーカーからそのマーカーに達するまでに要した時間(Ex Time)、さらには総時間に対するEx Timeの割合(%)が表示されます。 反復の場合の結果は次のとおりです。
Array
(
[name] => fibonacci0
[time] => 1209633208.32546500
[diff] => 0.000070
[total] => 0.000070
)
Array
(
[name] => fibonacci1
[time] => 1209633208.32549400
[diff] => 0.000029
[total] => 0.000099
)
Array
(
[name] => fibonacci2
[time] => 1209633208.32551600
[diff] => 0.000022
[total] => 0.000121
)
Array
(
[name] => fibonacci0
[time] => 1209633188.10322500
[diff] => 0.000165
[total] => 0.000165
)
Array
(
[name] => fibonacci1
[time] => 1209633188.10330500
[diff] => 0.000080
[total] => 0.000245
)
Array
(
[name] => fibonacci2
[time] => 1209633188.10335800
[diff] => 0.000053
[total] => 0.000298
)
Benchmark_IterateクラスBenchmark_Iterateクラスには、関数のベンチマークに使用できるメソッドが2つあります。
runメソッドで4回呼び出します。最後に結果を出力します。<?php require_once 'Benchmark/Iterate.php'; //create an instance of Benchmark_Iterate class $benchmark = new Benchmark_Iterate; function example($string) { print $string . '<br>'; } //Benchmarks the example function $benchmark->run(4, 'example', 'Octavia'); //Returns benchmark result $result = $benchmark->get(); echo 'The number of iterations is '.$result['iterations'].'<br />'; echo 'The mean is: '.$result['mean']; ?> Octavia Octavia Octavia Octavia The number of iterations is 4 The mean is: 0.000064 反復の結果
1 1 2 3 5 8 13 21 34 55 89 The execution time of 1 iteration: 0.000223 The number of iterations is: 1 The mean is: 0.000223 再帰の結果
1 1 2 3 5 8 13 21 34 55 89 The execution time of 1 iteration is: 0.001135 The number of iterations is: 1 The mean is: 0.001135 リスト2 関数レベルのベンチマーク(反復の場合)(fibonacci_iterative.php)
<?php require_once 'Benchmark/Iterate.php'; //create an instance of the Benchmark_Iterate class $benchmark = new Benchmark_Iterate; //this function implements the iterative solution //for the Fibonacci problem function fibonacci(){ $a=0;$b=1; echo $b.' '; for ($i = 0; $i < 10; $i++) { $s=$a+$b; echo $s.' '; $a=$b; $b=$s; } echo '<br />'; } //Benchmarks the Fibonacci function $benchmark->run(1, 'fibonacci'); //Returns benchmark result $result = $benchmark->get(); //Returns execution time of iteration 1 using the $result variabile echo 'The execution time of 1 iteration is: '.$result[1].'<br />'; //Returns the number of iterations using the $result variabile echo 'The number of iterations is: '.$result['iterations'].'<br />'; //Returns the mean execution time echo 'The mean is: '.$result['mean']; ?> リスト3 関数レベルのベンチマーク(再帰の場合)(fibonacci_recursive.php)
<?php require_once 'Benchmark/Iterate.php'; //create an instance of the Benchmark_Iterate class $benchmark = new Benchmark_Iterate; echo 'The first 10 terms of the Fibonacci series are: '; //this function implements the recursive solution //for the fibonacci problem function fibonacci($n){ if(($n>=0)and($n<2)) {return 1;} else {return fibonacci($n-1)+fibonacci($n-2);} } for ($i = 0; $i <= 10; $i++) { echo fibonacci($i).' '; } //Benchmarks the Fibonacci function $benchmark->run(2, 'fibonacci'); //Returns benchmark result $result = $benchmark->get(); echo 'The execution time of 1 iteration is: '.$result[1].'<br />'; echo '<br />'.'The number of iterations is: ' .$result['iterations'].'<br />'; echo 'The mean is: '.$result['mean']; ?> Benchmark_ProfilerクラスBenchmark_Profilerクラスには、フォーマットされた測定結果を返すメソッド一式があります。このクラスで最もよく使われるメソッドのプロトタイプを次に示します。
反復の場合
<?php require_once 'Benchmark/Profiler.php'; //create an instance of Benchmark_Iterate class $profiler = new Benchmark_Profiler(TRUE); function fibonacci(){ global $profiler; $a=0;$b=1; for ($i = 0; $i < 10; $i++) { //Enters code section $profiler->enterSection('fibonacci'.$i); $s=$a+$b; $a=$b; $b=$s; //Leaves code section $profiler->leaveSection('fibonacci'.$i); } //Returns formatted profiling information $profiler->display(); return; } fibonacci(); ?> 再帰の場合
<?php require_once 'Benchmark/Profiler.php'; //create an instance of Benchmark_Iterate class $profiler = new Benchmark_Profiler(TRUE); //this function implements the recursive solution //for the fibonacci problem function fibonacci($n){ if(($n>=0)and($n<2)) { return 1; } else { return fibonacci($n-1)+fibonacci($n-2); } } global $profiler; for ($i = 0; $i < 10; $i++) { //Enters code section $profiler->enterSection('fibonacci'.$i); fibonacci($i).'<br />'; //Leaves code section $profiler->leaveSection('fibonacci'.$i); } //Returns formatted profiling information $profiler->display(); return; ?> 反復の結果
再帰の結果
ベンチマーク結果をグラフで取得するベンチマーク結果の生の数値も役に立ちますが、棒グラフや円グラフのような形式で目にした方が役に立つ場合が少なくありません。fibonacci_iterative_timer.phpアプリケーションを拡張すると、Benchmark_Timerクラスを使ったベンチマークスクリプトにグラフ機能を追加できます。ここではSVGを使用して、ループの各反復に要した時間を視覚的に示す棒グラフと円グラフの画像を取得します。そのためには、測定結果の最後の列をグラフに描き、「ex time」列を視覚的に表現します。<?php require_once 'Benchmark/Timer.php'; function fibonacci(){ //create an instance of Benchmark_Timer class $timer = new Benchmark_Timer(); //Set "Start" marker $timer->start(); $a=0;$b=1; for ($i = 0; $i < 15; $i++) { $s=$a+$b; //Set the markers $timer->setMarker('f'.$i); $a=$b; $b=$s; } //Set "Stop" marker $timer->stop(); //Returns formatted informations echo '<table>'; echo '<tr>'; echo '<td>'; $timer->display(); echo '</td>'; echo '<td>'; //Get the profiler info as an associative array $profiling = $timer->getProfiling(); //serialize the $profiling $ser = serialize($profiling); // Display all the information: name, time, // difference between two consecutive // markers, and total time echo "<embed src='DiagramSvg.php?profile=".$ser."' width='900' height='500' type='image/svg+xml' pluginspage='http://www.adobe.com/svg/viewer/install/' />"; echo '</td>'; echo '</tr>'; echo '</table>'; } //start the process fibonacci(); ?> 図1に、測定結果のテーブルとグラフを示します。Fibonacciメソッドの15回の反復結果を視覚的に示すことができます。 リスト4 ベンチマーク結果の生データから円グラフと棒グラフを生成するコード(DiagramSvg.php)
<?php //include Graph.php and Canvas.php require_once 'Image/Graph.php'; require_once 'Image/Canvas.php'; $getser = $_GET['profile']; $results = array(); $results = unserialize($getser); //create a canvas $Canvas = & Image_Canvas::factory( 'svg', array('width' => 800, 'height' => 440)); //create the graph $Graph =& Image_Graph::factory('graph', $Canvas); //set Helvetica font $Font =& $Graph->addNew('font', 'Helvetica'); //font size 10 pixels $Font->setSize(10); //set the font to the graph $Graph->setFont($Font); //create the plotarea $Graph->add( Image_Graph::vertical( Image_Graph::vertical( $Title = Image_Graph::factory( 'title', array('Timer (the last column from the table) ', 15)), $SubTitle = Image_Graph::factory( 'title', array('Bar and pie representation', 11)), 100), $Plotarea = Image_Graph::factory('plotarea'), 8)); //set the title and subtitle at the right of the graphic $Title->setAlignment(IMAGE_GRAPH_ALIGN_RIGHT); $SubTitle->setAlignment(IMAGE_GRAPH_ALIGN_RIGHT); //add a bar grid $Grid =& $Plotarea->addNew('bar_grid', IMAGE_GRAPH_AXIS_X); //add a gradient background $Grid->setFillStyle(Image_Graph::factory('gradient', array(IMAGE_GRAPH_GRAD_RADIAL, 'white', 'lightgrey'))); //add a bar graph $dataset = & Image_Graph::factory('dataset'); for($i=0;$i<sizeof($results);$i++) { //computing the graphic's values $dataset->addPoint($i,(($results[$i][diff]*100.0) / ($results[sizeof($results)-1][total]))); } $Plot =& $Plotarea->addNew('Image_Graph_Plot_Bar', &$dataset); $Plot->setLineColor('white'); $Plot->setFillColor('#cc0000'); $Marker =& Image_Graph::factory( 'Image_Graph_Marker_Value', IMAGE_GRAPH_VALUE_Y); $Plot->setMarker($Marker); $Marker->setFillColor('pink'); $Marker->setBorderColor('white'); $Plot =& $Plotarea->addNew('Image_Graph_Plot_Pie', &$dataset); $Plot->setLineColor('pink'); $Plot->setFillColor('#cc0000'); $Marker =& Image_Graph::factory( 'Image_Graph_Marker_Value', IMAGE_GRAPH_VALUE_Y); $Plot->setMarker($Marker); $Marker->setFillColor('pink'); $Marker->setBorderColor('white'); //show axis $AxisY = $Plotarea->getAxis(IMAGE_GRAPH_AXIS_Y); $AxisY->showArrow(); $Array = array(); for($i=0;$i<sizeof($results);$i++) { $Array[$i] = $results[$i][name]; } $AxisX = $Plotarea->getAxis(IMAGE_GRAPH_AXIS_X); $AxisX -> setDataPreprocessor( Image_Graph::factory( 'Image_Graph_DataPreprocessor_Array', array($Array))); //display the graph $Graph->done(); ?> 著者紹介Octavia Andreea Anghel(Octavia Andreea Anghel)
経験豊富なPHP開発者。現在は、国内外のソフトウェア開発コンテストに参加するプログラミングチームの主任トレーナーを務める。国レベルの教育プロジェクト開発のコンサルティングも担当している。共著書に『XML technologies?XML in Java』があり、XML部分の執筆を担当。PHPやXMLのほか、ソフトウェアアーキテクチャ、Webサービス、UML、ハイパフォーマンスな単体テストについても関心を寄せている。
関連記事 最新トップニュース
|
|