PHP8 JIT 配置说明
注意点:
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个独立的数字组成,从左到右分别是:
是否在生成机器码点时候使用AVX指令, 需要CPU支持:
- 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
基于此,我们可以大概得到如下几个结论:
尽量使用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倍。