Speed comparison of member variable fetching for PHP code optimization

  • 2021-01-19 22:02:40
  • OfStack

There are four code examples below. How do you think they create objects and get member variables in order of speed?

1: Set the member variable to public, assign the value to the member variable through the assignment operation, get the variable directly


<?php
class Foo {
    public $id;
}
$data = new Foo;
$data->id = 10;
echo $data->id;
?>

2: Set the member variable to public, set the value of the member variable through the constructor, and get the variable directly

<?php
class Foo2 {
 public $id;
 public function __construct($id) {
  $this->id = $id;
 }
}
$data = new Foo2(10);
echo $data->id;
?>

3: Set the member variable to protected, set the value of the member variable through the constructor, and get the variable through the magic method

<?php
class Foo3 {
 protected $id;
 public function __construct($id) {
  $this->id = $id;
 }
 public function getId() {
  return $this->id;
 }
}
$data = new Foo3(10);
echo $data->getId();
?>

4: Set the member variable to protected, set the value of the member variable through the constructor, and get the variable through the member method
< ?php
class Foo4 {
protected $id;
public function __construct($id) {
$this- > id = $id;
}

public function __get($key) {
return $this- > id;
}
}
$data = new Foo4(10);
echo $data- > id;
? >
Sort by execution speed: 1243
Let's look at opcode first:
1:


1  ZEND_FETCH_CLASS 4  :4  'Foo'
2  NEW         $5 :4
3  DO_FCALL_BY_NAME   0          
4  ASSIGN         !0, $5
5  ZEND_ASSIGN_OBJ   !0, 'id'
6  ZEND_OP_DATA    10
7  FETCH_OBJ_R   $9 !0, 'id'
8  ECHO            $9

2:

1  ZEND_FETCH_CLASS 4  :10 'Foo2'
2  NEW               $11 :10
3  SEND_VAL           10
4  DO_FCALL_BY_NAME  1 
5  ASSIGN        !1, $11
6  FETCH_OBJ_R   $14 !1, 'id'
7  ECHO            $14

3:

1  ZEND_FETCH_CLASS 4  :15 'Foo3'
2  NEW            $16 :15
3  SEND_VAL        10
4  DO_FCALL_BY_NAME   1          
5  ASSIGN         !2, $16
6  ZEND_INIT_METHOD_CALL !2, 'getId'
7  DO_FCALL_BY_NAME  0  $20     
8  ECHO           $20

4:

1  ZEND_FETCH_CLASS 4  :21 'Foo4'
2  NEW            $22 :21
3  END_VAL         10
4  DO_FCALL_BY_NAME  1          
5  ASSIGN           !3, $22
6  FETCH_OBJ_R    $25 !3, 'id'
7   ECHO      $25


According to opcode above, what can be found by referring to its corresponding opcode implementation in zend_vm_execute.h file?

1. The process of creating objects in the PHP kernel is divided into three steps:

ZEND_FETCH_CLASS gets the variable that stores the class according to the class name, which is implemented as a lookup operation of hashtalbe EG(class_table)
NEW initializes the object to EX(call)- > fbc points to the constructor pointer.
The constructor is called, just like any other function call, by calling zend_do_fcall_common_helper_SPEC

2. Magic method invocations are conditional triggers, not direct calls, such as the member variable id in our example

(zend_std_read_property), the steps are:
Get the properties of the object, if there are, go to Step 2; If there are no related attributes, go to Step 3
Find out from the properties of the object whether the attribute corresponding to the name exists. If it does, return the result. If it does not, go to step 3
If the __get magic method exists, call this method to get the variable. If it does not exist, report an error
Back to the sorting problem:

1. What's the difference between # 1 and # 2?

The second opcode is less than the first, but slower than the first, because the constructor has more arguments, and there is an extra argument processing opcode. Parameter processing is a time-consuming operation, when we are doing code optimization, some unnecessary parameters can be removed; When a function has multiple arguments, consider wrapping it in an array and passing it in.

2. Why is the third slowest?

Because its parameters are essentially a call to an object member method, the cost of calling the method is higher than the cost of fetching the variable

3. Why is the fourth faster than the third?

Because the fourth operation essentially gets the variable, it implements the magic method call internally, which is more efficient than the user-defined method call. So don't reinvent the wheel when you have a few methods implemented by the PHP kernel to call.
4. Why is the fourth one slower than the second?
Because in the process of PHP object access variable, when the member variable in the class definition is not in, will call PHP unique magic method __get, more than one magic method call.

Summary 1:

1. Use PHP built-in functions
2. Not everything has to be object-oriented (OOP). Object-orientation tends to be expensive, with each method and object call consuming a lot of memory.
3. Use magic techniques sparingly -- don't use frames unless it's absolutely necessary, as there are plenty of tricks used with frames.
4. In performance-first scenarios, using member variables is a good idea when you need to use OOP.
5. If you can use PHP syntax, don't use functions. If you can use built-in functions, don't write them yourself


Related articles: