轉載自鳥哥博客 , 原文地址: https://www.laruence.com/2020/06/27/5963.htmlphp
PHP8 alpha1已經在昨天發佈,相信關於JIT是你們最關心的,它到底怎麼用,有什麼要注意的,以及性能提高到底咋樣?html
首先,咱們來看一張圖:數據結構
左圖是PHP8以前的Opcache流程示意圖(zend引擎每次都先解釋後執行), 右圖是PHP8中的Opcache示意圖(zend引擎直接執行機器碼), 能夠看出幾個關鍵點:架構
事實上JIT共用了不少原來Opcache作優化的基礎數據結構,好比data flow graph, call graph, SSA等,關於這部分,後續若是有時間,能夠單獨在寫一個文章來介紹,今天就只是着重在使用層面。函數
下載安裝好之後,除掉原有的opcache配置之外,對於JIT咱們須要添加以下配置到php.ini:oop
opcache.jit=1205 opcache.jit_buffer_size=64M
opcache.jit這個配置看起來稍微有點複雜,我來解釋下, 這個配置由4個獨立的數字組成,從左到右分別是(請注意,這個是基於目前alpha1的版本設置,一些配置可能會隨着後續版本作微調):性能
0: 不使用 1: 使用
第二個數字寄存器分配策略:學習
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
基於此,咱們能夠大概獲得以下幾個結論:
如今,咱們來測試下啓用和不啓用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
你們能夠嘗試閱讀這段彙編,好比其中針對i的遞增,能夠看到優化力度很大,好比由於i是局部變量直接分配在寄存器中,i的範圍推斷不會大於1000000,因此不須要判斷是否整數溢出等等。
而若是咱們採用opcache.jit=1005, 如前面的介紹,也就是不使用寄存器分配,能夠獲得以下結果:
JIT$simple: ; (/tmp/1.php) sub $0x10, %rsp mov $0x0, 0x50(%r14) mov $0x4, 0x58(%r14) jmp .L2 .L1: add $0x1, 0x50(%r14) .L2: cmp $0x0, EG(vm_interrupt) jnz .L4 cmp $0xf4240, 0x50(%r14) 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 $0x44cdb818, %r15 jmp JIT$$interrupt_handler
能夠看到針對i的部分,如今是在內存操做,並無使用寄存器。
再若是咱們採用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,將會有更多的輔助信息輸出。