【完全版】Pux - A High Performance PHP Routerのルーティング性能をベンチマークしてみた

Symfony routerより静的なルーティングで48.5倍、正規表現のルーティングでも31倍速いというPuxというPHPのルータに関するベンチマークのまとめです。

と続いたシリーズの最終回です。上記の記事への補足でもあります。

ベンチマーク環境

OS: Ubuntu 12.04
Apache: 2.4.7
PHP: 5.5.6 (32bit)
Phalcon: 1.2.5
Pux: 1.3.2 (C extension: 1.3.1, GitHub master 9b07e3106273a5823226051e71cca1e66628b431 Wed Jan 15 16:31:03 2014 +0800)

OrePux Basic

OrePhalconをPuxに移植したOrePuxです。

index.php:

<?php

ini_set('display_errors', 1);
error_reporting(-1);

require __DIR__ . '/../vendor/autoload.php';

$mux = new \Pux\Mux();
$mux->get('/hello/:name', ['hello','say']);
$route = $mux->dispatch($_SERVER['PATH_INFO']);

$controller = ucfirst($route[2][0]);
$action     = $route[2][1];
$params     = $route[3]['vars'];

try {
    $controllerFilePath = __DIR__ . '/../app/controllers/' . $controller . ".php";
    if (! file_exists($controllerFilePath)) {
        throw new Exception("controller file is not found");
    }
    require $controllerFilePath;
} catch (Exception $e) {
    echo $e->getMessage();
    exit;
}

echo $controller::$action($params);

.htaccess:

AddDefaultCharset UTF-8

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>

Hello controller

実行するHelloコントローラを用意します。

../app/controllers/Hello.php:

<?php

class Hello
{
    public static function say($params)
    {
        return "Hello " . $params['name'];
    }
}

OrePux Compiled Mux

Puxは、ルーティング情報をコンパイルすることでさらに高速化が可能です。

まず、ルーティング情報をroutes.phpに記載します。

routes.php:

<?php

require __DIR__ . '/../vendor/autoload.php';

$mux = new \Pux\Mux();
$mux->get('/hello/:name', ['hello','say']);
return $mux;

routes.phpをpuxコマンドでコンパイルします。

$ curl -O -k https://raw.github.com/c9s/Pux/master/pux
$ chmod +x pux
$ ./pux compile -o mux.php routes.php

そして、OrePuxのindex.phpをコンパイルした情報を使うように変更します。

index.php:

<?php

ini_set('display_errors', 1);
error_reporting(-1);

require __DIR__ . '/../vendor/autoload.php';

$mux = require 'mux.php';
$route = $mux->dispatch($_SERVER['PATH_INFO']);

$controller = ucfirst($route[2][0]);
$action     = $route[2][1];
$params     = $route[3]['vars'];

try {
    $controllerFilePath = __DIR__ . '/../app/controllers/' . $controller . ".php";
    if (! file_exists($controllerFilePath)) {
        throw new Exception("controller file is not found");
    }
    require $controllerFilePath;
} catch (Exception $e) {
    echo $e->getMessage();
    exit;
}

echo $controller::$action($params);

OrePux Compiled Mux w/o loading autoloader

OrePhalconとのパフォーマンスの差があまりに大きいので原因を探していましたが、結論として、ComposerのAutoloaderのロードに時間がかかるためでした。

PuxのPHP機能拡張を使い、コンパイル済みのルーティング情報を使う場合は、PHP機能拡張だけで処理が可能になるため、ComposerのAutoloaderをロードする必要はありません。

そこで、OrePux Compiled Muxから以下のrequireの行を削除します。

require __DIR__ . '/../vendor/autoload.php';

ちなみに、何故、Puxを使う場合、通常はAutoloaderが必要かというと、Pux\MuxはCで実装されていますが、Pux\PatternCompilerはそうでないので、正規表現でのルーティングを使う場合は、PHPで実装されたPux\PatternCompilerをロードする必要があるためです。ルーティング情報を事前にコンパイルすることで、Pux\PatternCompilerが実行時に不要になるということのようです。

ベンチマーク

ab -n 1000 http://localhost/OrePux/web/hello/xxxのようにベンチマークしました。

「w/o C extension」はPuxのPHP機能拡張なし、「w/z C extension」はPHP機能拡張あり、「w/o loading autoloader」はComposerのAutoloaderのロードなしです。

Framework Requests per second Relative
OrePux Basic w/o C extension 318.73 100%
OrePux Compiled Mux w/o C extension 325.00 102%
OrePux Basic w/z C extension 370.68 116%
OrePux Compiled Mux w/z C extension 452.63 140%
OrePux Compiled Mux w/z C extension w/o loading autoloader 787.17 247%
OrePhalcon 801.91 252%

わずかにOrePhalconの方が速いですが、大差はなくなりました。

PhalconとPHP機能拡張版のPuxのルーティング性能にそれほどの違いはないようです。

参考

Tags: pux, php, benchmark

さらにPuxのルーティング性能をベンチマークしてみた

(2014-01-15 13:30 追記) この記事のPuxのベンチマーク結果は、Cで書かれたPuxのPHP機能拡張が使われておらず、PHPで書かれたPuxの結果のようです。修正記事を後ほどアップします。

(2014-01-15 16:20 追記) PHP機能拡張が使われていないわけではないことがわかりました。後日、詳細な記事をアップします。

(2014-01-16 追記) 【完全版】Pux - A High Performance PHP Routerのルーティング性能をベンチマークしてみたをアップしました。

