OPcache优化opcode

这篇文章,我们会通过一些例子来介绍一下Opcache对于opcode的一些优化。

简单的本地优化(pass1)

这个pass会开启一些简单的优化,例如优化常数条件JMP

对应的opcache配置如下:

1
2
3
4
5
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.opt_debug_level=0x30000
opcache.optimization_level=0x1

我们有如下代码:

1
2
3
4
5
6
7
8
9
10
<?php

function foo()
{
if (1) {
echo 1;
} else {
echo 2;
}
}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
foo:
; (lines=5, args=0, vars=0, tmps=0)
; (before optimizer)
; /Users/hantaohuang/codeDir/cCode/php-src/test.php:3-10
0000 JMPZ int(1) 0003
0001 ECHO int(1)
0002 JMP 0004
0003 ECHO int(2)
0004 RETURN null

foo:
; (lines=5, args=0, vars=0, tmps=0)
; (after optimizer)
; /Users/hantaohuang/codeDir/cCode/php-src/test.php:3-10
0000 NOP
0001 ECHO int(1)
0002 JMP 0004
0003 ECHO int(2)
0004 RETURN null

我们发现,优化前,我们需要执行JMPZ,并且按照条件来执行0003或者0004。但是优化后的代码,我们只需要执行00010004即可。我们发现,优化后的代码00020003实际上不会被执行,但是却生成了opcode,实际上是因为我们没有开启对应的优化,我们后面会有例子来讲解。

常数传播优化(pass8)

对应的opcache配置如下:

1
2
3
4
5
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.opt_debug_level=0x30000
opcache.optimization_level=0xe0

我们有如下代码:

1
2
3
4
5
6
7
<?php

function foo()
{
$a = 1;
echo $a + 2 + 3;
}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
foo:
; (lines=5, args=0, vars=1, tmps=3)
; (before optimizer)
; /Users/hantaohuang/codeDir/cCode/php-src/test.php:3-7
; return [] RANGE[0..0]
0000 ASSIGN CV0($a) int(1)
0001 T2 = ADD CV0($a) int(2)
0002 T3 = ADD T2 int(3)
0003 ECHO T3
0004 RETURN null

foo:
; (lines=3, args=0, vars=1, tmps=3)
; (after optimizer)
; /Users/hantaohuang/codeDir/cCode/php-src/test.php:3-7
0000 CV0($a) = QM_ASSIGN int(1)
0001 ECHO string("6")
0002 RETURN null

我们发现,因为a是常量1,所以在优化opcode的时候,会直接用1替换掉a

死代码消除(pass14)

对应的opcache配置如下:

1
2
3
4
5
zend_extension=opcache.so
opcache.enable=1
opcache.enable_cli=1
opcache.opt_debug_level=0x30000
opcache.optimization_level=0x2061

我们有如下代码:

1
2
3
4
5
6
7
8
9
10
<?php

function foo()
{
if (1) {
echo 1;
} else {
echo 2;
}
}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
foo:
; (lines=5, args=0, vars=0, tmps=0)
; (before optimizer)
; /Users/hantaohuang/codeDir/cCode/php-src/test.php:3-10
; return [] RANGE[0..0]
0000 JMPZ int(1) 0003
0001 ECHO int(1)
0002 JMP 0004
0003 ECHO int(2)
0004 RETURN null

foo:
; (lines=2, args=0, vars=0, tmps=0)
; (after optimizer)
; /Users/hantaohuang/codeDir/cCode/php-src/test.php:3-10
0000 ECHO int(1)
0001 RETURN null

我们发现,优化前的0000, 0002, 0003都被删除了,因为它们不会被执行。