
Regarding PHP micro-optimizations by replacing double quotes with single quotes, there are so many broken copies that it is quite problematic to make a fresh stream. But I'll try.
In this article there will be only one benchmark, where without it, and the main emphasis is placed on the analysis of how it is arranged inside.
Disclaimer
- Everything described below is, for the most part, saving on nanoseconds, and in practice it will not give anything but time lost on such a micro-optimization. This is especially true of “optimizations” of compile time.
- I will cut the code and output to the maximum, leaving only the essence.
- When writing this article I used PHP 7.2.
Necessary introductory
The string in double quotes
at the compilation stage is handled a little differently than the string in single quotes.
Single quotes will be resolved like this:
')
statement -> expr -> scalar -> dereferencable_scalar -> T_CONSTANT_ENCAPSED_STRING
Double so:
statement -> expr -> scalar -> '"' encaps_list '"' -> , ,
In the articles on PHP micro-optimizations, there is often advice not to use
print , since it is slower than
echo . Let's see how they understand.
Parsing
echo :
statement -> T_ECHO echo_expr_list -> echo_expr_list -> echo_expr -> expr
Parsing
print :
statement -> expr -> T_PRINT expr -> expr ( )
Those. In general, yes,
echo is detected a step earlier and this step, it should be noted, is rather heavy.
So that in the course of the article once again do not focus attention, we’ll keep in mind that at the compilation stage, double quotes are lost by single quotes, and
print loses
echo . Also let's not forget that it is, in the worst case, about nanoseconds.
Well, so as not to get up twice. Here are the diff functions compiling
print and
echo :
1 - void zend_compile_print(znode *result, zend_ast *ast) 1 + void zend_compile_echo(zend_ast *ast) 2 2 { 3 3 zend_op *opline; 4 4 zend_ast *expr_ast = ast->child[0]; 5 5 6 6 znode expr_node; 7 7 zend_compile_expr(&expr_node, expr_ast); 8 8 9 9 opline = zend_emit_op(NULL, ZEND_ECHO, &expr_node, NULL); 10 - opline->extended_value = 1; 11 - 12 - result->op_type = IS_CONST; 13 - ZVAL_LONG(&result->u.constant, 1); 10 + opline->extended_value = 0; 14 11 }
Well, you understand - they are identical in functionality, but
print additionally returns a constant equal to 1. I think on this topic with
print you can close and forget about it forever.
Simple string, no frills
Strings
echo 'Some string';
and
echo "Some string";
will be split almost identically into 2 (disklimer p2) tokens.
T_ECHO: echo T_ENCAPSED_AND_WHITESPACE/T_CONSTANT_ENCAPSED_STRING: "Some string"
And for single quotes, there will always be T_CONSTANT_ENCAPSED_STRING, and for double quotes, it will always be as. If there is a space in the string, then T_ENCAPSED_AND_WHITESPACE.
Opcodes will be simple to ugliness and absolutely identical:
line
findings
If you want to save a couple of processor cycles at compile time, then, for constant strings, use single quotes.
Dynamic string
There are 4 options.
echo "Hello $name! Have a nice day!"; echo 'Hello '.$name.'! Have a nice day!'; echo 'Hello ', $name, '! Have a nice day!'; printf ('Hello %s! Have a nice day!', $name);
For the first option:
T_ECHO: echo T_ENCAPSED_AND_WHITESPACE: Hello T_VARIABLE: $name T_ENCAPSED_AND_WHITESPACE: ! Have a nice day!
For the second (for the third as well, but instead of points there will be commas):
T_ECHO: echo T_CONSTANT_ENCAPSED_STRING: 'Hello ' string: . T_VARIABLE: $name string: . T_CONSTANT_ENCAPSED_STRING: '! Have a nice day!'
For the fourth:
T_STRING: printf T_CONSTANT_ENCAPSED_STRING: 'Hello %s! Have a nice day!' string: , T_VARIABLE: $name
But with opcodes everything will be much more interesting.
The first:
echo "Hello $name! Have a nice day!"; line
Second:
echo 'Hello '.$name.'! Have a nice day!'; line
Third:
echo 'Hello ', $name, '! Have a nice day!'; line
Fourth:
printf ('Hello %s! Have a nice day!', $name); line
Common sense dictates that the option with `printf` will lose in speed to the first three (especially since at the end there is the same ECHO), so we’ll leave it for tasks where you need formatting and we’ll not recall any more in this article.
It would seem that the third option is the fastest - to print successively three lines without concatenations, strange ROPEs, and creating additional variables. But not everything is so simple. The printing function in PHP is of course not Rocket Science, but also by no means a banal C-shny
fputs . Who
cares - the ball is unraveled starting with
php_output_write in the file
main / output.c .
CONCAT. Everything is simple - we convert, if necessary, the arguments into strings and create a new
zend_string using fast
memcpy . The only drawback is that with a long chain of concatenations for each operation, new lines will be created by moving the same baytik from place to place.
But with ROPE_INIT, ROPE_ADD and ROPE_END everything is much more interesting. We follow hands:
- ROPE_INIT (ext = 3, return = ~ 3, operands = 'Hello +')
Allocate the “rope” of three slots (ext), put the string 'Hello +' (operands) in slot 0 and return the temporary variable ~ 3 (return) containing the “rope”. - ROPE_ADD (ext = 1, return = ~ 3, operands = ~ 3,! 0)
We put in the slot 1 (ext) “rope” ~ 3 (operands) the string 'Vasya' obtained from the variable! 0 (operands) and return the “rope” ~ 3 (return). - ROPE_END (ext = 2, return = ~ 2, operands = ~ 3, '% 21 + Have + a + nice + day% 21')
We place the line '% 21 + Have + a + nice + day% 21' (operands) in slot 2 (ext), then create a zend_string of the required size and copy all the “rope” slots into it with the same memcpy .
We should also note that in the case of constants and temporary variables, references to the data will be placed in the slots, and there will be no extra copying.
In my opinion, quite elegant. :)
Let's celebrate. Let's take
zend_vm_execute.h file as source data (
imkho it will be fair) for 71 thousand lines and print it in different ways in 100 passes, dropping the minimum and maximum (each measurement started 10 times, choosing the most common option):
<?php $file = explode("\n", file_get_contents("C:\projects\C\php-src\Zend\zend_vm_execute.h")); $out = []; for ($c = 0; $c < 100; $c++) { $start = microtime(true); ob_start(); $i = 0; foreach ($file as $line) { $i++;
What we measure | Average time in seconds |
---|
"Rope" | 0.0129 |
Multiple echo | 0.0135 |
Concatenation | 0.0158 |
printf , for completeness | 0.0245 |
findings
- For strings with simple substitution, suddenly, double quotes are more optimal than single quotes with concatenation. And the longer the lines are used - the greater the gain.
- Arguments separated by commas ... There are many nuances. Measurement is faster than concatenation and slower than the “rope”, but too many “variables” associated with I / O.
Conclusion
It is difficult for me to think of a situation when there may be a need for such micro-optimizations. When choosing one or another approach, it is more reasonable to be guided by other principles - for example, readability of the code or the coding style adopted by your company.
As for me personally, I don’t like the approach with concatenations because of the arched view, although in some cases it can be justified.
PS If this kind of analysis is interesting - let me know - there is a lot more there, far from always unambiguous and obvious: an array of VS objects, foreach VS while VS for, your option ... :)
A small explanation of the reading of comments
HEREDOC syntax and “complex strings” (where the variables in curly brackets are inside) are the same strings in double quotes and are compiled in exactly the same way.
PHP jumble with HTML like this:
<?php $name = 'Vasya';?>Hello <?=$name?>! Have a nice day!
It's just 3
echo in a row.