PHP7中的标量和严格类型是否具有性能增强功能? [英] Are scalar and strict types in PHP7 a performance enhancing feature?

查看:164
本文介绍了PHP7中的标量和严格类型是否具有性能增强功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

自PHP7以来,我们现在可以使用标量类型提示并按文件要求严格类型.使用这些功能有什么性能上的好处?如果是,怎么办?

Since PHP7 we can now use scalar typehint and ask for strict types on a per-file basis. Are there any performance benefits from using these features? If yes, how?

在互连网上,我只发现了一些概念上的好处,例如:

Around the interwebs I've only found conceptual benefits, such as:

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

推荐答案

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

Today, the use of scalar and strict types in PHP7 does not enhance performance.

PHP7没有JIT编译器.

PHP7 does not have a JIT compiler.

如果将来某个时候PHP确实获得了JIT编译器,那么很难想象可以使用附加类型信息执行的优化.

If at some time in the future PHP does get a JIT compiler, it is not too difficult to imagine optimizations that could be performed with the additional type information.

在没有JIT的情况下进行优化时,标量类型只是部分有用.

When it comes to optimizations without a JIT, scalar types are only partly helpful.

让我们采用以下代码:

<?php
function (int $a, int $b) : int {
    return $a + $b;
}
?>

这是Zend为此生成的代码:

This is the code generated by Zend for that:

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();
}

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

Without understanding what any of that code does, you can see that it's rather complex.

因此,目标将完全省略ZEND_RECV,而将ZEND_ADD替换为ZEND_ADD_INT_INT,因为参数类型是已知的,因此无需执行任何检查(除了保护)或分支.

So the target would be omitting ZEND_RECV completely, and replacing ZEND_ADD with ZEND_ADD_INT_INT which doesn't need to perform any checking (beyond guarding) or branching, because the types of params are known.

为了省略这些内容并使用ZEND_ADD_INT_INT,您需要能够在编译时可靠地推断$a$b的类型.编译时间推断有时很容易,例如$a$b是文字整数或常量.

In order to omit those, and have a ZEND_ADD_INT_INT you need to be able to reliably infer the types of $a and $b at compile time. Compile time inference is sometimes easy, for example, $a and $b are literal integers, or constants.

从字面上昨天,PHP 7.1的确非常相似:现在有一些针对特定类型的处理程序,频率操作码,如ZEND_ADD. Opcache能够推断某些变量的类型,甚至在某些情况下甚至能够推断数组中变量的类型,并更改生成的操作码以使用普通的ZEND_ADD来使用特定于类型的处理程序:

Literally yesterday, PHP 7.1 got something really similar: There are now type specific handlers for some high frequency opcodes like ZEND_ADD. Opcache is able to infer the type of some variables, it's even able to infer the types of variables within an array in some cases and change opcodes generated to use the normal ZEND_ADD, to use a type specific handler:

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();
}

同样,在不了解其作用的情况下,您可以说这很容易执行.

Again, without understanding what any of that does, you can tell that this is much simpler to execute.

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

These optimizations are very cool, however, the most effective, and most interesting optimizations will come when PHP has a JIT.

这篇关于PHP7中的标量和严格类型是否具有性能增强功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