PHP, почему continue n медленнее, чем использование break

Пожалуйста, рассмотрите следующий код:

$start = microtime();
for($i = 2; $i < 100; $i++)
{
    for($y = 2; $y <= sqrt($i); $y++)
    {
        if($i%$y != 0)
        {
            continue;
        }
        else
        {
          continue 2;
        }
    }

    echo $i.',';
}
echo "\nFinished in " . (microtime() - $start);

Учитывая, что приведенный выше код эффективно использует continue 2 для разрыва внутреннего цикла и пропуска любого кода после внутреннего цикла, почему следующий код в среднем выполняется быстрее, когда кажется, что он делает больше:

 $start = microtime();
for($i = 2; $i < 100; $i++)
{
    $flag = true;
    for($y = 2; $y <= sqrt($i); $y++)
    {
        if($i%$y != 0)
        {
            continue;
        }
        else
        {
          $flag = false;
          break;
        }
    }

    if($flag === true) echo $i.',';
}
echo "\nFinished in " . (microtime() - $start);

Спасибо за любой вклад.

_____ Обновление ______< /em>______

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

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

Было протестировано более одного прогона, как следует из использования слова «среднее».

Просто для иллюстрации рассмотрите следующие небольшие примеры использования microtime(true), которые показывают тот же шаблон, что и использование microtime().

Я знаю, что это небольшая выборка, но схема вполне ясна:

Продолжить 0,00037288665771484 0,00048208236694336 0,00046110153198242 0,00039386749267578 0,0003662109375

Перерыв 0,00033903121948242 0,00035715103149414 0,00033307075500488 0,00034403800964355 0,00032901763916016

Спасибо за внимание и спасибо за любые дальнейшие отзывы.

______ ОБНОВЛЕНИЕ Дальнейшее расследование _____ _______

Интересно, что если операторы echo удалены из кода, то continue выполняется быстрее, а с операторами echo на месте break быстрее.

Пожалуйста, рассмотрите следующий пример кода и учтите, что результаты конфликтуют в зависимости от того, удалены операторы эха или нет:

<?php
$breakStats = array();
$continueStats = array();

ob_start();
for($i = 0; $i < 10000; $i++)
{
   $breakStats[] = doBreakTest();
   $continueStats[] = doContinueTest();
}
ob_clean();

echo "<br/>Continue Mean " . (array_sum($continueStats) / count($continueStats));
echo "<br/>Break Mean " . (array_sum($breakStats) / count($breakStats));

function doBreakTest()
{
    $start = microtime(true);
    for($i = 2; $i < 100; $i++)
    {
        $flag = true;
        $root = sqrt($i);
        for($y = 2; $y <= $root; $y++)
        {
            if($i%$y != 0)
            {
                continue;
            }
            else
            {
                $flag = false;
                break;
            }
        }
    }

    if($flag === true) echo $i . '';
    return microtime(true) - $start;
}

function doContinueTest()
{
    $start = microtime(true);
    for($i = 2; $i < 100; $i++)
    {
        $root = sqrt($i);
        for($y = 2; $y <= $root; $y++)
        {
            if($i%$y != 0)
            {
                continue;
            }
            else
            {
                echo $i . '';
                continue 2;
            }
        }
    }
    return microtime(true) - $start;
}

Присутствуют эхо-утверждения:

Среднее значение продолжения 0,00014134283065796 Среднее значение перерыва 0,00012669243812561

Эхо-выражения отсутствуют:

Среднее значение продолжения 0,00011746988296509 Среднее значение перерыва 0,00013022310733795

Обратите внимание, что, удаляя оператор echo из теста break and flag, мы также удаляем проверку ($flag === true), поэтому нагрузка должна уменьшиться, но в этом случае continue все равно выигрывает. Вт

Таким образом, в чистом сценарии continue n против break + flag оказывается, что continue n является более быстрой конструкцией. Но добавьте равное количество идентичных операторов эха и флаги производительности continue n.

Для меня логически это имеет смысл, что continue n должен быть быстрее, но я ожидал увидеть то же самое с присутствующими операторами эха.