昨日のOrePhalconとOrePuxでPhalconとPuxのルーティングをベンチマークしてみたの続きです。

Puxについては、チューニングの余地がまだありました。

ベンチマーク環境

OS: Ubuntu 12.04
Apache: 2.4.7
PHP: 5.5.6 (32bit)
Phalcon: 1.2.5
Pux: 1.1.2 (C extension), GitHub master f42393f9d174872b48884363acb1344355aa43da (Tue Jan 14 02:22:26 2014 +0800)

OrePux Basic版

OrePhalconをPuxに移植したOrePuxです(昨日の記事と同じ)。

index.php:

<?php

ini_set('display_errors', 1);
error_reporting(-1);

require __DIR__ . '/../vendor/autoload.php';

$mux = new \Pux\Mux();
$mux->get('/hello/:name', ['hello','say']);
$route = $mux->dispatch($_SERVER['PATH_INFO']);

$controller = ucfirst($route[2][0]);
$action     = $route[2][1];
$params     = $route[3]['vars'];

try {
    $controllerFilePath = __DIR__ . '/../app/controllers/' . $controller . ".php";
    if (! file_exists($controllerFilePath)) {
        throw new Exception("controller file is not found");
    }
    require $controllerFilePath;
} catch (Exception $e) {
    echo $e->getMessage();
    exit;
}

echo $controller::$action($params);

.htaccess:

AddDefaultCharset UTF-8

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>

Hello controller

Helloコントローラを用意します(昨日の記事と同じ)。

../app/controllers/Hello.php:

<?php

class Hello
{
    public static function say($params)
    {
        return "Hello " . $params['name'];
    }
}

OrePux Compiled Mux版

Puxは、ルーティング情報をコンパイルすることでさらに高速化が可能です。

まず、ルーティング情報をroutes.phpに記載します。

routes.php:

<?php

require __DIR__ . '/../vendor/autoload.php';

$mux = new \Pux\Mux();
$mux->get('/hello/:name', ['hello','say']);
return $mux;

routes.phpをpuxコマンドでコンパイルします。

$ curl -O -k https://raw.github.com/c9s/Pux/master/pux
$ chmod +x pux
$ ./pux compile -o mux.php routes.php

そして、OrePuxのindex.phpをコンパイルした情報を使うように変更します。

index.php:

<?php

ini_set('display_errors', 1);
error_reporting(-1);

require __DIR__ . '/../vendor/autoload.php';

$mux = require 'mux.php';
$route = $mux->dispatch($_SERVER['PATH_INFO']);

$controller = ucfirst($route[2][0]);
$action     = $route[2][1];
$params     = $route[3]['vars'];

try {
    $controllerFilePath = __DIR__ . '/../app/controllers/' . $controller . ".php";
    if (! file_exists($controllerFilePath)) {
        throw new Exception("controller file is not found");
    }
    require $controllerFilePath;
} catch (Exception $e) {
    echo $e->getMessage();
    exit;
}

echo $controller::$action($params);

ベンチマーク

ab -n 1000 http://localhost/OrePux/web/hello/xxxのようにベンチマークしました。

Framework Requests per second Relative
OrePux Basic 389.64 100%
OrePux Compiled Mux 477.80 123%

Compiled Muxを使うことで、23%ほど高速化しました。

なお、$mux->get()より$mux->add()にした方が速いという情報ももらいましたが、手許の環境では差はわかりませんでした。

(2014-01-15 13:30 追記) この記事のPuxのベンチマーク結果は、Cで書かれたPuxのPHP機能拡張が使われておらず、PHPで書かれたPuxの結果のようです。修正記事を後ほどアップします。

(2014-01-15 16:20 追記) PHP機能拡張が使われていないわけではないことがわかりました。後日、詳細な記事をアップします。

(2014-01-16 追記) 【完全版】Pux - A High Performance PHP Routerのルーティング性能をベンチマークしてみたをアップしました。

参考

Tags: pux, php, benchmark

PHPUnit Selenium2で失敗時にスクリーンショットを取得する方法

PHPUnit_Extensions_Selenium2TestCaseで失敗時にスクリーンショットを取得には、onNotSuccessfulTest()メソッドをオーバーライドし、PHPUnit_Extensions_Selenium2TestCase_ScreenshotListenerを使用します。

class WebTestCase extends PHPUnit_Extensions_Selenium2TestCase
{
    public function onNotSuccessfulTest(Exception $e)
    {
        if ($e instanceof PHPUnit_Framework_AssertionFailedError) {
            // 失敗時にスクリーンショットを保存
            $listener = new PHPUnit_Extensions_Selenium2TestCase_ScreenshotListener(
                TESTPATH . 'failure-screenshot' // 保存先のフォルダ
            );
            $listener->addFailure($this, $e, null);
        }

        parent::onNotSuccessfulTest($e);
    }
…略…

また、テスト中に任意の時点でスクリーンショットを取得するには、currentScreenshot()メソッドを使います。

    public function testScreenshotsCanBeTakenAtAnyMoment()
    {
        $this->url('html/test_open.html');
        $screenshot = $this->currentScreenshot();
        …略…
    }

https://github.com/sebastianbergmann/phpunit-selenium/blob/master/Tests/Selenium2TestCaseTest.php#L739より。

画像が文字列として取得できますので、ファイルに書き出せばOKです。

関連

Tags: phpunit, selenium, php, testing