返回上一级

PHP8 JIT 配置说明

php

注意点:

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

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

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

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

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

JIT共用了很多原来Opcache做优化的基础数据结构,比如data flow graph, call graph, SSA.

配置php.ini

参考文档:

https://www.php.net/manual/zh/opcache.configuration.php

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

1opcache.jit=1205
2opcache.jit_buffer_size=128M

opcache.jit这个配置看起来稍微有点复杂,我来解释下, 这个配置由4个独立的数字组成,从左到右分别是:

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

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

    • 0: 不使用寄存器分配 1: 局部(block)域分配 2: 全局(function)域分配
  3. 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

  4. JIT优化策略,数值越大优化力度越大:

    • 0: 不JIT

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

    • 2: 内敛opcode handler调用

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

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

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

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

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

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

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

现在,我们来测试下启用和不启用JIT的时候,Zend/bench.php的差异。

Zend/bench.php:https://github.com/php/php-src/blob/master/Zend/bench.php

  1<?php
  2if (function_exists("date_default_timezone_set")) {
  3    date_default_timezone_set("UTC");
  4}
  5
  6function simple() {
  7  $a = 0;
  8  for ($i = 0; $i < 1000000; $i++)
  9    $a++;
 10
 11  $thisisanotherlongname = 0;
 12  for ($thisisalongname = 0; $thisisalongname < 1000000; $thisisalongname++)
 13    $thisisanotherlongname++;
 14}
 15
 16/****/
 17
 18function simplecall() {
 19  for ($i = 0; $i < 1000000; $i++)
 20    strlen("hallo");
 21}
 22
 23/****/
 24
 25function hallo($a) {
 26}
 27
 28function simpleucall() {
 29  for ($i = 0; $i < 1000000; $i++)
 30    hallo("hallo");
 31}
 32
 33/****/
 34
 35function simpleudcall() {
 36  for ($i = 0; $i < 1000000; $i++)
 37    hallo2("hallo");
 38}
 39
 40function hallo2($a) {
 41}
 42
 43/****/
 44
 45function mandel() {
 46  $w1=50;
 47  $h1=150;
 48  $recen=-.45;
 49  $imcen=0.0;
 50  $r=0.7;
 51  $s=0;  $rec=0;  $imc=0;  $re=0;  $im=0;  $re2=0;  $im2=0;
 52  $x=0;  $y=0;  $w2=0;  $h2=0;  $color=0;
 53  $s=2*$r/$w1;
 54  $w2=40;
 55  $h2=12;
 56  for ($y=0 ; $y<=$w1; $y=$y+1) {
 57    $imc=$s*($y-$h2)+$imcen;
 58    for ($x=0 ; $x<=$h1; $x=$x+1) {
 59      $rec=$s*($x-$w2)+$recen;
 60      $re=$rec;
 61      $im=$imc;
 62      $color=1000;
 63      $re2=$re*$re;
 64      $im2=$im*$im;
 65      while( ((($re2+$im2)<1000000) && $color>0)) {
 66        $im=$re*$im*2+$imc;
 67        $re=$re2-$im2+$rec;
 68        $re2=$re*$re;
 69        $im2=$im*$im;
 70        $color=$color-1;
 71      }
 72      if ( $color==0 ) {
 73        print "_";
 74      } else {
 75        print "#";
 76      }
 77    }
 78    print "<br>";
 79    flush();
 80  }
 81}
 82
 83/****/
 84
 85function mandel2() {
 86  $b = " .:,;!/>)|&IH%*#";
 87  //float r, i, z, Z, t, c, C;
 88  for ($y=30; printf("\n"), $C = $y*0.1 - 1.5, $y--;){
 89    for ($x=0; $c = $x*0.04 - 2, $z=0, $Z=0, $x++ < 75;){
 90      for ($r=$c, $i=$C, $k=0; $t = $z*$z - $Z*$Z + $r, $Z = 2*$z*$Z + $i, $z=$t, $k<5000; $k++)
 91        if ($z*$z + $Z*$Z > 500000) break;
 92      echo $b[$k%16];
 93    }
 94  }
 95}
 96
 97/****/
 98
 99function Ack($m, $n){
100  if($m == 0) return $n+1;
101  if($n == 0) return Ack($m-1, 1);
102  return Ack($m - 1, Ack($m, ($n - 1)));
103}
104
105function ackermann($n) {
106  $r = Ack(3,$n);
107  print "Ack(3,$n): $r\n";
108}
109
110/****/
111
112function ary($n) {
113  for ($i=0; $i<$n; $i++) {
114    $X[$i] = $i;
115  }
116  for ($i=$n-1; $i>=0; $i--) {
117    $Y[$i] = $X[$i];
118  }
119  $last = $n-1;
120  print "$Y[$last]\n";
121}
122
123/****/
124
125function ary2($n) {
126  for ($i=0; $i<$n;) {
127    $X[$i] = $i; ++$i;
128    $X[$i] = $i; ++$i;
129    $X[$i] = $i; ++$i;
130    $X[$i] = $i; ++$i;
131    $X[$i] = $i; ++$i;
132
133    $X[$i] = $i; ++$i;
134    $X[$i] = $i; ++$i;
135    $X[$i] = $i; ++$i;
136    $X[$i] = $i; ++$i;
137    $X[$i] = $i; ++$i;
138  }
139  for ($i=$n-1; $i>=0;) {
140    $Y[$i] = $X[$i]; --$i;
141    $Y[$i] = $X[$i]; --$i;
142    $Y[$i] = $X[$i]; --$i;
143    $Y[$i] = $X[$i]; --$i;
144    $Y[$i] = $X[$i]; --$i;
145
146    $Y[$i] = $X[$i]; --$i;
147    $Y[$i] = $X[$i]; --$i;
148    $Y[$i] = $X[$i]; --$i;
149    $Y[$i] = $X[$i]; --$i;
150    $Y[$i] = $X[$i]; --$i;
151  }
152  $last = $n-1;
153  print "$Y[$last]\n";
154}
155
156/****/
157
158function ary3($n) {
159  for ($i=0; $i<$n; $i++) {
160    $X[$i] = $i + 1;
161    $Y[$i] = 0;
162  }
163  for ($k=0; $k<1000; $k++) {
164    for ($i=$n-1; $i>=0; $i--) {
165      $Y[$i] += $X[$i];
166    }
167  }
168  $last = $n-1;
169  print "$Y[0] $Y[$last]\n";
170}
171
172/****/
173
174function fibo_r($n){
175    return(($n < 2) ? 1 : fibo_r($n - 2) + fibo_r($n - 1));
176}
177
178function fibo($n) {
179  $r = fibo_r($n);
180  print "$r\n";
181}
182
183/****/
184
185function hash1($n) {
186  for ($i = 1; $i <= $n; $i++) {
187    $X[dechex($i)] = $i;
188  }
189  $c = 0;
190  for ($i = $n; $i > 0; $i--) {
191    if ($X[dechex($i)]) { $c++; }
192  }
193  print "$c\n";
194}
195
196/****/
197
198function hash2($n) {
199  for ($i = 0; $i < $n; $i++) {
200    $hash1["foo_$i"] = $i;
201    $hash2["foo_$i"] = 0;
202  }
203  for ($i = $n; $i > 0; $i--) {
204    foreach($hash1 as $key => $value) $hash2[$key] += $value;
205  }
206  $first = "foo_0";
207  $last  = "foo_".($n-1);
208  print "$hash1[$first] $hash1[$last] $hash2[$first] $hash2[$last]\n";
209}
210
211/****/
212
213function gen_random ($n) {
214    global $LAST;
215    return( ($n * ($LAST = ($LAST * IA + IC) % IM)) / IM );
216}
217
218function heapsort_r($n, &$ra) {
219    $l = ($n >> 1) + 1;
220    $ir = $n;
221
222    while (1) {
223    if ($l > 1) {
224        $rra = $ra[--$l];
225    } else {
226        $rra = $ra[$ir];
227        $ra[$ir] = $ra[1];
228        if (--$ir == 1) {
229        $ra[1] = $rra;
230        return;
231        }
232    }
233    $i = $l;
234    $j = $l << 1;
235    while ($j <= $ir) {
236        if (($j < $ir) && ($ra[$j] < $ra[$j+1])) {
237        $j++;
238        }
239        if ($rra < $ra[$j]) {
240        $ra[$i] = $ra[$j];
241        $j += ($i = $j);
242        } else {
243        $j = $ir + 1;
244        }
245    }
246    $ra[$i] = $rra;
247    }
248}
249
250function heapsort($N) {
251  global $LAST;
252
253  define("IM", 139968);
254  define("IA", 3877);
255  define("IC", 29573);
256
257  $LAST = 42;
258  for ($i=1; $i<=$N; $i++) {
259    $ary[$i] = gen_random(1);
260  }
261  heapsort_r($N, $ary);
262  printf("%.10f\n", $ary[$N]);
263}
264
265/****/
266
267function mkmatrix ($rows, $cols) {
268    $count = 1;
269    $mx = array();
270    for ($i=0; $i<$rows; $i++) {
271    for ($j=0; $j<$cols; $j++) {
272        $mx[$i][$j] = $count++;
273    }
274    }
275    return($mx);
276}
277
278function mmult ($rows, $cols, $m1, $m2) {
279    $m3 = array();
280    for ($i=0; $i<$rows; $i++) {
281    for ($j=0; $j<$cols; $j++) {
282        $x = 0;
283        for ($k=0; $k<$cols; $k++) {
284        $x += $m1[$i][$k] * $m2[$k][$j];
285        }
286        $m3[$i][$j] = $x;
287    }
288    }
289    return($m3);
290}
291
292function matrix($n) {
293  $SIZE = 30;
294  $m1 = mkmatrix($SIZE, $SIZE);
295  $m2 = mkmatrix($SIZE, $SIZE);
296  while ($n--) {
297    $mm = mmult($SIZE, $SIZE, $m1, $m2);
298  }
299  print "{$mm[0][0]} {$mm[2][3]} {$mm[3][2]} {$mm[4][4]}\n";
300}
301
302/****/
303
304function nestedloop($n) {
305  $x = 0;
306  for ($a=0; $a<$n; $a++)
307    for ($b=0; $b<$n; $b++)
308      for ($c=0; $c<$n; $c++)
309        for ($d=0; $d<$n; $d++)
310          for ($e=0; $e<$n; $e++)
311            for ($f=0; $f<$n; $f++)
312             $x++;
313  print "$x\n";
314}
315
316/****/
317
318function sieve($n) {
319  $count = 0;
320  while ($n-- > 0) {
321    $count = 0;
322    $flags = range (0,8192);
323    for ($i=2; $i<8193; $i++) {
324      if ($flags[$i] > 0) {
325        for ($k=$i+$i; $k <= 8192; $k+=$i) {
326          $flags[$k] = 0;
327        }
328        $count++;
329      }
330    }
331  }
332  print "Count: $count\n";
333}
334
335/****/
336
337function strcat($n) {
338  $str = "";
339  while ($n-- > 0) {
340    $str .= "hello\n";
341  }
342  $len = strlen($str);
343  print "$len\n";
344}
345
346/*****/
347
348function gethrtime()
349{
350  $hrtime = hrtime();
351  return (($hrtime[0]*1000000000 + $hrtime[1]) / 1000000000);
352}
353
354function start_test()
355{
356    ob_start();
357  return gethrtime();
358}
359
360function end_test($start, $name)
361{
362  global $total;
363  $end = gethrtime();
364  ob_end_clean();
365  $total += $end-$start;
366  $num = number_format($end-$start,3);
367  $pad = str_repeat(" ", 24-strlen($name)-strlen($num));
368
369  echo $name.$pad.$num."\n";
370    ob_start();
371  return gethrtime();
372}
373
374function total()
375{
376  global $total;
377  $pad = str_repeat("-", 24);
378  echo $pad."\n";
379  $num = number_format($total,3);
380  $pad = str_repeat(" ", 24-strlen("Total")-strlen($num));
381  echo "Total".$pad.$num."\n";
382}
383
384$t0 = $t = start_test();
385simple();
386$t = end_test($t, "simple");
387simplecall();
388$t = end_test($t, "simplecall");
389simpleucall();
390$t = end_test($t, "simpleucall");
391simpleudcall();
392$t = end_test($t, "simpleudcall");
393mandel();
394$t = end_test($t, "mandel");
395mandel2();
396$t = end_test($t, "mandel2");
397ackermann(7);
398$t = end_test($t, "ackermann(7)");
399ary(50000);
400$t = end_test($t, "ary(50000)");
401ary2(50000);
402$t = end_test($t, "ary2(50000)");
403ary3(2000);
404$t = end_test($t, "ary3(2000)");
405fibo(30);
406$t = end_test($t, "fibo(30)");
407hash1(50000);
408$t = end_test($t, "hash1(50000)");
409hash2(500);
410$t = end_test($t, "hash2(500)");
411heapsort(20000);
412$t = end_test($t, "heapsort(20000)");
413matrix(20);
414$t = end_test($t, "matrix(20)");
415nestedloop(12);
416$t = end_test($t, "nestedloop(12)");
417sieve(30);
418$t = end_test($t, "sieve(30)");
419strcat(200000);
420$t = end_test($t, "strcat(200000)");
421total();
422?>

