php: output[] w/join vs $output .=

Я изменяю некоторый код, в котором первоначальный автор создал веб-страницу, используя массив таким образом:

 $output[]=$stuff_from_database;
 $output[]='more stuff';
 // etc
 echo join('',$output);

Кто-нибудь может придумать причину, по которой это было бы предпочтительнее (или наоборот):

 $output =$stuff_from_database;
 $output .='more stuff';
 // etc
 echo $output;

person Community    schedule 21.09.2008    source источник


Ответы (10)


Вероятно, это было написано кем-то, кто пришел из языка, в котором строки неизменяемы, и поэтому конкатенация обходится дорого. PHP не является одним из них, как показывают следующие тесты. Таким образом, второй подход лучше с точки зрения производительности. Единственная другая причина, по которой я могу использовать первый подход, заключается в том, чтобы иметь возможность заменить часть массива другой, но это означает отслеживать индексы, которые не указаны.

~$ cat join.php
<?php

for ($i=0;$i<50000;$i++) {
$output[] = "HI $i\n";
}

echo join('',$output);
?>


~$ time for i in `seq 100`; do php join.php >> outjoin ; done

real    0m19.145s
user    0m12.045s
sys     0m3.216s

~$ cat dot.php
<?php

for ($i=0;$i<50000;$i++) {
$output.= "HI $i\n";
}

echo $output;
?>


~$ time for i in `seq 100`; do php dot.php >> outdot ; done

real    0m15.530s
user    0m8.985s
sys     0m2.260s
person Vinko Vrsalovic    schedule 21.09.2008
comment
Это явное улучшение по сравнению с PHP 4, где все было наоборот. - person staticsan; 27.04.2009

Это немного не по теме, но

$output =$stuff_from_database;
$output .='more stuff';
// etc
echo $output;

Гораздо медленнее, чем:

echo = $stuff_from_database;
echo 'more stuff';

На самом деле, самый быстрый способ построить строку в PHP:

ob_start();
echo = $stuff_from_database;
echo 'more stuff';
$output = ob_get_contents();
ob_end_clean();

Из-за того, как работают буферы вывода и тому подобное, это самый быстрый способ построить строку. Очевидно, вы бы сделали это только в том случае, если вам действительно нужно оптимизировать построение жала, поскольку это уродливо и не приводит к легкому чтению кода. И всем известно, что "преждевременная оптимизация - корень всех зол".

person Kris Erickson    schedule 21.09.2008
comment
с небольшими исправлениями кода вокруг echo = и выводом '$ output', это быстрее всего, потому что буферизация вывода выделяется фрагментами по 4 или 8 КБ, а не столько, сколько необходимо (всего 1 байт). - person Alister Bulman; 22.09.2008

Опять же, немного не по теме (не очень далеко), но если бы вы стремились поместить что-то между выводимыми элементами массива, то, если бы нужно было конкатенировать всего несколько строк, join(', ', $output) сделал бы это легко и достаточно быстро. Это было бы проще написать и избежать необходимости проверять конец списка (где вам не нужен завершающий ',').

Что касается времени программиста, поскольку оно примерно в 1000 раз дороже, чем цикл процессора, я обычно просто добавляю его в соединение, если оно не будет запускаться более 10 000 раз в секунду.

Подобная микрооптимизация после кодирования очень редко оправдывает себя с точки зрения затраченного времени по сравнению с сэкономленным процессорным временем.

person Alister Bulman    schedule 22.09.2008

!-- Предыдущий комментарий удален -->

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

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

  1. UNTRUE: интерполяция строк выполняется медленнее, чем объединение строк. (!)
  2. НЕВЕРНО: SprintF быстрее.

В реальных тестах Sprintf был самым медленным, а интерполяция была самой быстрой.

PHP 5.2.6-pl7-gentoo (cli) (built: Sep 21 2008 13:43:03) 
Copyright (c) 1997-2008 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2008 Zend Technologies
    with Xdebug v2.0.3, Copyright (c) 2002-2007, by Derick Rethans

Это может быть условно для моей настройки, но все равно странно. :/

person Kent Fredric    schedule 21.09.2008

Я думаю, что самый быстрый способ сделать это с эхом. Это не так красиво и, вероятно, недостаточно быстро, чтобы окупить затраты на читабельность.

echo $stuff_from_database
   , 'more stuff'
   , 'yet more'
   // etc
   , 'last stuff';
person Kip    schedule 22.09.2008

Нижняя часть будет многократно перераспределять строку $output, тогда как я считаю, что верхняя часть просто сохранит каждую часть в массиве, а затем объединит их все в конце. В результате исходный пример может оказаться быстрее. Если это не зависит от производительности, я бы, вероятно, добавил, а не присоединился.

person dvorak    schedule 21.09.2008

Эта часть кода не зависит от производительности; он используется для форматирования и отображения информации о корзине покупок пользователя на веб-сайте с низким трафиком. Кроме того, массив (или строка) каждый раз строится с нуля, и нет необходимости обращаться к конкретным элементам или искать их. Похоже, что массив несколько более эффективен, но я думаю, что это не имеет значения в любом случае для этого использования. Спасибо за всю информацию!

person Community    schedule 24.09.2008

Если вы создаете список (например, корзину для покупок), у вас должен быть некоторый код, который генерирует HTML из каждой записи, полученной из базы данных.

for ($prod in $cart)
{
  $prod->printHTML();
}

Или что-то подобное. Таким образом, код становится намного чище. Конечно, это предполагает, что у вас хороший код с объектами, а не полная идиотская каша, как это делает моя компания (и заменяет).

person Dan Udey    schedule 24.09.2008

Это уже было сказано, но я думаю, что внятный ответ поможет.

Первоначальный программист написал это так, потому что думал, что так будет быстрее. На самом деле в PHP 4 это действительно было быстрее, особенно для больших строк.

person staticsan    schedule 27.04.2009

При соединении с implode() или join() PHP может лучше оптимизировать это. Он проходит по всем строкам в вашем списке, вычисляет длину, выделяет необходимое пространство и заполняет его. По сравнению с ".=" он должен постоянно free() и malloc() места для строки.

person Armin Ronacher    schedule 21.09.2008
comment
Вы поднимаете хороший вопрос. Будет интересно измерить потребление памяти... возможно, это может быть узким местом для большого набора данных. - person Camilo Díaz Repka; 21.09.2008