PHP7中的标量和严格类型是性能增强功能吗?

igo*_*s07 34 php performance types strong-typing php-7

从PHP7开始,我们现在可以使用标量typehint并在每个文件的基础上请求严格类型.使用这些功能是否有任何性能优势?如果有,怎么样?

在互联网周围,我只发现了概念上的好处,例如:

  • 更精确的错误
  • 避免不必要的类型强制问题
  • 更多的语义代码,避免在使用其他代码时产生误解
  • 更好的IDE评估代码

Joe*_*ins 42

今天,在PHP7中使用标量和严格类型并不能提高性能.

PHP7没有JIT编译器.

如果在未来的某个时间PHP确实获得了JIT编译器,那么想象可以使用其他类型信息执行的优化并不是很困难.

当谈到没有JIT的优化时,标量类型只是部分有用.

我们来看下面的代码:

<?php
function (int $a, int $b) : int {
    return $a + $b;
}
?>
Run Code Online (Sandbox Code Playgroud)

这是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
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

如果不了解任何代码的作用,您会发现它相当复杂.

因此,目标将被省略ZEND_RECV完全和更换ZEND_ADDZEND_ADD_INT_INT它不需要进行任何检查(超出守卫)或分支,因为则params的类型是已知的.

为了省略那些,并且ZEND_ADD_INT_INT你需要能够在编译时可靠地推断出类型$a和类型$b.例如,编译时间推断有时很容易,$a并且$b是文字整数或常量.

从字面上昨天,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();
}
Run Code Online (Sandbox Code Playgroud)

同样,如果不了解其中的任何内容,您可以告诉它执行起来简单得多.

这些优化非常酷,但是,当PHP具有JIT时,最有效,最有趣的优化将会出现.

  • 确切地说,它甚至会使性能降低很小......可以忽略不计的数量;-)这样可以减少一些检查,但没有什么值得注意的. (4认同)
  • 这在技术上是正确的.但是,增强或降级不应成为您是否使用(或不使用)此类功能的决定性因素. (2认同)

Fed*_*kun 17

使用这些功能是否有任何性能优势?如果有,怎么样?

没有.

但这是更高效的操作码生成的第一步.根据RFC:Scalar Type Hints的 未来范围:

因为标量类型提示保证传递的参数在函数体中是某种类型(至少最初),所以可以在Zend Engine中使用它来进行优化.例如,如果一个函数接受两个float-hinted参数并对它们进行算术运算,则算术运算符不需要检查它们的操作数类型.

在以前的php版本中,无法知道哪种参数可以传递给函数,这使得JIT编译方法很难实现卓越的性能,就像facebook的HHVM那样.

@ircmaxell在他的博客中提到了使用本机编译将所有这些提升到一个新的水平的可能性,这将比JIT更好.

从性能的角度来看,类型标量提示为实现这些优化打开了大门.但并不能提高性能本身.