PHP7以降、次のことが可能になりました スカラーtypehintを使用し、ファイルごとに厳密な型を要求する 。これらの機能を使用することによるパフォーマンス上の利点はありますか?はいの場合、どのように?
インターウェブの周りで、私は次のような概念的な利点のみを見つけました。
現在、PHP7でスカラー型とストリクト型を使用しても、パフォーマンスは向上しません。
PHP7にはJITコンパイラがありません。
将来のある時点でPHPがJITコンパイラを取得する場合、追加の型情報を使用して実行できる最適化を想像することはそれほど難しくありません。
JITを使用しない最適化に関しては、スカラー型は部分的にしか役に立ちません。
次のコードを見てみましょう:
<?php
function (int $a, int $b) : int {
return $a + $b;
}
?>
これは、そのためにZendによって生成されたコードです。
function name: {closure}
L2-4 {closure}() /usr/src/scalar.php - 0x7fd6b30ef100 + 7 ops
L2 #0 RECV 1 $a
L2 #1 RECV 2 $b
L3 #2 ADD $a $b ~0
L3 #3 VERIFY_RETURN_TYPE ~0
L3 #4 RETURN ~0
L4 #5 VERIFY_RETURN_TYPE
L4 #6 RETURN null
ZEND_RECV
は、受信したパラメータの型検証と強制を実行するオペコードです。次のオペコードは ZEND_ADD
です。
ZEND_VM_HANDLER(1, ZEND_ADD, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
{
USE_OPLINE
zend_free_op free_op1, free_op2;
zval *op1, *op2, *result;
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) {
if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) {
result = EX_VAR(opline->result.var);
fast_long_add_function(result, op1, op2);
ZEND_VM_NEXT_OPCODE();
} else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) {
result = EX_VAR(opline->result.var);
ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2));
ZEND_VM_NEXT_OPCODE();
}
} else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) {
if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) {
result = EX_VAR(opline->result.var);
ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
ZEND_VM_NEXT_OPCODE();
} else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) {
result = EX_VAR(opline->result.var);
ZVAL_DOUBLE(result, Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2)));
ZEND_VM_NEXT_OPCODE();
}
}
SAVE_OPLINE();
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) {
op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R);
}
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) {
op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R);
}
add_function(EX_VAR(opline->result.var), op1, op2);
FREE_OP1();
FREE_OP2();
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
そのコードの機能を理解しないと、コードがかなり複雑であることがわかります。
したがって、ターゲットはZEND_RECV
を完全に省略し、ZEND_ADD
をZEND_ADD_INT_INT
で置き換えます。これは、パラメーターのタイプがわかっているため、(ガードを超えて)チェックや分岐を実行する必要はありません。
それらを省略し、ZEND_ADD_INT_INT
を使用するには、コンパイル時に$a
と$b
のタイプを確実に推測できる必要があります。たとえば、$a
と$b
はリテラル整数または定数です。
文字通り yesterday 、PHP 7.1は本当に似たものを得ました:ZEND_ADD
のようないくつかの高頻度オペコード用のタイプ固有のハンドラーがあります。Opcacheは、一部の変数のタイプ。場合によっては、配列内の変数のタイプを推測し、生成されたオペコードを変更して、通常のZEND_ADD
を使用し、タイプ固有のハンドラーを使用することもできます。
ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_ADD_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
{
USE_OPLINE
zval *op1, *op2, *result;
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
result = EX_VAR(opline->result.var);
ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2));
ZEND_VM_NEXT_OPCODE();
}
繰り返しになりますが、それが何をするのか理解していなくても、これはmuchの方が実行が簡単であることがわかります。
これらの最適化は非常に優れていますが、最も効果的で興味深いのは、PHPにJITがある場合です。
これらの機能を使用することによるパフォーマンス上の利点はありますか?はいの場合、どのように?
まだまだ。
しかし、これはより効率的なオペコード生成のための最初のステップです。 RFCによると:スカラー型ヒントの将来のスコープ :
スカラー型ヒントは、渡された引数が関数本体内で特定の型であることを保証するため(少なくとも最初は)、これはZend Engineで最適化に使用できます。たとえば、関数が2つの浮動小数点の引数を取り、それらを使って算術を行う場合、算術演算子がそのオペランドの型をチェックする必要はありません。
以前のバージョンのphpでは、どのような種類のパラメーターを関数に渡すことができるかを知る方法がなかったため、Facebookの [〜#〜] hhvm [〜 #〜] します。
彼の blog の@ircmaxellは、ネイティブコンパイルでこれをすべて次のレベルに引き上げる可能性について言及しています。これはJITよりも優れています。
パフォーマンスの観点から見ると、タイプスカラーヒントは、これらの最適化を実装するための扉を開きます。ただし、それ自体のパフォーマンスは向上しません。