DEV Community

Sebastian Rapetti
Sebastian Rapetti

Posted on

PHP code, about readability and performance

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);
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

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();
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

Latest comments (2)

Collapse
 
kellyjandrews profile image
Kelly Andrews

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.

Collapse
 
sebastianr1982 profile image
Sebastian Rapetti

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