开发者

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. For foreach, 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 in_array(), I don't know how its speed compares. (Seems like this is 0(n), so not faster)

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