PHP8新特性介绍之JIT

PHP8 alpha1已经在昨天发布,相信关于JIT是大家最关心的,它到底怎么用,有什么要注意的,以及性能提升到底咋样?

首先,我们来看一张图:

image

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

  • Opcache会做opcode层面的优化,比如图中的俩条opcode合并为一条

  • PHP8的JIT目前是在Opcache之中提供的

  • JIT在Opcache优化之后的基础上,结合Runtime的信息再次优化,直接生成机器码

  • JIT不是原来Opcache优化的替代,是增强

  • 目前PHP8只支持x86架构的CPU

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

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

10年架构师领你架构-成长之路-(附面试题(含答案))

(腾讯T3-T4)打造互联网PHP架构师教程目录大全,只要你看完,薪资立马提升2倍(持续更新)

点击与我交流企鹅群.

<pre class="public-DraftStyleDefault-pre" data-offset-key="8k2f0-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="8pipe" data-offset-key="8k2f0-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

opcache.jit=1205
opcache.jit_buffer_size=64M

</pre>

</pre>

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

  • 是否在生成机器码点时候使用AVX指令, 需要CPU支持:
  1. 0: 不使用

  2. 1: 使用

  • 寄存器分配策略:
  1. 0: 不使用寄存器分配

  2. 1: 局部(block)域分配

  3. 2: 全局(function)域分配

  • JIT触发策略:
  1. 0: PHP脚本载入的时候就JIT

  2. 1: 当函数第一次被执行时JIT

  3. 2: 在一次运行后,JIT调用次数最多的百分之(opcache.prof_threshold * 100)的函数

  4. 3: 当函数/方法执行超过N(N和opcache.jit_hot_func相关)次以后JIT

  5. 4: 当函数方法的注释中含有@jit的时候对它进行JIT

  6. 5: 当一个Trace执行超过N次(和opcache.jit_hot_loop, jit_hot_return等有关)以后JIT

  • JIT优化策略,数值越大优化力度越大:
  1. 0: 不JIT

  2. 1: 做opline之间的跳转部分的JIT

  3. 2: 内敛opcode handler调用

  4. 3: 基于类型推断做函数级别的JIT

  5. 4: 基于类型推断,过程调用图做函数级别JIT

  6. 5: 基于类型推断,过程调用图做脚本级别的JIT

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

  • 尽量使用12x5型的配置,此时应该是效果最优的

  • 对于x, 如果是脚本级别的,推荐使用0, 如果是Web服务型的,可以根据测试结果选择3或5

  • @jit的形式,在有了attributes以后,可能变为<<jit>>

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

<pre class="public-DraftStyleDefault-pre" data-offset-key="7nuhj-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="8pipe" data-offset-key="7nuhj-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

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
hash1(50000) 0.013
hash2(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

</pre>

</pre>

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

<pre class="public-DraftStyleDefault-pre" data-offset-key="6m541-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="8pipe" data-offset-key="6m541-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

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
hash1(50000) 0.011
hash2(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

</pre>

</pre>

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

点击与我交流企鹅群.

感谢大家一直来支持,这是我准备的1000粉丝福利

【1000粉丝福利】10年架构师分享PHP进阶架构资料,助力大家都能30K

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

<pre class="public-DraftStyleDefault-pre" data-offset-key="ai1ka-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="8pipe" data-offset-key="ai1ka-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

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

</pre>

</pre>

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

<pre class="public-DraftStyleDefault-pre" data-offset-key="87v3v-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="8pipe" data-offset-key="87v3v-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

JITsimple: ; (/tmp/1.php) sub0x10, %rsp
xor %rdx, %rdx
jmp .L2
.L1:
add 0x1, %rdx .L2: cmp0x0, EG(vm_interrupt)
jnz .L4
cmp 0xf4240, %rdx jl .L1 mov 0x10(%r14), %rcx test %rcx, %rcx jz .L3 mov0x1, 0x8(%rcx)
.L3:
mov 0x30(%r14), %rax
mov %rax, EG(current_execute_data)
mov 0x28(%r14), %edi
test 0x9e0000, %edi jnz JITleave_function
mov %r14, EG(vm_stack_top)
mov 0x30(%r14), %r14
cmp 0x0, EG(exception) mov (%r14), %r15 jnz JITleave_throw
add 0x20, %r15 add0x10, %rsp
jmp (%r15)
.L4:
mov 0x45543818, %r15 jmp JITinterrupt_handler

</pre>

</pre>

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

而如果我们采用opcache.jit=1005, 如前面的介绍,也就是不使用寄存器分配,可以得到如下结果:

<pre class="public-DraftStyleDefault-pre" data-offset-key="4l7l1-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="8pipe" data-offset-key="4l7l1-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

JITsimple: ; (/tmp/1.php) sub0x10, %rsp
mov 0x0, 0x50(%r14) mov0x4, 0x58(%r14)
jmp .L2
.L1:
add 0x1, 0x50(%r14) .L2: cmp0x0, EG(vm_interrupt)
jnz .L4
cmp 0xf4240, 0x50(%r14) jl .L1 mov 0x10(%r14), %rcx test %rcx, %rcx jz .L3 mov0x1, 0x8(%rcx)
.L3:
mov 0x30(%r14), %rax
mov %rax, EG(current_execute_data)
mov 0x28(%r14), %edi
test 0x9e0000, %edi jnz JITleave_function
mov %r14, EG(vm_stack_top)
mov 0x30(%r14), %r14
cmp 0x0, EG(exception) mov (%r14), %r15 jnz JITleave_throw
add 0x20, %r15 add0x10, %rsp
jmp (%r15)
.L4:
mov 0x44cdb818, %r15 jmp JITinterrupt_handler

</pre>

</pre>

可以看到针对i的部分,现在是在内存操作,并没有使用寄存器。

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

<pre class="public-DraftStyleDefault-pre" data-offset-key="8f93q-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="8pipe" data-offset-key="8f93q-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

JITsimple: ; (/tmp/1.php) sub0x10, %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 cmp0x0, EG(exception)
jnz JITexception_handler .L2: cmp0x0, EG(vm_interrupt)
jnz JITinterrupt_handler call ZEND_IS_SMALLER_LONG_SPEC_TMPVARCV_CONST_JMPNZ_HANDLER cmp0x0, EG(exception)
jnz JITexception_handler cmp0x452a0858, %r15d
jnz .L1
add $0x10, %rsp
jmp ZEND_RETURN_SPEC_CONST_LABEL

</pre>

</pre>

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

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

好了,JIT的使用就简单介绍到这里,关于JIT本身的实现等细节,以后有时间,我再来写吧。

大家现在就可以去php.net下载PHP8来测试了 :)

大厂2000道面试题(含答案)

PHP面试题汇总,看完这些面试题助力你面试成功,工资必有20-25K

点击与我交流企鹅群.

喜欢我的文章就关注我吧,持续更新中.....

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以点击进入暗号:知乎

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345