前のトピック次のトピックこのページ |
独自のオプティマイザ¶Zephirでは、ほとんどの一般的な関数は内部オプティマイザを使います。‘optimizer’ は関数呼び出しのインタセプタのように動作します。‘optimizer’ はPHPユーザランド内の関数の呼び出しを、より速くオーバーヘッドが低くパフォーマンスが改善された直接のC呼び出しに置き換えます。 ‘optimizers’ ディレクトリ内のクラスを生成しなければならないオプティマイザを生成するために、以下の慣習が使われるべきです:
これは ‘optimizer’ の基本的な構造です:
namespace Zephir\Optimizers\FunctionCall;
use Zephir\Call;
use Zephir\CompilerException;
use Zephir\CompilationContext;
use Zephir\Optimizers\OptimizerAbstract;
class CalculatePiOptimizer extends OptimizerAbstract
{
public function optimize(array $expression, Call $call, CompilationContext $context)
{
//...
}
}
オプティマイザの実装は生成したいコードの種類に強く依存します。例では、この関数への呼び出しをc関数の呼び出しと置き換えるつもりです。Zephirでは、この関数の呼び出しに使われるコードは以下の通りです: let pi = calculate_pi(1000);
つまり、オプティマイザは単に一つのパラメータを期待し、後の問題を避けるためにそれを検証する必要があります:
public function optimize(array $expression, Call $call, CompilationContext $context)
{
if (!isset($expression['parameters'])) {
throw new CompilerException("'calculate_pi' requires one parameter", $expression);
}
if (count($expression['parameters']) > 1) {
throw new CompilerException("'calculate_pi' requires one parameter", $expression);
}
//...
}
単純に呼び出され何も値を返さない関数があります。関数は計算されたPIの値を返します。つまり、この計算された値を受け取るために使われる変数の型がOKかを気にする必要があります:
public function optimize(array $expression, Call $call, CompilationContext $context)
{
if (!isset($expression['parameters'])) {
throw new CompilerException("'calculate_pi' requires one parameter", $expression);
}
if (count($expression['parameters']) > 1) {
throw new CompilerException("'calculate_pi' requires one parameter", $expression);
}
/**
* 期待するシンボルが返されるように処理する
*/
$call->processExpectedReturn($context);
$symbolVariable = $call->getSymbolVariable();
if (!$symbolVariable->isDouble()) {
throw new CompilerException("Calculated PI values only can be stored in double variables", $expression);
}
//...
}
コンパイラ例外が投げられない場合、返された値が変数型 ‘double’に格納されるかどうかをチェックします。 次にしなければならないことは、関数に渡されるパラメータの処理です:
$resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression);
Zephirでの優れた方法として、パラメータを変更しない関数を作成することが重要です。渡されたパラメータを変更する場合、Zephirは渡された定数のためのメモリを割り当てる必要があり、getReadOnlyResolvedParamsの代わりにgetResolvedParams を使う必要があるでしょう。 これらのメソッドによって返されるコードは有効なCコードで、c関数呼び出しを生成するためにコード出力の中で使うことができます:
//Cコードの生成
return new CompiledExpression('double', 'calculate_pi( ' . $resolvedParams[0] . ')', $expression);
全てのオプティマイザは CompiledExpression インスタンスを返す必要があります。これはコンパイラにコードによって返される型とそれに関係するCコードを伝えます。 完全なオプティマィザのコードは以下になります:
namespace Zephir\Optimizers\FunctionCall;
use Zephir\Call;
use Zephir\CompilerException;
use Zephir\CompilationContext;
use Zephir\CompiledExpression;
use Zephir\Optimizers\OptimizerAbstract;
class CalculatePiOptimizer extends OptimizerAbstract
{
public function optimize(array $expression, Call $call, CompilationContext $context)
{
if (!isset($expression['parameters'])) {
throw new CompilerException("'calculate_pi' requires one parameter", $expression);
}
if (count($expression['parameters']) > 1) {
throw new CompilerException("'calculate_pi' requires one parameter", $expression);
}
/**
* 期待するシンボルが返されるように処理する
*/
$call->processExpectedReturn($context);
$symbolVariable = $call->getSymbolVariable();
if (!$symbolVariable->isDouble()) {
throw new CompilerException("Calculated PI values only can be stored in double variables", $expression);
}
$resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression);
return new CompiledExpression('double', 'my_calculate_pi(' . $resolvedParams[0] . ')', $expression);
}
}
関数 “my_calculate_pi” を実装するコードはCで書かれ、拡張と一緒にコンパイルされる必要があります。 このコードは適切だと思われる ext/ ディレクトリ内に配置されなければならず、これらのファイルがZephirによって生成されるファイルと衝突しないことだけをチェックします。 このファイルは Zend Engine ヘッダと関数の C 実装を含む必要があります: #ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ext.h"
double my_calculate_pi(zval *accuracy) {
return 0.0;
}
このファイルはconfig.json ファイルの特別なセクションに追加されなければなりません: "extra-sources": [
"utils/pi.c"
]
最後に、optimizer-dirs 設定オプションを使ってZephirがオプティマイザを見つけることができる場所を指定する必要があるでしょう。 この例の完全なソースコードをここ <https://github.com/phalcon/zephir-samples/tree/master/ext-optimizers>でチェックしてください。 |