9

这是一个简单的编程问题,因为我不了解 PHP 如何在foreach循环期间处理数组复制和取消设置。就像这样,我有一个来自外部源的数组,它以我想要更改的方式格式化。一个简单的例子是:

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

但我想要的是这样的:

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

所以我采用第一个$myData并将其格式化为第二个$myData。我对我的格式化算法完全没问题。我的问题在于找到一种节省内存的方法,因为这些数组可能有点笨拙。因此,在我的foreach循环中,我将当前数组值复制到新格式中,然后从原始数组中取消设置我正在使用的值。例如:

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

    $formattedData[] = $reformattedVal;

    unset($myData[$key]);
}

这里呼吁unset()一个好主意吗?即,它是否节省内存,因为我已经复制了数据并且不再需要原始值?或者,PHP 是否会自动垃圾收集数据,因为我没有在任何后续代码中引用它?

代码运行良好,到目前为止,我的数据集的大小可以忽略不计,无法测试性能差异。我只是不知道我以后是否要为一些奇怪的错误或 CPU 命中做好准备。

感谢您的任何见解。
-sR

4

5 回答 5

4

请记住优化俱乐部的规则

  1. 优化俱乐部的第一条规则是,你不要优化。
  2. 优化俱乐部的第二条规则是,没有衡量就不要优化。
  3. 如果您的应用程序比底层传输协议运行得更快,那么优化就结束了。
  4. 一次一个因素。
  5. 没有市场机器人,没有市场机器人时间表。
  6. 只要需要,测试就会继续进行。
  7. 如果这是您在优化俱乐部的第一个晚上,您必须编写一个测试用例。

规则#1 和#2 在这里特别重要。除非你知道你需要优化,除非你已经衡量了需要优化,否则不要这样做。添加 unset 将增加运行时命中,并使未来的程序员为什么要这样做。

不要管它。

于 2011-01-12T22:52:39.530 回答
4

foreach使用运算符在循环中使用对变量的引用&。这避免了在内存中复制数组以foreach进行迭代。

编辑:正如Artefacto指出的那样,取消设置变量只会减少对原始变量的引用次数,因此节省的内存仅在指针上而不是变量的值上。奇怪的是,使用引用实际上会增加总内存使用量,因为推测该值被复制到新的内存位置而不是被引用。

除非引用了数组,否则 foreach 对指定数组的副本而不是数组本身进行操作。foreach 对数组指针有一些副作用。不要在 foreach 期间或之后依赖数组指针而不重置它。

用于memory_get_usage()确定您正在使用多少内存。

这里有一篇很好的关于内存使用和分配的文章

这是查看内存分配的有用测试代码 - 尝试取消注释注释行以查看不同场景中的总内存使用情况。

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;
于 2011-01-12T21:23:05.453 回答
3

我在循环中处理文本(xml)文件的行时内存不足。对于任何有类似情况的人,这对我有用:

while($data = array_pop($xml_data)){
     //process $data
}
于 2013-02-01T16:48:09.743 回答
2

如果在“格式化”中的任何时候您执行以下操作:

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

然后做unset($myData[$key]);与内存无关,因为你只是减少变量的引用计数,它现在存在于两个地方(内部$myData[$key]$reformattedVal['a']['b'])。实际上,您节省了在原始数组中索引变量的内存,但这几乎没有。

于 2011-01-12T21:23:59.003 回答
0

除非您通过引用访问元素,否则 unsetting 将不执行任何操作,因为您无法在迭代器中更改数组。

也就是说,修改你正在迭代的集合通常被认为是不好的做法——更好的方法是将源数组分解成更小的块(一次只加载一部分源数据)并处理这些,随时取消设置每个整个数组“块”。

于 2011-01-12T21:22:05.090 回答