Это явно разница в сгенерированных кодах операций и позиции оператора эха (внутренний цикл по сравнению с внешним циклом). Кто-нибудь знает способ увидеть сгенерированные коды операций? Я полагаю, это в конечном счете то, что мне нужно, поскольку я пытаюсь понять, что происходит внутри.

Спасибо :)


person Gavin    schedule 02.04.2011    source источник
comment
Выходы одинаковые? Вы можете вставить их сюда?   -  person Preet Sangha    schedule 03.04.2011
comment
Эм, насколько быстрее? И вы должны использовать microtime(TRUE), чтобы получить число с плавающей запятой, полезное для сравнения.   -  person mario    schedule 03.04.2011
comment
Сравнение с одиночным прогоном бессмысленно. Сценарий Time 10 000 запускается и получает среднее значение, чтобы получить полезные данные. А затем подумайте, насколько это на самом деле быстрее (если вообще) и насколько это действительно важно для вас.   -  person Lightness Races in Orbit    schedule 03.04.2011
comment
Вам следует избегать чрезмерного использования continue и break в циклах. Вместо этого рассмотрите возможность использования while. Не используйте функции в тестовом выражении цикла for.   -  person KingCrunch    schedule 03.04.2011
comment
Попробуйте использовать microtime(true), а не microtime()... это будет иметь большое значение. microtime() возвращает строку microtime(true) возвращает число с плавающей запятой, которое можно вычесть более аккуратно   -  person Mark Baker    schedule 03.04.2011
comment
Здесь, в PHP 5.3.6, они выполняются примерно одинаковое время. Тем не менее, это микробенчмарк, поэтому любая разница, с которой вы можете столкнуться, связана с тем, как она реализована внутри PHP.   -  person Josh Davis    schedule 03.04.2011
comment
@KingCrunch: или иди. Это может быть чище, чем куча continue <n>.   -  person Josh Davis    schedule 03.04.2011
comment
Пожалуйста, смотрите редактирование. Кстати у меня php 5.3   -  person Gavin    schedule 03.04.2011
comment
Извините, написал до того, как закончил :) PHP 5.3.5 для Windows.   -  person Gavin    schedule 03.04.2011
comment
@Джош Дэвис: xkcd.com/292 :D   -  person KingCrunch    schedule 03.04.2011
comment
Использование microtime() и microtime(true) не так незначительно, как вы предлагаете... если время начала/окончания пересекает вторую границу, простое время окончания - время начала может фактически дать вам отрицательную разницу, и это не никогда не рассчитывать правильно, если цикл занимает больше секунды, игнорируя секунды - это делает метод таймера весьма важным   -  person Mark Baker    schedule 03.04.2011
comment
@Mark Baker признал, что microtime (true) должен был использоваться и использовался в перечисленных образцах, которые показывают точно такой же шаблон, что и использование microtime ().   -  person Gavin    schedule 03.04.2011


Ответы (2)


Да, первый немного быстрее. Это потому, что он просто выскакивает при продолжении 2 и печатает $i.

2-й пример имеет больше работы... присваивает значение переменной $flag, выходит из цикла, проверяет значение $flag, проверяет тип $flag (также сравнивает) и затем выводит $i. Это немного медленнее (простая логика).

В любом случае, есть ли в этом какая-то цель?

Некоторые мои результаты для сравнения

0.0011570 < 0.0012173
0.0011540 < 0.0011754
0.0011820 < 0.0012036
0.0011570 < 0.0011693
0.0011970 < 0.0012790

Использовано: PHP 5.3.5 @ Windows (1000 попыток; 100% сначала было быстрее)

0.0011570 < 0.0012173
0.0005000 > 0.0003333
0.0005110 > 0.0004159
0.0003900 < 0.0014029
0.0003950 > 0.0003119
0.0003120 > 0.0002370

Использовано: PHP 5.3.3 @ Linux (1000 попыток; 32% первое: 68% второе было быстрее)

0.0006700 > 0.0004863
0.0003470 > 0.0002591
0.0005360 > 0.0004027
0.0004720 > 0.0004229
0.0005300 > 0.0004366

Использовано: PHP 5.2.13 @ Linux (1000 попыток; 9% первый: 91% второй был быстрее)

