Performance Improvement: Alternative for array_flip function
Is there any way I can avoid using array_flip to optimize performance. I am doing a select
statement from database, preparing the query and executing it and storing data as an associative array in $resultCollection
and than I have array op
and for each el开发者_StackOverflowement in $resultCollection
am storing its outputId
in op[]
as evident from the code.
I have explained code and so my question is how can I achieve an similar alternative for array_flip with using array_flip as I want to improve performance.
$resultCollection = $statement->fetchAll(PDO::FETCH_ASSOC);
$op = array();
//Looping through result collection and storing unicaOfferId into op array.
foreach ($resultCollection as $output)
{
$op[] = $output['outputId'];
}
//Here op array has key as 0, 1, 2...and value as id {which I am interested in}
//Flip op array to get offer ids as key
$op = array_flip($op);
//Doing a flip to get id as key.
foreach ($ft as $Id => $Off)
{
$ft[$Id]['is_set'] = isset($op[$Id]);
}
I'm adding this answer because you mentioned in the title "Performance Improvement".
TL;DR
You should continue with array_flip()
. In the terms of performance compared to a foreach
loop, it does things much faster, especially on large arrays (maybe because of its native nature).
Let's test
The result of what I tested (PHP 8.1.5):
Testing an array with 10 elements:
Average time for array_flip(): 2.0E-5
Average time for foreach: 3.0E-5
How much array_flip() is faster: 78%
Testing an array with 100 elements:
Average time for array_flip(): 0.00011
Average time for foreach: 0.00025
How much array_flip() is faster: 137%
Testing an array with 1000 elements:
Average time for array_flip(): 0.00108
Average time for foreach: 0.00272
How much array_flip() is faster: 150%
Testing an array with 10000 elements:
Average time for array_flip(): 0.00918
Average time for foreach: 0.02429
How much array_flip() is faster: 164%
Testing an array with 100000 elements:
Average time for array_flip(): 0.10644
Average time for foreach: 0.2439
How much array_flip() is faster: 129%
Testing an array with 1000000 elements:
Average time for array_flip(): 0.93669
Average time for foreach: 2.47616
How much array_flip() is faster: 164%
Did you see that? Approximately, array_flip()
is 2.5 times faster. In addition, it seems the performance of array_flip()
increases slightly when the array becomes bigger. So, forget foreach
. Even with the help of JIT? Read on...
Could JIT help things out?
Yes! Let's see (PHP 8.1.5, with tracing JIT, opcache.jit_buffer_size = 100M
):
Testing an array with 10 elements:
Average time for array_flip(): 3.0E-5
Average time for foreach: 3.0E-5
How much array_flip() is faster: -3%
Testing an array with 100 elements:
Average time for array_flip(): 0.00011
Average time for foreach: 0.00011
How much array_flip() is faster: 0%
Testing an array with 1000 elements:
Average time for array_flip(): 0.00098
Average time for foreach: 0.00101
How much array_flip() is faster: 3%
Testing an array with 10000 elements:
Average time for array_flip(): 0.00955
Average time for foreach: 0.00985
How much array_flip() is faster: 3%
Testing an array with 100000 elements:
Average time for array_flip(): 0.09958
Average time for foreach: 0.10182
How much array_flip() is faster: 2%
Testing an array with 1000000 elements:
Average time for array_flip(): 0.90585
Average time for foreach: 1.03467
How much array_flip() is faster: 14%
Interesting! Notice that:
It may seem obvious for some, but tracing JIT starts becoming more useful when you start calling a particular function in a run more and more (try playing with the
$iterationsCount
in the code below). Also, in most cases you could ignore the performance of a single function call.JIT made everything faster. For
array_flip()
, it was up to 5-10% performance increase. Forforeach
, it was magical: about 150% (i.e. 2.5 times faster)!As a result of the previous note, the difference between these two methods reduced drastically. JIT is great and is improving!
Although having a JIT-enabled environment is great, internal functions mostly are faster, independent from your configurations. So, use array_flip()
.
Talk is cheap...
This is what I tested (feel free to configure variables yourself):
// The ratio of array size being increased
$arraySizeBase = 10;
// Limits of the exponention of the array size
$arraySizePowerMin = 1;
$arraySizePowerMax = 6;
// Number of tests being run within one time capture
$iterationsCount = 100;
// Number of time capture repeats
$repeatCount = 20;
// Precision of rounded result
$precision = 5;
// Array values limits
$minVal = 0;
$maxVal = 10000;
function printTime(callable $x, string $title)
{
global $iterationsCount, $repeatCount, $precision;
$tests = [];
for ($i = 0; $i < $repeatCount; $i++) {
$startTime = microtime(true);
for ($j = 0; $j < $iterationsCount; $j++) {
$x();
}
$tests[] = microtime(true) - $startTime;
}
$averageTime = array_sum($tests) / $repeatCount;
echo " Average time for $title: ", round($averageTime, $precision), PHP_EOL;
// To be used to calculate ratio
return $averageTime;
}
$arraySizeMin = $arraySizeBase ** $arraySizePowerMin;
$arraySizeMax = $arraySizeBase ** $arraySizePowerMax;
for ($i = $arraySizeMin; $i <= $arraySizeMax; $i *= $arraySizeBase) {
// Filling the array with some random stuff
echo "Testing an array with $i elements:", PHP_EOL;
$array = array_fill(0, $i - 1, random_int($minVal, $maxVal));
$arrayFlipTime = printTime(function () use ($array) {
$flippedArray = array_flip($array);
// Don't be crazy, clean RAM
$flippedArray = null;
}, "array_flip()");
$foreachTime = printTime(function () use ($array) {
$flippedArray = [];
foreach ($array as $key => $value) {
$flippedArray[$value] = $key;
}
// Don't be crazy, clean RAM
$flippedArray = null;
}, "foreach");
// Print a ratio in percentage
echo " How much array_flip() is faster: ",
floor(($foreachTime / $arrayFlipTime) * 100) - 100, "%",
PHP_EOL;
echo PHP_EOL;
}
You should be able to use the key from the foreach for indexes and build the array pre-flipped like this.
foreach ($resultCollection as $key => $output) {
$op[ $output['outputId'] ] = $key;
}
Well, since it doesn't look like you care about the value, bur rather just the O(1) lookup speed of the keys, I'd build the $op
that way the first time
foreach ($resultCollection as $output)
{
$op[$output['outputId']] = null;
}
Or you can look into (Seems like this is 0(n), so not faster)in_array()
, I don't know how its speed compares.
EDIT
If you wanted to create the first time around what you were getting after array_flip()
, do it this way.
$i = 0;
foreach ($resultCollection as $output)
{
$op[$output['outputId']] = $i++;
}
But after your comments, I'm still not sure if I really "get" what you're after.
精彩评论