Как часто вы беспокоитесь о том, сколько дел нужно будет обрабатывать?

StackOverflow https://stackoverflow.com/questions/172831

Вопрос

Если у вас есть следующее:

$var = 3; // we'll say it's set to 3 for this example
if ($var == 4) {
    // do something
} else if ($var == 5) {
    // do something
} else if ($var == 2) {
    // do something
} else if ($var == 3) {
    // do something
} else {
    // do something
}

Если, скажем, в 80% случаев $ var равно 3, вы беспокоитесь о том, что он проходит через 4 случая, прежде чем найти истинный случай?

Я думаю, что на небольшом сайте это не имеет большого значения, но как насчет того, когда оператор if будет работать тысячи раз в секунду?

Я работаю в PHP, но думаю, что язык не имеет значения.

Это было полезно?

Решение

Вот как мы это сделали, когда я писал программы для радиолокационных систем. (Скорость имеет значение в радаре. Это одно из немногих мест, где «в реальном времени» фактически означает «реальный» вместо «быстрый».)

[Я переключусь на синтаксис Python, мне проще, и я уверен, что вы можете его интерпретировать.]

if var <= 3:
    if var == 2:
        # do something
    elif var == 3:
        # do something
    else: 
        raise Exception
else:
    if var == 4:
        # do something
    elif var == 5:
        # do something
    else:
        raise Exception

Ваши операторы if формируют дерево, а не плоский список. Добавляя условия в этот список, вы перемещаетесь по центру дерева. Плоская последовательность сравнений n занимает в среднем n / 2 шага. Дерево приводит к последовательности сравнений, которая берет журнальные ( n ) сравнения.

Другие советы

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

Сказав это, как и при любой оптимизации:

<Ол>
  • Сделай так, чтобы это работало
  • Измерь это
  • Если это достаточно быстро, оставьте это в покое
  • Если это слишком медленно, ТО оптимизируйте его
  • О, и я бы, наверное, использовал переключатель / чехол с самого начала! ; -)

    Классический случай этого (буквально 5 вариантов, как в вашем посте) был в ffmpeg, в функции decode_cabac_residual. Это было довольно важно, так как профилирование (очень важно - не оптимизировать перед профилированием!) Показало, что на него приходится более 10-15% времени, потраченного на декодирование видео H.264. Оператор if контролировал набор операторов, которые рассчитывались по-разному для различных типов невязок, подлежащих декодированию, и, к сожалению, слишком большая скорость терялась из-за размера кода, если функция дублировалась 5 раз для каждого из 5 типов остаточное. Так что вместо этого нужно было использовать цепочку if.

    Профилирование было выполнено во многих общих тестовых потоках, чтобы упорядочить их по вероятности; верх был самым распространенным, нижний - наименьшим. Это дало небольшой прирост скорости.

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

    Использование оператора switch / case - определенно правильный путь.

    Это дает компилятору (интерпретатору) возможность использовать таблицу переходов для перехода к нужной ветви без необходимости выполнять N сравнений. Представьте, что он создает массив адресов с индексами 0, 1, 2, ... тогда он может просто найти правильный адрес в массиве за одну операцию.

    Кроме того, поскольку в выражении case меньше накладных расходов, он также читается легче.

    Update: , если сравнения подходят для оператора switch, то в этой области может помочь оптимизация с учетом профиля. Запустив сборку PGO с реалистичными тестовыми нагрузками, система может сгенерировать информацию об использовании филиала, а затем использовать ее для оптимизации выбранного пути.

    Вместо того, чтобы отвечать на вопрос PHP, я отвечу немного более широко. Это не относится напрямую к PHP, поскольку будет проходить некоторую интерпретацию.

    Многие компиляторы могут конвертировать в блоки if-elif-elif -... и из них, чтобы переключать блоки, если это необходимо, и тесты в elif-частях достаточно просты (а остальная семантика оказывается совместимой). Для 3-4 тестов не обязательно что-либо выиграть, используя таблицу переходов.

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

    Однако в вашем примере большинство компиляторов распознают, что $ var является константой 3, а затем заменяют $ var на 3 в блоках if..elif .. Это, в свою очередь, делает выражения постоянными, поэтому они складываются в одно из истинных или ложных Все ложные ответвления уничтожаются средством удаления мертвых кодов, а также проверяется проверка на истинность. Остается случай, когда $ var == 3. Вы не можете полагаться на то, что PHP такой умный. В общем, вы не можете распространять $ var, но это может быть возможно на некоторых сайтах вызовов.

    Вы можете попробовать массив блоков кода, который вы вызываете. Тогда все блоки кода имеют одинаковые издержки.

    Perl 6:

    our @code_blocks = (
      { 'Code Block 0' },
      { 'Code Block 1' },
      { 'Code Block 2' },
      { 'Code Block 3' },
      { 'Code Block 4' },
      { 'Code Block 5' },
    );
    
    if( 0 <= $var < @code_blocks.length ){
      @code_blocks[$var]->();
    }
    

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

    Обычно я согласен с критерием " затем оптимизировать " метод, когда вы не уверены, будет ли производительность достаточно быстрой, но если код просто должен работать как можно быстрее, а исправление так же просто, как перераспределение тестов, то я бы сейчас сделал код быстрым и немного измерил после того, как вы начнете жить, чтобы убедиться, что ваше предположение (например, 3 произойдет в 80% случаев) действительно верно.

    В коде, где это чисто анализ равенства, я бы переместил его в переключатель / регистр, так как это обеспечивает лучшую производительность.

    $var = 3; // we'll say it's set to 3 for this example
    switch($var)
     {
       case 4:
          //do something
          break;
       case 5:
          //do something
          break;
       case:
          //do something when none of the provided cases match (same as using an else{ after the elseif{
     }
    

    Теперь, если вы делаете более сложные сравнения, я бы либо вложил их в переключатель, либо просто использовал elseif.

    В объектно-ориентированных языках, если опция предоставляет массивные ifs, то это означает, что вы должны просто переместить поведение (например, ваши блоки // делать что-то ) в объект, содержащий значение.

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

    Время это. Посмотрите, сколько раз в секунду вы можете выполнить приведенный выше оператор if / else if / else без каких-либо действий, и $ var не является одним из вариантов.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top