Home > Article > Backend Development > PHP opcode – Improve application performance without changing your code
The PHP opcode generated by the PHP engine is strongly influenced by the way you write your code. Not only in terms of the number of statements to accomplish a task. Clearly it matters a lot, and I think it’s obvious to you.
What could be less obvious is that even the syntax of the code can completely change the generated opcode causing a lot of overhead for the machine’s CPU to execute the exact same code.
In the last few years my SaaS product has grown a lot, and it has given me the opportunity to go deeper and deeper into the optimization techniques to run my workload as efficiently as possible.
The results I saw are impressive and helped me a lot in unlocking free cash flow to continue developing my SaaS journey.
At this point the PHP process inside my SaaS product is processing more than 1.2 billion (with B) data packets every day on a machine with 2vCPU and 8GB of memory. I use an AWS autoscaling group to have more flexibility in case of unpredictable spikes, but it rarely adds a second machine (one/two times a week).
For more technical articles you can follow me on Linkedin or X.
Recently I also wrote about the migration of the Inspector servers to ARM instances: https://inspector.dev/inspector-adoption-of-graviton-arm-instances-and-what-results-weve-seen/
Let's go into the topic of the article. I think you will find it very interesting.
PHP opcode stands for operation code, and it refers to the low-level instructions that are executed by the PHP engine after the PHP source code you write has been compiled.
In PHP, code compilation happens at runtime, basically the first time that your code is taken by the PHP engine it will be compiled into this machine friendly code, cached, so the engine doesn’t compile the same code again, and then executed.
This is a simple representation of the process:
Caching the PHP opcode allows you to save three steps in the process of executing the code: Parsing the raw PHP code, Tokenization, and Compilation.
Once the opcode is generated for the first time it is stored in memory so it can be reused in subsequent requests. This reduces the need for the PHP engine to recompile the same PHP code every time it’s executed, saving a lot of CPU and memory consumption.
The most commonly used opcode cache in PHP is OPCache, and it is included by default since PHP 5.5 up to recent versions. It is highly efficient and widely supported.
缓存预编译脚本字节码需要在每次部署后使缓存失效。因为如果更改的文件在缓存中具有字节码版本,PHP 将继续运行旧版本的代码。直到您清除操作码缓存,以便再次编译新代码并生成新的缓存项。
要了解不同的语法如何影响脚本的操作码,我们需要一种方法来获取 PHP 引擎生成的编译代码。
有两种获取操作码的方法。
如果您的计算机上启用了 OPCache 扩展,您可以使用其本机函数来获取特定 php 文件的操作码:
// Force compilation of a script opcache_compile_file(__DIR__.'/yourscript.php'); // Get OPcache status $status = opcache_get_status(); // Inspect the script's entry in the cache print_r($status['scripts'][__DIR__.'/yourscript.php']);
VLD 是一个流行的 PHP 扩展,它可以反汇编已编译的 PHP 代码并输出操作码。它是了解 PHP 如何解释和执行代码的强大工具。安装后,您可以使用带有 -d 选项的 php 命令来运行启用了 VLD 的 PHP 脚本:
php -d vld.active=1 -d vld.execute=0 yourscript.php
输出将包含有关已编译操作码的详细信息,包括每个操作、其关联的代码行等等。
3v4l 是一个非常有用的在线工具,它允许您查看在编辑器中输入的 PHP 代码生成的操作码。它基本上是一个安装了 VLD 的 PHP 服务器,因此它可以抓取 VLD 输出并向您显示浏览器中的操作码。
由于它是免费分发的,我们将使用这个在线工具进行下一步分析。
3v4l 非常适合理解我们使用的代码语法如何以好或坏的方式影响生成的 PHP 操作码。让我们开始将下面的代码粘贴到 3v4l 中。保留配置“所有支持的版本”,然后单击“评估”。
<?php namespace App; strlen('ciao');
执行代码后,底部会出现一个选项卡菜单。导航到 VLD 选项卡以可视化相应的操作码。
line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 5 0 E > INIT_NS_FCALL_BY_NAME 'App%5CSpace%5Cstrlen' 1 SEND_VAL_EX 'ciao' 2 DO_FCALL 0 3 > RETURN 1
请注意,第一个操作是 INIT_NS_FCALL_BY_NAME。解释器使用当前文件的命名空间构造函数的名称。但它并不存在于 AppExample 命名空间中,那么它是如何工作的呢?
解释器将检查该函数是否存在于当前命名空间中。如果没有,它会尝试调用相应的核心函数。
这里我们有机会告诉解释器避免这种双重检查并直接执行核心函数。
尝试在strlen前加一个反斜杠(),然后点击“eval”:
<?php namespace App; \strlen('ciao');
在 VLD 选项卡中,您现在只需一条语句即可看到操作码。
line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 5 0 E > > RETURN 1
这是因为你传达了函数的确切位置,所以它不需要考虑任何后备。
If don't like to use the the backslash, you can import the function like any other class from the root namespace:
<?php namespace App; use function strlen; strlen('ciao');
There are also a lot of internal automatisms of the PHP engine to generate an optimized opcode evaluating static expressions in advance. This was one of the most important reasons of the great performance improvement of PHP since the version 7.x
Being aware of these dynamics can really help you reduce resource consumption and cut costs. Once I made this research, I started using these tricks throughout the code.
Let me show you an example using PHP constants. Run this script into 3v4l:
<?php namespace App; if (PHP_OS === 'Linux') { echo "Linux"; }
Take a look at the first two lines of the PHP opcode:
line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 5 0 E > FETCH_CONSTANT ~0 'App%5CPHP_OS' 1 IS_IDENTICAL ~0, 'Linux' 2 > JMPZ ~1, ->4 6 3 > ECHO 'Linux' 7 4 > > RETURN 1
FETCH_CONSTANT try to get the value of PHP_OS from the current namespace and it will look into the global namespace as it doesn’t exist here. Then the IS_IDENTICAL instruction executes the IF statement.
Now try adding the backslash to constant:
<?php namespace App; if (\PHP_OS === 'Linux') { echo "Linux"; }
As you can see in the opcode the engine doesn't need to try to fetch the constant because now it's clear where it is, and since it's a static value it already has it in memory.
Also the IF statement disappeared because the other side of the IS_IDENTITCAL statement is a static string ('Linux') so the IF can be marked as "true" without the overhead of interpreting it on every execution.
This is why you have a lot of power to influence the ultimate performance of your PHP code.
I hope it was an interesting topic, as I mentioned at the beginning of the article I'm getting a lot of benefits from using this tactic and in fact they are also used in our packages.
You can see here an example of how I used this tips in our PHP package to optimize its performance: https://github.com/inspector-apm/inspector-php/blob/master/src/Inspector.php#L302
For more technical articles you can follow me on Linkedin or X.
Inspector is a Code Execution Monitoring tool specifically designed for software developers. You don't need to install anything at the server level, just install the Laravel or Symfony package and you are ready to go.
If you are looking for HTTP monitoring, database query insights, and the ability to forward alerts and notifications into your preferred messaging environment, try Inspector for free. Register your account.
Or learn more on the website: https://inspector.dev
The above is the detailed content of PHP opcode – Improve application performance without changing your code. For more information, please follow other related articles on the PHP Chinese website!