PHP foreach производительность при добавлении строки в переменную - PullRequest
1 голос
/ 13 октября 2011

Иметь foreach следующим образом:

        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 ]

2 голосов
/ 13 октября 2011

Я провел несколько аналогичных экспериментов с использованием сгенерированного массива:

$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_start и ob_get_clean, добавляющими предельные издержки.

0 голосов
/ 13 октября 2011

Там - это разница между двумя подходами, но это удивительно, это должно иметь такое большое значение.

Каждый раз, когда вы объединяете две строки, PHP должен выделять новуюблок памяти, достаточно большой для новой строки.Для действительно больших строк он должен найти еще больший блок непрерывной памяти.Поскольку каждый раз, когда он должен быть немного больше, он не может повторно использовать прежние блоки (они слишком малы).Так что, если ваша строка увеличивается, может быть медленнее найти другой блок памяти и скопировать строку.

  1. В вашем первом примере у вас много операций . в одном цикле.С каждым . уже большая строка становится больше.

  2. Во втором примере вы собираете все . операции цикла.Переменная $ сейчас будет относительно небольшой, и поэтому эти объединения будут быстрыми.Только один раз за цикл вам нужно найти большой блок памяти.

Как уже упоминалось, я немного удивлен, что это должно иметь такое большое значение, но в зависимости от числаитераций это может быть возможно.

...