Identifying more possible optimizations

When optimizing an application, you will start by identifying the most time-consuming functions, especially along the application's critical path. As stated in a previous chapter, most of those functions will be I/O functions as these are always the most expensive operations for a computer to execute. Most of the time you will see the possibility to optimize loops and reduce the number of system calls, but you will soon realize that I/O operations remain costly no matter what optimizations you wish to bring to them. Sometimes, though, you might run into very slow PHP structures that can simply be replaced with faster ones, or you may realize that poorly designed code can easily be refactored to be less resource-hungry, such as when replacing a dynamic structure with a simpler static one.

Indeed, dynamic structures should be avoided unless absolutely necessary. We will now have a look at a very simple example. We will program the same functionality four times, but with three different approaches: functional and dynamic, functional and static, and finally, structural and static. Let's start with the functional and dynamic approach:

// chap3_dynamic_1.php 
 
$start = microtime(true); 
 
$x = 1; 
 
$data = []; 
 
$populateArray = function ($populateArray, $data, $x) { 
 
    $data[$x] = $x; 
 
    $x++; 
 
    return $x <= 1000 ? $populateArray($populateArray, $data, $x) : $data; 
 
}; 
 
$data = $populateArray($populateArray, $data, $x); 
 
$time = microtime(true) - $start; 
 
echo 'Time elapsed: ' . $time . PHP_EOL; 
 
echo memory_get_usage() . ' bytes' . PHP_EOL; 

This code creates an array with 1,000 elements by calling the same closure recursively. If we run this code, we get the following result:

Time elapsed and memory consumed when running the script programmed with the functional and dynamic approach

Let's have a look at the results of running this script using Blackfire.io:

The profiling report when running the script programmed with the functional and dynamic approach

Let's code the same functionality, but in a more static fashion with a classic named function instead:

// chap3_dynamic_2.php 
 
$start = microtime(true); 
 
$x = 1; 
 
$data = []; 
 
function populateArray(Array $data, $x) 
{ 
    $data[$x] = $x; 
 
    $x++; 
 
    return $x <= 1000 ? populateArray($data, $x) : $data; 
} 
 
$data = populateArray($data, $x); 
 
$time = microtime(true) - $start; 
 
echo 'Time elapsed: ' . $time . PHP_EOL; 
 
echo memory_get_usage() . ' bytes' . PHP_EOL; 

If we execute this version of our code, we obtain the following result:

Time elapsed and memory consumed when running the script programmed with the functional and static approach

Running the script with the Blackfire.io profiler yields these results:

The profiling report when running the script programmed with the functional and static approach

Finally, let's program this functionality again, but in a very structural and static way by using a for loop instead of tail-calling the function recursively:

// chap3_dynamic_3.php 
 
$start = microtime(true); 
 
$data = []; 
 
function populateArray(Array $data) 
{ 
    static $x = 1; 
 
    $data[$x] = $x; 
 
    $x++; 
 
    return $data; 
} 
 
for ($x = 1; $x <= 1000; $x++) { 
    $data = populateArray($data); 
} 
 
$time = microtime(true) - $start; 
 
echo 'Time elapsed: ' . $time . PHP_EOL; 
 
echo memory_get_usage() . ' bytes' . PHP_EOL; 

Here are the results after executing this latest version of the code:

Time elapsed and memory consumed when running the script programmed with the structural and static approach

Here are the results of profiling this version of the script with Blackfire.io:

The profiling report when running the script programmed with the structural and static approach

The results clearly show that the structural approach is the fastest. If we now go a little further down the structural route, with only a hint of functional programming, and try using a generator to create the array iteratively, we should not be surprised by the high performance results that we will get. Here is the last version of our code:

// chap3_dynamic_4.php

$start = microtime(true);

$data = [];

function populateArray()
{
for ($i = 1; $i <= 1000; $i++) {

yield $i => $i;

}

return;
}

foreach (populateArray() as $key => $value) {

$data[$key] = $value;

}

$time = microtime(true) - $start;

echo 'Time elapsed: ' . $time . PHP_EOL;

echo memory_get_usage() . ' bytes' . PHP_EOL;

This is the result when running the latest version of our code:

Time elapsed and memory consumed when running the script programmed with a very structural and static approach

Here are the results with Blackfire.io:

The profiling report when running the script programmed with a very structural and static approach

The results clearly show how this last version of our code really outperforms the other ones. Indeed, PHP is still a very structural language as its compiler still does not fully optimize tail-recursion calls and does take less time to complete the execution of a program if it is coded in a structural way. Does this mean that PHP will never be a functional language and that it is best to avoid programming in a functional way in PHP? The short answer is no. Also, does this mean that functional programming with PHP is a thing of the future only? Again, the answer is no. There are certain functional programming techniques that we can use immediately and that will help our scripts be more performant. Let's have a look at one in particular, which is memoization.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset