web-dev-qa-db-ja.com

PHP7のスカラー型とストリクト型はパフォーマンスを向上させる機能ですか?

PHP7以降、次のことが可能になりました スカラーtypehintを使用し、ファイルごとに厳密な型を要求する 。これらの機能を使用することによるパフォーマンス上の利点はありますか?はいの場合、どのように?

インターウェブの周りで、私は次のような概念的な利点のみを見つけました。

  • より正確なエラー
  • 不要な型強制の問題を回避する
  • よりセマンティックなコードで、他人のコードを使用する際の誤解を回避
  • より良いIDEコードの評価
35
igorsantos07

現在、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_ADDZEND_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がある場合です。

42
Joe Watkins

これらの機能を使用することによるパフォーマンス上の利点はありますか?はいの場合、どのように?

まだまだ

しかし、これはより効率的なオペコード生成のための最初のステップです。 RFCによると:スカラー型ヒントの将来のスコープ

スカラー型ヒントは、渡された引数が関数本体内で特定の型であることを保証するため(少なくとも最初は)、これはZend Engineで最適化に使用できます。たとえば、関数が2つの浮動小数点の引数を取り、それらを使って算術を行う場合、算術演算子がそのオペランドの型をチェックする必要はありません。

以前のバージョンのphpでは、どのような種類のパラメーターを関数に渡すことができるかを知る方法がなかったため、Facebookの [〜#〜] hhvm [〜 #〜] します。

彼の blog の@ircmaxellは、ネイティブコンパイルでこれをすべて次のレベルに引き上げる可能性について言及しています。これはJITよりも優れています。

パフォーマンスの観点から見ると、タイプスカラーヒントは、これらの最適化を実装するための扉を開きます。ただし、それ自体のパフォーマンスは向上しません。

17
Federkun