Derick Rethans - tag: vld https://derickrethans.nl/feed-vld.xml This feed shows the latest 100 items with the tag vld en-us All rights reserved - Derick Rethans derick@derickrethans.nl (Derick Rethans) Tue, 25 Mar 2025 10:06:03 +0000 Tue, 25 Mar 2025 10:06:03 +0000 eZ Components Feed dev (http://ezcomponents.org/docs/tutorials/Feed) http://www.rssboard.org/rss-specification 60 Derick Rethans derick@derickrethans.nl Derick Rethans PHP 7.2's "switch" optimisations https://derickrethans.nl/php7.2-switch.html <div class="article"> <div class="body"> <div class="articleListItem"> <h1><a name="php_7_2_s_switch_optimisations"/>PHP 7.2's "switch" optimisations</h1> <dl class="head"/> <div class="PHP 7Content"> <div class="articleMetaData"> <div class="date">Wednesday, November 1st 2017, 10:33 GMT</div> <div class="location"> London, UK</div> </div> <p>PHP 7.2 is around the corner soon, and comes with many optimisations. Many new optimisations are implemented in <a href="/load/view.php?a=aHR0cDovL3BocC5uZXQvbWFudWFsL2VuL2Jvb2sub3BjYWNoZS5waHA">opcache</a>, but some others are implemented in PHP itself. One optimisation that falls in the latter category is an optimisation of the <code>switch</code>/<code>case</code> construct.</p> <p>Before PHP 7.2, PHP considers each <code>case</code> statement in order. Let's say we have the following (trivial code):</p> <pre>&lt;?php $cc = "no"; switch ( $cc ) { case 'de': echo "DEUTSCH"; break; case 'en': echo "English"; break; case 'nl': echo "Nederlands"; break; case 'no': echo "Norsk"; break; default: echo "unknown"; break; } ?&gt; </pre> <p>The PHP interpreter approaches the executing of this <code>switch</code>/<code>case</code> structure as if it was written like:</p> <pre>&lt;?php $cc = "no"; if ($cc == 'de') { echo "DEUTSCH"; } else if ($cc == 'en') { echo "English"; } else if ($cc == 'nl') { echo "Nederlands"; } else if ($cc == 'no') { echo "Norsk"; } else { echo "unknown"; break; } ?&gt; </pre> <p>Yup, it really compares <code>$cc</code> variable to each <code>case</code> statement one by one. Which means that if your most commonly used switch case is further down the list, you end up wasting valuable CPU code.</p> <p>With <a href="/load/view.php?a=aHR0cHM6Ly9kZXJpY2tyZXRoYW5zLm5sL3Byb2plY3RzLmh0bWwjdmxk">vld</a> we can represent this in a graph. For each <code>case</code> statement, the graph shows that the interpreter can either go <em>left</em> or <em>right</em>, just as it was executing multiple <code>if</code> statements in sequence:</p> <img src="derickrethans.nl/rst/images/switch-php71.png"/> <p>PHP 7.2 introduces an optimisation that converts this sequence of <code>if</code> statements into a jump table if all the <code>case</code> statements are either integers <strong>or</strong> strings. If we look at the pseudo code (PHP does not support variable labels with <code>goto</code>), we get something like:</p> <pre>&lt;?php $cc = "no"; $_table = [ "de" =&gt; 1, "en" =&gt; 2, "nl" =&gt; 3, "no" =&gt; 4 ]; if (gettype($cc) == 'string') { if (array_key_exists($cc, $_table)) { goto "jmp_{$_table[$cc]}"; } else { goto jmp_default; } } else { /* do original if/else if/else sequence */ } jmp_1: echo "DEUTSCH"; goto end; jmp_2: echo "English"; goto end; jmp_3: echo "Nederlands"; goto end; jmp_4: echo "Norsk"; goto end; jmp_default: echo "unknown"; end: ?&gt; </pre> <p>This example shows that as long as <code>$cc</code> is a string, it will use the jump table <code>$_table</code> to jump (with <code>goto</code>) to the immediate code representing the <code>case</code> statement. If <code>$cc</code> is not a string, it will have to do the usual <em>if/else if/else</em> dance. In this case, it will eventually end up on the <code>default</code> case.</p> <p>Looking at the graph generated by <a href="/load/view.php?a=aHR0cHM6Ly9kZXJpY2tyZXRoYW5zLm5sL3Byb2plY3RzLmh0bWwjdmxk">vld</a> for this optimised construct, we see this new structure as well:</p> <img src="derickrethans.nl/rst/images/switch-php72.png"/> <p>Especially with big <code>switch</code>/<code>case</code> structures this is a welcome optimisation!</p> <p>However&#x2026;</p> <p><a href="/load/view.php?a=aHR0cHM6Ly94ZGVidWcub3Jn">Xdebug</a> implements <a href="/load/view.php?a=aHR0cHM6Ly94ZGVidWcub3JnL2RvY3MvY29kZV9jb3ZlcmFnZQ">code coverage</a> gathering functionality. This can be used with <a href="/load/view.php?a=aHR0cHM6Ly9naXRodWIuY29tL3NlYmFzdGlhbmJlcmdtYW5uL3BocC1jb2RlLWNvdmVyYWdl">PHP_CodeCoverage</a> to see which parts of your code, which branches, and which <a href="/load/view.php?a=aHR0cHM6Ly9kZXJpY2tyZXRoYW5zLm5sL3BhdGgtYnJhbmNoLWNvdmVyYWdlLmh0bWw">paths</a> are covered while executing your tests. In order for <a href="/load/view.php?a=aHR0cHM6Ly94ZGVidWcub3Jn">Xdebug</a> to figure out the branches and paths, it has to follow PHP's internal opcode structures to see where jump instructions happen. I had to add support for this new <code>switch</code>/<code>case</code> optimisation to make this work again. This has now been <a href="/load/view.php?a=aHR0cHM6Ly9naXRodWIuY29tL3hkZWJ1Zy94ZGVidWcvY29tbWl0LzMzMWMzZWM5MDcxYmE3Mzk5NTE1MzBlYzY2ODZkNjc4NTkyOTFmNmE">done</a>, and is part of Xdebug's master branch. This branch will soon become the first beta release of Xdebug 2.6 which will provide support for PHP 7.2.</p> <div class="patreon"> <a href="/load/view.php?a=aHR0cHM6Ly93d3cucGF0cmVvbi5jb20vYmVQYXRyb24/dT03ODY0MzI4"> <img src="/load/view.php?a=aHR0cHM6Ly9kZXJpY2tyZXRoYW5zLm5sL2ltYWdlcy9iZWNvbWVfYV9wYXRyb25fYnV0dG9uLnBuZw" alt="Become a Patron!"/> </a> </div> </div> </div> </div> </div> derick@derickrethans.nl (Derick Rethans) https://derickrethans.nl/php7.2-switch.html Wed, 01 Nov 2017 10:33:00 +0000 no VLD released https://derickrethans.nl/vld-released.html <div class="article"> <div class="body"> <div class="articleListItem"> <h1><a name="vld_released"/>VLD released</h1> <dl class="head"/> <div class="VLD releasedContent"> <div class="articleMetaData"> <div class="date">Monday, April 12th 2010, 21:57 BST</div> <div class="location"> London, UK</div> </div> <p>I just released a new version of <a href="/load/view.php?a=aHR0cDovL2Rlcmlja3JldGhhbnMubmwvcHJvamVjdHMuaHRtbCN2bGQ">VLD</a> through <a href="/load/view.php?a=aHR0cDovL3BlY2wucGhwLm5ldC9wYWNrYWdlL3ZsZA">PECL</a>. This release features the path analytics code that I've outlined in an <a href="/load/view.php?a=aHR0cDovL2Rlcmlja3JldGhhbnMubmwvbW9yZS1zb3VyY2UtYW5hbHlzaXMtd2l0aC12bGQuaHRtbA">earlier article</a>. There are also a few fixes for PHP's current trunk, as well as the possibility to format output slightly like a CSV file. That feature was used to make <a href="/load/view.php?a=aHR0cDovL3d3dy56YXB0LmluZm8vb3Bjb2Rlcy5odG1s">this</a> documentation of opcodes.</p> <p>The path analysis algorithm that <a href="/load/view.php?a=aHR0cDovL2Rlcmlja3JldGhhbnMubmwvcHJvamVjdHMuaHRtbCN2bGQ">VLD</a> now has, can possibly also be used to get path/branch coverage working within <a href="/load/view.php?a=aHR0cDovL3hkZWJ1Zy5vcmc">Xdebug's</a> code-coverage framework. This is however not a minor task.</p> <div class="patreon"> <a href="/load/view.php?a=aHR0cHM6Ly93d3cucGF0cmVvbi5jb20vYmVQYXRyb24/dT03ODY0MzI4"> <img src="/load/view.php?a=aHR0cHM6Ly9kZXJpY2tyZXRoYW5zLm5sL2ltYWdlcy9iZWNvbWVfYV9wYXRyb25fYnV0dG9uLnBuZw" alt="Become a Patron!"/> </a> </div> </div> </div> </div> </div> derick@derickrethans.nl (Derick Rethans) https://derickrethans.nl/vld-released.html Mon, 12 Apr 2010 20:57:00 +0000 no More source analysis with VLD https://derickrethans.nl/more-source-analysis-with-vld.html <div class="article"> <div class="body"> <div class="articleListItem"> <h1><a name="more_source_analysis_with_vld"/>More source analysis with VLD</h1> <dl class="head"/> <div class="More source analysis with VLDContent"> <div class="articleMetaData"> <div class="date">Friday, February 19th 2010, 11:27 GMT</div> <div class="location"> London, UK</div> </div> <p><a href="/load/view.php?a=aHR0cDovL2Rlcmlja3JldGhhbnMubmwvcHJvamVjdHMuaHRtbCN2bGQ">VLD</a> is a tool that I started working on years ago to visualise the opcode arrays in PHP. Opcode arrays are what PHP's compiler generates from your source code and can be compared to assembler code that is generated by a C compiler. Instead of it being directly executed by the CPU, it is instead executed by PHP's interpreter.</p> <p>Over the years I've been adding some functionality, also aided by <a href="/load/view.php?a=aHR0cDovL2lsaWEud3M">Ilia</a> and some others, to show more information. For example Ilia has added a more verbose dumping format for opcodes (through the <code>vld.verbosity</code> setting) whereas I have added routines to find out which ops in oparrays can never be reached. A very simple example of the latter is shown here:</p> <pre>&lt;?php function test() { echo "Hello!\n"; return true; echo "This will not be executed.\n"; } ?&gt; </pre> <p>If we run the above through VLD with <code>php -dvld.active=1 test.php</code>, you'll see the following output (I removed the part about the script body itself):</p> <pre>Function test: filename: /tmp/test1.php function name: test number of ops: 9 compiled vars: none line # * op fetch ext return operands --------------------------------------------------------- 2 0 &gt; EXT_NOP 4 1 EXT_STMT 2 ECHO 'Hello%21%0A' 5 3 EXT_STMT 4 &gt; RETURN true 7 5* EXT_STMT 6* ECHO 'This+will+not+be+executed.%0A' 8 7* EXT_STMT 8* &gt; RETURN null End of function test. </pre> <p>Every opcode that has a <code>*</code> after the number (like in <code>5*</code>) is code that can not be reached, and can possibly be eliminated from the oparrays in an optimiser.</p> <p>The dead code analysis routines have also made their way into <a href="/load/view.php?a=aHR0cDovL3hkZWJ1Zy5vcmc">Xdebug</a> which uses them for the <a href="/load/view.php?a=aHR0cDovL3hkZWJ1Zy5vcmcvZG9jcy9jb2RlX2NvdmVyYWdl">code coverage</a> functionality to highlight dead code. This mostly makes sense if you are running your code coverage together with unit tests such as you can do with <a href="/load/view.php?a=aHR0cDovL3d3dy5waHB1bml0LmRl">PHPUnit</a>.</p> <p>Recently I've been working on some new functionality to visualise all the code <em>paths</em> that make up each function. These new routines sit on top of the routines that do dead code analysis. Every branch instruction (such as <code>if</code>, but also <code>for</code> and <code>foreach</code>) is analysed and a list of branches is created. Each branch contains information about the line on which the branch starts, the starting and ending opcode numbers that belong to the branch, as well as to which other branches this branch can jump to. There can be either no linked branches (when for example a <code>return</code> or <code>throw</code> statement is found), one linked branch (for an unconditional jump) or two linked branches (on a branch instruction). However, you need to be aware that internally, PHP's opcode don't always reflect the <em>source code</em> exactly.</p> <p>Once all the branches and their links are found, another algorithm runs to figure out which paths can be created out of all the branches. It is best to illustrate this with an example. So let us look at the following script:</p> <pre>&lt;?php function test() { for( $i = 0; $i &lt; 10; $i++ ) { if ( $i &lt; 5 ) { echo "-"; } else { echo "+"; } } echo "\n"; } ?&gt; </pre> <p>In this script we have a <code>for</code>-loop with a nested <code>if</code> construct. When we run this script through VLD (with <code>php -dvld.verbosity=0 -dvld.dump_paths=1 -dvld.active=1 test2.php</code>) we get the following output (again, only the <code>test()</code> function and with some white space modifications):</p> <pre>Function test: filename: /tmp/test2.php function name: test number of ops: 22 compiled vars: !0 = $i line # * op fetch ext return operands ----------------------------------------------------------- 2 0 &gt; EXT_NOP 4 1 EXT_STMT 2 ASSIGN !0, 0 3 &gt; IS_SMALLER ~1 !0, 10 4 EXT_STMT 5 &gt; JMPZNZ 9 ~1, -&gt;18 6 &gt; POST_INC ~2 !0 7 FREE ~2 8 &gt; JMP -&gt;3 6 9 &gt; EXT_STMT 10 IS_SMALLER ~3 !0, 5 7 11 &gt; JMPZ ~3, -&gt;15 8 12 &gt; EXT_STMT 13 ECHO '-' 9 14 &gt; JMP -&gt;17 12 15 &gt; EXT_STMT 16 ECHO '%2B' 14 17 &gt; &gt; JMP -&gt;6 15 18 &gt; EXT_STMT 19 ECHO '%0A' 16 20 EXT_STMT 21 &gt; RETURN null branch: # 0; line: 2- 4; sop: 0; eop: 2; out1: 3 branch: # 3; line: 4- 4; sop: 3; eop: 5; out1: 18; out2: 9 branch: # 6; line: 4- 4; sop: 6; eop: 8; out1: 3 branch: # 9; line: 6- 7; sop: 9; eop: 11; out1: 12; out2: 15 branch: # 12; line: 8- 9; sop: 12; eop: 14; out1: 17 branch: # 15; line: 12-14; sop: 15; eop: 16; out1: 17 branch: # 17; line: 14-14; sop: 17; eop: 17; out1: 6 branch: # 18; line: 15-16; sop: 18; eop: 21 path #1: 0, 3, 18, path #2: 0, 3, 9, 12, 17, 6, 3, 18, path #3: 0, 3, 9, 15, 17, 6, 3, 18, End of function test. </pre> <p>This dump consists of a few different parts. First of all we can see some basic information containing the name, the number of ops (22) and the compiled variables. The second part is a dump of all the opcodes that make up this function. The last part contains information about all the branches and the possible paths. This information is a bit hard to visualize in its textual form, so I've also added some code that dumps this information to a file format that the <a href="/load/view.php?a=aHR0cDovL2dyYXBodml6Lm9yZy8">GraphViz</a> tool "dot" can use to create a pretty graph. For this we re-run the previous PHP invocation as <code>php -dvld.dump_paths=1 -dvld.verbosity=0 -dvld.save_paths=1 -dvld.active=1 test2.php</code>. This creates the file <code>/tmp/paths.dot</code> that "dot" can use. If we run <code>dot -Tpng /tmp/paths.dot &gt; /tmp/paths.png</code> we end up with the following picture:</p> <img src="derickrethans.nl/images/vld-paths.png"/> <p>If we put this graph next to the code, we can explain how this works. Every branch is named by the number of the first opcode in that branch:</p> <ul> <li> <p><code>op #1</code> is the assignment of <code>$i</code> in line 4.</p> </li> <li> <p><code>op #3</code> is the <em>loop test</em> in line 4. If the condition doesn't match, we jump to <code>op #18</code> on line 16 that echos the newline.</p> </li> <li> <p><code>op #9</code> is the <code>if</code> condition on line 6.</p> </li> <li> <p><code>op #12</code> is when the <code>if</code> condition returns true and</p> </li> <li> <p><code>op #15</code> is when the <code>if</code> condition returns false.</p> </li> <li> <p><code>op #17</code> sits behind both <code>op #12</code> and <code>op #15</code> and makes sure there is a jump to the counting expression in <code>#op 6</code>.</p> </li> <li> <p><code>op #6</code> is the post increment operation on line 4 which will then again be followed by <code>op #3</code> to check whether the end of the loop has been reached.</p> </li> </ul> <p>This is of course a very simple example, but it also works for (multiple) classes and functions in a file. You just need to make sure to tell VLD that you don't want the code <em>executed</em> as the output could be very large. You can use the <code>vld.execute=0</code> php.ini setting for that.</p> <p>I hope this new functionality can spread some light on how loops etc. work in PHP. In order to play with the code, you need to check-out VLD from my SVN with <code>svn co svn://svn.xdebug.org/svn/php/vld/trunk vld</code>. You can also view the code on-line at <a href="/load/view.php?a=aHR0cDovL3N2bi54ZGVidWcub3JnL2NnaS1iaW4vdmlld3ZjLmNnaS92bGQvdHJ1bmsvP3Jvb3Q9cGhw">http://svn.xdebug.org/cgi-bin/viewvc.cgi/vld/trunk/?root=php</a>. Look out for a new release coming soon!</p> <div class="patreon"> <a href="/load/view.php?a=aHR0cHM6Ly93d3cucGF0cmVvbi5jb20vYmVQYXRyb24/dT03ODY0MzI4"> <img src="/load/view.php?a=aHR0cHM6Ly9kZXJpY2tyZXRoYW5zLm5sL2ltYWdlcy9iZWNvbWVfYV9wYXRyb25fYnV0dG9uLnBuZw" alt="Become a Patron!"/> </a> </div> </div> </div> </div> </div> derick@derickrethans.nl (Derick Rethans) https://derickrethans.nl/more-source-analysis-with-vld.html Fri, 19 Feb 2010 11:27:00 +0000 no