首先是不启用(php -d opcache.jit_buffer_size=0 Zend/bench.php):

 1simple 0.008
 2
 3simplecall 0.004
 4
 5simpleucall 0.004
 6
 7simpleudcall 0.004
 8
 9mandel 0.035
10
11mandel2 0.055
12
13ackermann(7) 0.020
14
15ary(50000) 0.004
16
17ary2(50000) 0.003
18
19ary3(2000) 0.048
20
21fibo(30) 0.084
22
23hash1(50000) 0.013
24
25hash2(500) 0.010
26
27heapsort(20000) 0.027
28
29matrix(20) 0.026
30
31nestedloop(12) 0.023
32
33sieve(30) 0.013
34
35strcat(200000) 0.006
36
37------------------------
38
39Total 0.387

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

 1simple 0.002
 2
 3simplecall 0.001
 4
 5simpleucall 0.001
 6
 7simpleudcall 0.001
 8
 9mandel 0.010
10
11mandel2 0.011
12
13ackermann(7) 0.010
14
15ary(50000) 0.003
16
17ary2(50000) 0.002
18
19ary3(2000) 0.018
20
21fibo(30) 0.031
22
23hash1(50000) 0.011
24
25hash2(500) 0.008
26
27heapsort(20000) 0.014
28
29matrix(20) 0.015
30
31nestedloop(12) 0.011
32
33sieve(30) 0.005
34
35strcat(200000) 0.004
36
37------------------------
38
39Total 0.157

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