Lot of my coding time is spent to improve performance and find bugs, PHP give much freedom for coding and this often evolve in bad habits, therefore need pay attention for write fast and efficient code.
Write fast code for me means that some times I must do a choice between readability, short code, simplicity and performance.
As example I can report my experience with the recursion. Recursion, informally when a function calls itself, can be used for solve problems and create algorithms with a few lines of code.
This is good, but with PHP could be a bad thing because call a user-defined function is expensive in term of time.
The problem
Write a dependency injection resolver. I need a method that create a map of the dependencies and store it inside an array.
First I used the recursive approach as fastest to implement. When I did profiling, i saw that most of the execution time was used to resolve the class dependencies. The recursion is too expensive and need to rewrite the code.
Solution
After a fast search on internet, I decided to use iterative approach that compared to recursive need more code but is faster.
As recursive:
<?php
/**
* Create a dependencies map for a class.
*
* @param int $level Depth of dependency
* @param string $class Class to build dependencies
*/
private function buildTree(int $level, string $class)
{
//initialize array
$this->tree[$level][$class] = [];
//get parameter from constructor
$param = (new \ReflectionClass($class))->getConstructor()->getParameters();
//loop parameter
foreach ($param as $key => $value) {
//if there is parameter with callable type
if ($value->hasType() === true && class_exists((string) $value->getType())) {
//store dependency
$this->tree[$level][$class][] = $value->getClass()->name;
//call recursive
$this->buildTree($level + 1, $value->getClass()->name);
}
}
}
And as iterative:
<?php
/**
* Create a dependencies map for a class.
*
* @param string $class Class to build dependencies
*/
private function buildTree(string $class)
{
//set start level
$level = 0;
//create stack
$stack = new \SplStack();
//iterate
while (true) {
//initialize array if not already initialized
if (!isset($this->tree[$level][$class])) {
$this->tree[$level][$class] = [];
}
//get parameter from constructor
$parameters = (new \ReflectionClass($class))->getConstructor()->getParameters();
//loop parameter
foreach ($parameters as $param) {
//check if argument is already stored
$notAlreadyStored = !in_array($param, $this->tree[$level][$class]);
//if there is parameter with callable type
if (class_exists((string) $param->getType()) && $notAlreadyStored) {
//push values in stack for simulate later recursive function
$stack->push([$level, $class]);
//store dependency
$this->tree[$level][$class][] = $param;
//update values for simulate recursive function
$level++;
$class = (is_object($param->getClass())) ?
$param->getClass()->name : null;
//return to main while
continue 2;
}
if ($notAlreadyStored) {
//store dependency
$this->tree[$level][$class][] = $param;
}
}
//if stack is empty break while end exit from function
if ($stack->count() === 0) {
break;
}
//get last value pushed into stack;
list($level, $class) = $stack->pop();
}
}
As you can see, at first look, iterative code is longer and more complicated than recursive. In details, main differences are only a while loop and a LIFO data structure for simulate the call stack.
Benchmark
Returning to profiling of the code and to performance, differences in execution time between previous pieces of code are very significative. The speed gap, grows with the number of function calls.
I do a little benchmark using php microtime() and this is the result:
Calls | Execution Time Iteractive | Execution Time Recursive | Fastest |
---|---|---|---|
1 | 100% | 30% - 50% | Recursive |
10 | 100% | 180% - 210% | Iteractive |
100 | 100% | 300% - 450% | Iteractive |
1000 | 100% | 700% - 1100% | Iteractive |
10000 | 100% | 600% - 700% | Iteractive |
In benchmark I have simulated a class that depend by other classes. Example Class A depend from B, B from C, C from D, D from E etc. Total nine nested classes.
Benchmark was executed with Virtual Box, Ubuntu Server 16.04.2, Apache/2.4.18, php 7.1.7 with mod-fpm. Virtual machine: Intel(R) Core(TM)2 Extreme CPU X9100, 2 Cpu max 40% and 512Mb of Ram, SSD. Benchmark ran 10 times and recursion values are averaged.
Conclusion
In conclusion, when need to do a choice between "how the code is written" and performance, for me it's best have more execution speed and less code simplicity /readability. Can help in this case, leave more detailled comments (what and because, not only because) for not forget what the code do.
Of course, the previous consideration does not mean that you do not have to follow the good code practices.
Top comments (2)
Out of curiosity, are there any style linters for PHP? I have been looking for one with no luck. I know that has helped JavaScript clean up some of it's style problem.
Hi for my project, I use StyleCI that automatically scan new github commits. Also you can check the PSR2 Coding style guide php-fig.org/psr/psr-2/ and php-cs-fixer, a command line tool that correct the code to comply with PSR2