Извините, у меня больше нет серверов для тестирования :) Теперь я думаю, что это в основном зависит от железа (и, возможно, от ОС тоже).

В целом: Это доказывает только то, что Linux-сервер работает быстрее, чем один запуск на Windows :)

person Wh1T3h4Ck5    schedule 02.04.2011
comment
Возможно, вы правы, но вопрос предполагает, что первый медленнее, а не быстрее. - person KingCrunch; 03.04.2011
comment
@KingCrunch: может быть, но я проверил этот код, и каждый первый запуск был быстрее. - person Wh1T3h4Ck5; 03.04.2011
comment
@ Wh1T3h4Ck5 Интересное наблюдение, могу я спросить, какая платформа и версия PHP? - person Gavin; 03.04.2011
comment
Это может быть связано с моим конкретным оборудованием, логически я с вами, отсюда и вопрос, но это не те результаты, которые я вижу. Попробую другую коробку. - person Gavin; 03.04.2011
comment
Я провел тест на своем 3-м сервере и получил эти 9% в первую очередь: 91% во второй процедуре было быстрее. - person Wh1T3h4Ck5; 03.04.2011
comment
@ Wh1T3h4Ck5 Спасибо за все отзывы! Я вижу ту же картину на сервере Win php 5.3.5. - person Gavin; 03.04.2011
comment
@Gavin: Это для тебя, приятель (10 000 попыток x 3 сервера). Вы были правы, 2-й быстрее... намного быстрее. service-kl.com/code/results.zip Полные результаты моего последнего тест, улучшенный с микровременем (правда). - person Wh1T3h4Ck5; 03.04.2011
comment
Легенда: f1 - 1-й гноился. f2 - 2-й был быстрее, fE - такая же скорость (невероятно, даже я использовал 16-значную точность). - person Wh1T3h4Ck5; 03.04.2011
comment
Серьезно, спасибо за всю эту статистику! И для развлечения моего навязчивого любопытства - person Gavin; 03.04.2011
comment
Большая точность цифр не делает результат более значимым или значимым! Я знаю, что вы просто играете, но такие различия не имеют значения и являются пустой тратой всех видов ресурсов. - person markus; 03.04.2011

Версия continue 2 для меня немного быстрее. Но это не те вещи, которыми вам обычно нужно заниматься. Рассмотреть возможность:

for($y = 2; $y <= sqrt($i); $y++)

Здесь вы вычисляете sqrt на каждой итерации. Просто изменив это на:

$sqrt = sqrt($i);
for($y = 2; $y <= $sqrt; $y++)

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

continue 2 следует использовать, если вы обнаружите, что вам его легче понять. Компьютеру все равно.

Чтобы обратиться к вашему обновлению, касающемуся просмотра кодов операций, см.:

php -d vld.active=1 -d vld.execute=0 -f foo.php

person Matthew    schedule 02.04.2011
comment
отличная читаемость справочного кода и вывод вычислений из инициализатора for. Чтобы поместить это в контекст, мы на самом деле не искали самый быстрый способ добиться этого. Мы обсуждали ответы на вопросы интервью, и в ходе обсуждения я предположил, что метод continue 2 будет работать немного лучше, чем вариант с перерывом. Мы протестировали его, и на моем оборудовании и нашем сервере я ошибся. Я просто не понимаю, почему. - person Gavin; 03.04.2011
comment
@ Гэвин, если вы не обсуждаете самый быстрый способ, то этот вопрос даже не задавался бы. ;) Я понимаю любопытство, но подходы настолько похожи, что почти невозможно провести какое-либо осмысленное сравнение, особенно когда это даже близко не является узким местом. Лучшим тестом (но все же в основном бесполезным) был бы тот, который не включал бы операторы эха. - person Matthew; 03.04.2011
comment
@Gavin, см. pecl.php.net/package/vld, если вы хотите сравнить коды операций. Хотя не думаю, что это будет слишком полезно. Я думаю, что понимание того, как работает движок Zend и как современные компиляторы + компьютеры оптимизируют и делают прогнозы, было бы более полезным. - person Matthew; 03.04.2011