1. 程式人生 > 程式設計 >PHP8新特性之JIT案例講解

PHP8新特性之JIT案例講解

8 alpha1已經在昨天釋出,相信關於JIT是大家最關心的,它到底怎麼用,有什麼要注意的,以及效能提升到底咋樣?

首先,我們來看一張圖:

deabbe4c2476e16f4314c7d54b552f48.png

左圖是 PHP 8之前的Opcache流程示意圖, 右圖是 PHP 8中的Opcache示意圖, 可以看出幾個關鍵點:

PHP8的JIT是在Opcache之中提供的

目前PHP8只支援x86架構的CPU

JIT是在原來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的版本設定,一些配http://www.cppcns.com置可能會隨著後續版本做微調 ):

是否在生成機器碼點時候使用AVX指令,需要CPU支援: 0: 不使用

1: 使用

暫存器分配http://www.cppcns.com策略: 0: 不使用暫存器分配

1: 區域性(block)域分配

2: 全域性(function)域分配

JIT觸發策略: 0: PHP載入的時候就JIT

1: 當函式第一次被執行時JIT

2: 在一次執行後,JIT呼叫次數最多的百分之(opcache.prof_threshold * 100)的函式

3: 當函式/方法執行超過N(N和opcache.jit_hot_func相關)次以後JIT

4: 當函式方法的註釋中含有@jit的時候對它進行JIT

5: 當一個Trace執行超過N次(和opcache.jit_hot_loop,jit_hot_return等有關)以後JIT

JIT優化策略,數值越大優化力度越大: 0: 不JIT

1: 做opline之間的跳轉部分的JIT

2: 內斂opcode handler呼叫

3: 基於型別推斷做函式級別的JIT

4: 基於型別推斷,過程呼叫圖做函式級別JIT

5: 基於型別推斷,過程呼叫圖做指令碼級別的JIT

基於此,我們可以大概得到如下幾個結論:

儘量使用12x5型的配置,此時應該是效果最優的

對於x, 如果是指令碼級別的,推薦使用0, 如果是Web服務型的,可以根據測試結果選擇3或5

@jit的形式,在有了attributes以後,可能變為<>

現在,我們來測試下啟用和不啟用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

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

根據上面的介紹,我們選擇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

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

可見, 對於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

而如果我們採用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_LABELhttp://www.cppcns.com

你也可以嘗試各種debug的配置,比如opcache.jit_debug=0xff,將會有更多的資訊輸出。

好了,JIT的使用就簡單介紹到這裡,關於JIT本身的實現等細節,以後有時間,我再來寫吧。

大家現在就可以去php.net下載PHP8來測試了 :)

thanks

到此這篇關於PHP8新特性之JIT案例講解的文章就介紹到這了,更多相關PHP8新特性之JIT內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!