将字符串添加到变量时,PHP的foreach性能

时间:2011-10-13 13:47:02

标签: php performance variables foreach variable-assignment

如下所示:

        foreach($data as $r=>$d)
          {
            $return = $return. "<tr>
            <td>
            ".$d["client_id"]."
            </td>
        ......
            <td>
                ".$d["date_stamp"]."

            </td>
            </tr>";
            }
          } 

这会使我的数据超过2秒!处理,但是如果我执行以下操作:

      foreach($data as $r=>$d)
        {
          $now= "<tr>
          <td>
          ".$d["client_id"]."
          </td>
      ......
          <td>
              ".$d["date_stamp"]."

          </td>
          </tr>";
          $return = $return.$now;
        } 

只需0.2秒......

好吧,好吧,你说“很好,使用第二种方法”,我肯定会这样做,但对我来说这是一个谜。为什么这两种方法之间存在如此巨大的性能差异?任何想法wellcome..thanks

添加测试用例:

   //////////function to get time
    function parsemicrotime(){
       list($usec, $sec) = explode(" ",microtime());
       return ((float)$usec + (float)$sec);
       }

    ////////////define test array
    $a = array();
    for($i = 0; $i < 5000; $i ++)//generate 5k rows
      {
        for($k=0; $k<5;$k++)//lets have just 6 columns
          {
            $a[$i]["column_".$k] = 'test string '.$i.' / '.$k.' - note that the size of the $output string makes a huge difference ';
          }
      }

    ///////////////first test
    $time_start = parsemicrotime();
        $output = '';
        foreach($a as $row=>$columns)
          {
            $output = $output ."
            <tr>
              <td>".$columns["test_0"]. "</td>
              <td>" .$columns["test_1"]. "</td>
              <td>" .$columns["test_2"]. "</td>
              <td>" .$columns["test_3"]. "</td>
              <td>" .$columns["test_4"]. "</td>
              <td>" .$columns["test_5"]. "</td>
            </tr>";
          }
    $approach_1_result = parsemicrotime()-$time_start;


    /////////////second test
    $time_start2 = parsemicrotime();
        $output2 = '';
        foreach($a as $row2=>$columns2)
          {
            $now2= "
            <tr>
              <td>".$columns["test_0"]. "</td>
              <td>" .$columns["test_1"]. "</td>
              <td>" .$columns["test_2"]. "</td>
              <td>" .$columns["test_3"]. "</td>
              <td>" .$columns["test_4"]. "</td>
              <td>" .$columns["test_5"]. "</td>
            </tr>";
            $output2 = $output2 .$now2;
          }
    $approach_2_result = parsemicrotime()-$time_start2;


    /////////////third test
    $time_start3 = parsemicrotime();
    ob_start();
        $output3 = '';
        foreach($a as $row3=>$columns3)
          {
            echo "
            <tr>
              <td>".$columns["test_0"]. "</td>
              <td>" .$columns["test_1"]. "</td>
              <td>" .$columns["test_2"]. "</td>
              <td>" .$columns["test_3"]. "</td>
              <td>" .$columns["test_4"]. "</td>
              <td>" .$columns["test_5"]. "</td>
            </tr>";
          }
    $output3 = ob_get_clean();
    $approach_3_result = parsemicrotime()-$time_start3;


    die("first test:".$approach_1_result."<br>second test:".$approach_2_result."<br>third test:".$approach_3_result);

2 个答案:

答案 0 :(得分:2)

我使用生成的数组进行了一些类似的实验:

$a = [];
for($i = 0; $i < 10000; $i ++)
  $a[] = $i;

为时间计时我只是在执行前存储microtime,并在执行后从microtime中减去它。我执行了10次代码并取平均值。

首先,我尝试了类似于您的第一种方法:

$output = '';
foreach($a as $k => $v)
  $output = $output . "some static text" . $v . "some other text";

这记录了~3s的疯狂时间!然后,我使用单引号尝试了相同的代码,得到了相同的结果。

然后我将连接线更改为:

$output .= 'some static text' . $v . 'some other text';

这导致时间~0.007s,快〜429倍!

最后我将代码更改为:

$output = '';
ob_start();
foreach($a as $k => $v)
  echo 'some static text' . $v . 'some other text';
$output = ob_get_clean();

得分略微慢于.=方法(仍为~0.007)。

免责声明:以下所有内容都只是我对时代原因的直觉。

现在,我不是PHP内部的专家,但我猜测第一种方法速度慢得多的原因是因为它必须创建一个新字符串并复制旧字符串(它正在慢慢地制作它方式到~350,000个字符的最终大小)10,000次,并且复制通常是一个效率相当低的操作。但是,.=方法只是扩展了原始字符串,从而避免了复制操作。缓冲方法类似,可能是因为写入输出流的成本与扩展变量类似,ob_startob_get_clean增加了边际开销。

答案 1 :(得分:0)

两种方法之间存在差异,但这是惊人的,应该会产生如此大的差异。

每次连接两个字符串时,PHP都必须分配一个新的内存块,大小足以容纳新字符串。对于非常大的字符串,它必须找到更大的连续内存块。因为每次它必须要大一点,它不能重复使用前面的块(它们很小)。因此,如果你的字符串增长,找到另一个内存块并复制字符串会很慢。

  1. 在第一个示例中,您在一个循环中有很多.个操作。每.个已经很大的字符串变大。

  2. 在第二个示例中,您将收集循环的所有.个操作。变量$ now现在相对较小,因此这些连接速度很快。每个循环只需要找一个大内存块。

  3. 正如已经提到的,我有点惊讶,它应该产生如此大的差异,但取决于迭代次数,它是可能的。