【问题标题】:Exploding an array within a foreach loop parameter在 foreach 循环参数中分解数组
【发布时间】:2011-08-17 05:42:00
【问题描述】:
foreach(explode(',' $foo) as $bar) { ... }

$test = explode(',' $foo);
foreach($test as $bar) { ... }

在第一个示例中,它是 explode 每次迭代的 $foo 字符串还是 PHP 将其保存在内存中并在其自己的临时变量中爆炸?从效率的角度来看,创建额外变量 $test 是否有意义,或者两者几乎相等?

【问题讨论】:

  • @tomalak-geretkal 谢谢,这是一个控制结构。

标签: php performance foreach


【解决方案1】:

效率在什么意义上?内存管理还是处理器?处理器不会对内存产生影响 - 您可以随时使用 $foo = explode(',', $foo)

【讨论】:

    【解决方案2】:

    在第一种情况下,PHP 将其分解一次并保存在内存中。

    创建不同变量或其他方式的影响可以忽略不计。 PHP解释器需要维护一个指向下一项的位置的指针,无论它们是否是用户定义的。

    【讨论】:

      【解决方案3】:

      从内存的角度来看,它不会产生影响,因为 PHP 使用 copy on write concept

      除此之外,我个人会选择第一个选项 - 它少了一行,但可读性不低(恕我直言!)。

      【讨论】:

      • @Ryan:糟糕,我的意思是第一个;)
      【解决方案4】:

      我可以做出有根据的猜测,但让我们试试看

      我认为有三种主要方法可以解决这个问题。

      1. 在进入循环之前分解和赋值
      2. 在循环内爆炸,没有赋值
      3. 字符串分词

      我的假设:

      1. 可能由于分配而消耗更多内存
      2. 可能与 #1 或 #3 相同,不确定是哪个
      3. 可能更快、更小内存占用

      方法

      这是我的测试脚本:

      <?php
      
      ini_set('memory_limit', '1024M');
      
      $listStr = 'text';
      $listStr .= str_repeat(',text', 9999999);
      
      $timeStart = microtime(true);
      
      /*****
       * {INSERT LOOP HERE}
       */
      
      $timeEnd = microtime(true);
      $timeElapsed = $timeEnd - $timeStart;
      
      printf("Memory used: %s kB\n", memory_get_peak_usage()/1024);
      printf("Total time: %s s\n", $timeElapsed);
      

      以下是三个版本:

      1)

      // explode separately 
      $arr = explode(',', $listStr);
      foreach ($arr as $val) {}
      

      2)

      // explode inline-ly 
      foreach (explode(',', $listStr) as $val) {}
      

      3)

      // tokenize
      $tok = strtok($listStr, ',');
      while ($tok = strtok(',')) {}
      

      结果

      结论

      看起来有些假设被推翻了。你不爱科学吗? :-)

      • 总体而言,这些方法中的任何一种都足够快,可以生成“合理大小”(几百或几千)的列表。
      • 如果您正在迭代巨大的东西,时间差异相对较小,但内存使用量可能会相差一个数量级!
      • 当您 explode() 在没有预先分配的情况下内联时,由于某种原因,它会慢一些。
      • 令人惊讶的是,标记化比显式迭代声明的数组要一点。在如此小的规模上工作,我相信这是由于每次迭代都对strtok() 进行函数调用的调用堆栈开销。详情请参阅下文。

      就函数调用的数量而言,explode()ing 确实在标记化方面名列前茅。 O(1) vs O(n)

      我在运行方法 1) 的图表中添加了一个奖励,并在循环中调用了一个函数。我使用了strlen($val),认为这将是一个相对相似的执行时间。这是有争议的,但我只是想提出一个一般性的观点。 (我只运行了strlen($val) 并忽略了它的输出。我确实没有将它分配给任何东西,因为分配会增加时间成本。)

      // explode separately 
      $arr = explode(',', $listStr);
      foreach ($arr as $val) {strlen($val);}
      

      从结果表中可以看出,它成为三种方法中最慢的方法。

      最后的想法

      这很有趣,但我的建议是做任何你认为最易读/可维护的事情。只有当你真的在处理一个非常大的数据集时,你才应该担心这些微优化。

      【讨论】:

      • 很明显$x=...;foreach($x 将比foreach(... 使用更多的内存在循环之后。仅仅因为 $x 变量它仍然完好无损。但这无关紧要。该变量将在return 上销毁,或者,如果内存很关键,则可以只是unset有趣的是峰值内存使用,因为只有这对 memory_limit 很重要。在这里,我非常有信心它会为您提供两种 foreach 变体非常相似的结果。请记住:如果您正在“基准测试”内存,您通常希望使用memory_get_peak_usage,而不是...
      • ...memory_get_usage。 #2 和 #3 在内存中相同的事实也源于您只是在循环后测量内存使用情况。 strtok 的峰值内存可能会更小。这与 PHP 在循环中将 explode 转换为标记化无关。关于你的最后一个例子:函数调用在 PHP 中很昂贵;)你 strlen($val); 将涉及执行五个操作码。
      • 谢谢,对于这些结果,请务必提及 versionOS
      猜你喜欢
      • 2022-07-30
      • 2018-04-14
      • 1970-01-01
      • 2017-12-25
      • 2014-05-28
      • 2022-09-27
      • 1970-01-01
      • 2014-02-13
      • 1970-01-01
      相关资源
      最近更新 更多