PHP8中的JIT是什么

发布时间:2020-06-28 16:22:35 作者:元一
来源:亿速云 阅读:265

PHP8中的JIT是什么?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

JIT 是一种编译器策略,它将代码表述为一种中间状态,在运行时将其转换为依赖于体系结构的机器码,并即时执行。在 PHP 中,这意味着 JIT 将为 Zend VM 生成的指令视为中间表述,并以依赖于体系结构的机器码执行,也就是说托管代码的不再是 Zend VM,而是更为底层的 CPU。

首先,我们来看一张图:

PHP8中的JIT是什么

右图有点错误就是,当JIT以后,下次请求的时候,会直接从JIT Buffer中读取执行,后续我把图改一下

左图是PHP8之前的Opcache流程示意图, 右图是PHP8中的Opcache示意图, 可以看出几个关键点:

事实上JIT共用了很多原来Opcache做优化的基础数据结构,比如data flow graph, call graph, SSA等,关于这部分,后续如果有时间,可以单独在写一个文章来介绍,今天就只是着重在使用层面。

下载安装好以后,除掉原有的opcache配置以外,对于JIT我们需要添加如下配置到php.ini:

opcache.jit=1205
opcache.jit_buffer_size=64M

opcache.jit这个配置看起来稍微有点复杂,我来解释下, 这个配置由4个独立的数字组成,从左到右分别是(请注意,这个是基于目前alpha1的版本设置,一些配置可能会随着后续版本做微调):

基于此,我们可以大概得到如下几个结论:

现在,我们来测试下启用和不启用JIT的时候,Zend/bench.php的差异,首先是不启用(php -d opcache.jit_buffer_size=0 Zend/bench.php):

simple             0.008
simplecall         0.004
simpleucall        0.004
simpleudcall       0.004
mandel             0.035
mandel2            0.055
ackermann(7)       0.020
ary(50000)         0.004
ary2(50000)        0.003
ary3(2000)         0.048
fibo(30)           0.084
hash2(50000)       0.013
hash3(500)         0.010
heapsort(20000)    0.027
matrix(20)         0.026
nestedloop(12)     0.023
sieve(30)          0.013
strcat(200000)     0.006
------------------------
Total              0.387

根据上面的介绍,我们选择opcache.jit=1205, 因为bench.php是脚本(php -d opcache.jit_buffer_size=64M -d opcache.jit=1205 Zend/bench.php):

simple             0.002
simplecall         0.001
simpleucall        0.001
simpleudcall       0.001
mandel             0.010
mandel2            0.011
ackermann(7)       0.010
ary(50000)         0.003
ary2(50000)        0.002
ary3(2000)         0.018
fibo(30)           0.031
hash2(50000)       0.011
hash3(500)         0.008
heapsort(20000)    0.014
matrix(20)         0.015
nestedloop(12)     0.011
sieve(30)          0.005
strcat(200000)     0.004
------------------------
Total              0.157

可见,对于Zend/bench.php, 相比不开启JIT,开启了以后,耗时降低将近60%,性能提升将近2倍

对于大家研究学习来说,可以通过opcache.jit_debug来观测JIT后生成的汇编结果,比如对于:

function simple() {
  $a = 0;
  for ($i = 0; $i < 1000000; $i++)
    $a++;
}

我们通过php -d opcache.jit=1205 -dopcache.jit_debug=0x01 可以看到:

JIT$simple: ; (/tmp/1.php)
	sub $0x10, %rsp
	xor %rdx, %rdx
	jmp .L2
.L1:
	add $0x1, %rdx
.L2:
	cmp $0x0, EG(vm_interrupt)
	jnz .L4
	cmp $0xf4240, %rdx
	jl .L1
	mov 0x10(%r14), %rcx
	test %rcx, %rcx
	jz .L3
	mov $0x1, 0x8(%rcx)
.L3:
	mov 0x30(%r14), %rax
	mov %rax, EG(current_execute_data)
	mov 0x28(%r14), %edi
	test $0x9e0000, %edi
	jnz JIT$$leave_function
	mov %r14, EG(vm_stack_top)
	mov 0x30(%r14), %r14
	cmp $0x0, EG(exception)
	mov (%r14), %r15
	jnz JIT$$leave_throw
	add $0x20, %r15
	add $0x10, %rsp
	jmp (%r15)
.L4:
	mov $0x45543818, %r15
	jmp JIT$$interrupt_handler

大家可以尝试阅读这段汇编,比如其中针对i的递增,可以看到优化力度很大,比如因为i是局部变量直接分配在寄存器中,i的范围推断不会大于10000,所以不需要判断是否整数溢出等等。

而如果我们采用opcache.jit=1201, 我们可以得到如下结果:

JIT$simple: ; (/tmp/1.php)
	sub $0x10, %rsp
	call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER
	add $0x40, %r15
	jmp .L2
.L1:
	call ZEND_PRE_INC_LONG_NO_OVERFLOW_SPEC_CV_RETVAL_UNUSED_HANDLER
	cmp $0x0, EG(exception)
	jnz JIT$$exception_handler
.L2:
	cmp $0x0, EG(vm_interrupt)
	jnz JIT$$interrupt_handler
	call ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER
	cmp $0x0, EG(exception)
	jnz JIT$$exception_handler
	cmp $0x452a0858, %r15d
	jnz .L1
	add $0x10, %rsp
	jmp ZEND_RETURN_SPEC_CONST_LABEL

这就只是简单的内敛部分opcode handler的调用了。

你也可以尝试各种opcache.jit的策略结合debug的配置,来观测结果的不同,你也可以尝试各种opcache.jit_debug的配置,比如0xff,将会有更多的辅助信息输出。

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

推荐阅读:
  1. opcode cache与JIT之间的区别是什么
  2. PHP8中新增的JIT编译器怎么用

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

jit php8 p8

上一篇:python中input输入数据类型错误怎么办

下一篇:nagios——check_memory.pl脚本测试

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》