Soulriser
Soulriser

Reputation: 449

Does unsetting array values during iterating save on memory?

This is a simple programming question, coming from my lack of knowledge of how PHP handles array copying and unsetting during a foreach loop. It's like this, I have an array that comes to me from an outside source formatted in a way I want to change. A simple example would be:

$myData = array('Key1' => array('value1', 'value2'));

But what I want would be something like:

$myData = array([0] => array('MyKey' => array('Key1' => array('value1', 'value2'))));

So I take the first $myData and format it like the second $myData. I'm totally fine with my formatting algorithm. My question lies in finding a way to conserve memory since these arrays might get a little unwieldy. So, during my foreach loop I copy the current array value(s) into the new format, then I unset the value I'm working with from the original array. E.g.:

$formattedData = array();
foreach ($myData as $key => $val) {
    // do some formatting here, copy to $reformattedVal

    $formattedData[] = $reformattedVal;

    unset($myData[$key]);
}

Is the call to unset() a good idea here? I.e., does it conserve memory since I have copied the data and no longer need the original value? Or, does PHP automatically garbage collect the data since I don't reference it in any subsequent code?

The code runs fine, and so far my datasets have been too negligible in size to test for performance differences. I just don't know if I'm setting myself up for some weird bugs or CPU hits later on.

Thanks for any insights.
-sR

Upvotes: 11

Views: 7937

Answers (5)

Amos
Amos

Reputation: 161

I was running out of memory while processing lines of a text (xml) file within a loop. For anyone with a similar situation, this worked for me:

while($data = array_pop($xml_data)){
     //process $data
}

Upvotes: 6

Andy Lester
Andy Lester

Reputation: 93725

Please remember the rules of Optimization Club:

  1. The first rule of Optimization Club is, you do not Optimize.
  2. The second rule of Optimization Club is, you do not Optimize without measuring.
  3. If your app is running faster than the underlying transport protocol, the optimization is over.
  4. One factor at a time.
  5. No marketroids, no marketroid schedules.
  6. Testing will go on as long as it has to.
  7. If this is your first night at Optimization Club, you have to write a test case.

Rules #1 and #2 are especially relevant here. Unless you know that you need to optimize, and unless you have measured that need to optimize, then don't do it. Adding the unset will add a run-time hit and will make future programmers why you are doing it.

Leave it alone.

Upvotes: 3

Andy
Andy

Reputation: 17781

Use a reference to the variable in the foreach loop using the & operator. This avoids making a copy of the array in memory for foreach to iterate over.

edit: as pointed out by Artefacto unsetting the variable only decreases the number of references to the original variable, so the memory saved is only on pointers rather than the value of the variable. Bizarrely using a reference actually increases the total memory usage as presumably the value is copied to a new memory location instead of being referenced.

Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it.

Use memory_get_usage() to identify how much memory you are using.

There is a good write up on memory usage and allocation here.

This is useful test code to see memory allocation - try uncommenting the commented lines to see total memory usage in different scenarios.

echo memory_get_usage() . PHP_EOL;
$test = $testCopy = array();
$i = 0;
while ($i++ < 100000) {
    $test[] = $i;
}
echo memory_get_usage() . PHP_EOL;
foreach ($test as $k => $v) {
//foreach ($test as $k => &$v) {
    $testCopy[$k] = $v;
    //unset($test[$k]);
}
echo memory_get_usage() . PHP_EOL;

Upvotes: 4

Artefacto
Artefacto

Reputation: 97835

If at any point in the "formatting" you do something like:

$reformattedVal['a']['b'] = $myData[$key];

Then doing unset($myData[$key]); is irrelevant memory-wise because you are only decreasing the reference count of the variable, which now exists in two places (inside $myData[$key] and $reformattedVal['a']['b']). Actually, you save the memory of indexing the variable inside the original array, but that's almost nothing.

Upvotes: 2

John Parker
John Parker

Reputation: 54445

Unless you're accessing the element by reference unsetting will do nothing whatsoever, as you can't alter the array during within the iterator.

That said, it's generally considered bad practice to modify the collection you're iterating over - a better approach would be to break down the source array into smaller chunks (by only loading a portion of the source data at a time) and process these, unsetting each entire array "chunk" as you go.

Upvotes: 0

Related Questions