PHP

PHP7源码系列-Zend虚拟机

2019年4月11日
image.png

虚拟机

编译

image.png

词法分析,语法分析

re2c,bison
token_get_all

AST

PHP-Parser

Opcode

struct _zend_op {
  // 对应的处理函数
    const void *handler;
  // 操作数1
    znode_op op1;
  // 操作数2
    znode_op op2;
  // 返回结果
    znode_op result;
    uint32_t extended_value;
    uint32_t lineno;
  // opcode指令
    zend_uchar opcode;
    zend_uchar op1_type;
    zend_uchar op2_type;
    zend_uchar result_type;
};
typedef union _znode_op {
    uint32_t      constant;
    uint32_t      var;
    uint32_t      num;
    uint32_t      opline_num; /*  Needs to be signed */
#if ZEND_USE_ABS_JMP_ADDR
    zend_op       *jmp_addr;
#else
    uint32_t      jmp_offset;
#endif
#if ZEND_USE_ABS_CONST_ADDR
    zval          *zv;
#endif
} znode_op;
struct _zend_op_array {
    /* Common elements */
    zend_uchar type;
    zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */
    uint32_t fn_flags;
    zend_string *function_name;
    zend_class_entry *scope;
    zend_function *prototype;
    uint32_t num_args;
    uint32_t required_num_args;
    zend_arg_info *arg_info;
    /* END of common elements */

    uint32_t *refcount;

    uint32_t this_var;

    uint32_t last;
  // opcode
    zend_op *opcodes;

    int last_var;
    uint32_t T;
    zend_string **vars;

    int last_brk_cont;
    int last_try_catch;
    zend_brk_cont_element *brk_cont_array;
    zend_try_catch_element *try_catch_array;

    /* static variables support */
    HashTable *static_variables;

    zend_string *filename;
    uint32_t line_start;
    uint32_t line_end;
    zend_string *doc_comment;
    uint32_t early_binding; /* the linked list of delayed declarations */

    int last_literal;
    zval *literals;

    int  cache_size;
    void **run_time_cache;

    void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};

执行

struct _zend_execute_data {
  // 当前opcode
    const zend_op       *opline;           /* executed opline                */
    // 当前函数执行栈
    zend_execute_data   *call;             /* current call                   */
    // 返回值
    zval                *return_value;
  // 执行的函数
    zend_function       *func;             /* executed funcrion              */
  // 
    zval                 This;             /* this + call_info + num_args    */
    zend_class_entry    *called_scope;
  // 调用当前函数的上一个zend_execute_data
    zend_execute_data   *prev_execute_data;
  // 符号表
    zend_array          *symbol_table;
#if ZEND_EX_USE_RUN_TIME_CACHE
    void               **run_time_cache;   /* cache op_array->run_time_cache */
#endif
#if ZEND_EX_USE_LITERALS
  // 
    zval                *literals;         /* cache op_array->literals       */
#endif
};
ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value)
{
    zend_execute_data *execute_data;

    if (EG(exception) != NULL) {
        return;
    }

    execute_data = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_CODE,
        (zend_function*)op_array, 0, zend_get_called_scope(EG(current_execute_data)), zend_get_this_object(EG(current_execute_data)));
    if (EG(current_execute_data)) {
        execute_data->symbol_table = zend_rebuild_symbol_table();
    } else {
        execute_data->symbol_table = &EG(symbol_table);
    }
    EX(prev_execute_data) = EG(current_execute_data);
    i_init_execute_data(execute_data, op_array, return_value);
    zend_execute_ex(execute_data);
    zend_vm_stack_free_call_frame(execute_data);
}
ZEND_API void execute_ex(zend_execute_data *ex)
{
    DCL_OPLINE

#ifdef ZEND_VM_IP_GLOBAL_REG
    const zend_op *orig_opline = opline;
#endif
#ifdef ZEND_VM_FP_GLOBAL_REG
    zend_execute_data *orig_execute_data = execute_data;
    execute_data = ex;
#else
    zend_execute_data *execute_data = ex;
#endif


    LOAD_OPLINE();

    while (1) {
#if !defined(ZEND_VM_FP_GLOBAL_REG) || !defined(ZEND_VM_IP_GLOBAL_REG)
        int ret;
#endif
#if defined(ZEND_VM_FP_GLOBAL_REG) && defined(ZEND_VM_IP_GLOBAL_REG)
        ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
        if (UNEXPECTED(!OPLINE)) {
#else
        if (UNEXPECTED((ret = ((opcode_handler_t)OPLINE->handler)(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)) != 0)) {
#endif
#ifdef ZEND_VM_FP_GLOBAL_REG
            execute_data = orig_execute_data;
# ifdef ZEND_VM_IP_GLOBAL_REG
            opline = orig_opline;
# endif
            return;
#else
            if (EXPECTED(ret > 0)) {
                execute_data = EG(current_execute_data);
            } else {
# ifdef ZEND_VM_IP_GLOBAL_REG
                opline = orig_opline;
# endif
                return;
            }
#endif
        }

    }
    zend_error_noreturn(E_CORE_ERROR, "Arrived at end of main loop which shouldn't happen");
}
image.png

OPcache

安装

只能作为共享库
php5.5.0+ 
–enable-opcache
php5.2, 5.3 and 5.4
单独编译安装
https://pecl.php.net/package/ZendOpcache

zend_extension=/full/path/to/opcache.so on non-Windows platforms, and zend_extension=C:\path\to\php_opcache.dll on Windows.

参数配置

JIT

Just-In-Time 即时编译,狭义指某段代码即将第一次被执行时进行编译,而后则不用编译直接执行,它为动态编译的一种特例。
php代码  =>  字节码 => 机器码
php代码 => 机器码

https://wiki.php.net/rfc/jithttps://www.v2ex.com/t/532386?r=easychen

// 在php8中加入
We propose to include JIT in PHP 8 and provide additional efforts to increase its performance and usability.
// 考虑在php7.4中做为实验性质的功能加入,默认关闭
In addition, we propose to consider including JIT in PHP 7.4 as an experimental feature (disabled by default).
// 作为OPcache的一部分来实现
JIT would be implemented as a part of OPcache.

引用v2ex上@jfcherng

JIT 對 web 領域的提升非常有限(絕大部分的 web 應用都不是“計算繁重”的類型,見原文的 wordpress benchmark 提升 10%不到)。

但 JIT 開啟了將 PHP 用在其他領域的可能性。Reddit 上也有人看了這個 RFC 之後,分享用 PHP 寫日常腳本(類似 shell script, python )的心得,不過 PHP 不是系統默認安裝的稍嫌麻煩。

发表评论

电子邮件地址不会被公开。 必填项已用*标注