2016-09-15 4 views
2

У меня есть простой метод:Почему ненужное предложение IF увеличивает производительность?

public function validateStringByPrefix(string $string, $prefix) 
{ 
    $valid = false; 
    if (is_string($prefix)) { 
     if (! empty($prefix) && strpos($string, $prefix) === 0) { 
      $valid = true; 
     } 
    } elseif (is_array($prefix)) { 
     foreach ($prefix as $partPrefix) { 
      if (! empty($partPrefix) && strpos($string, $partPrefix) === 0) { 
       $valid = true; 
       break; 
      } 
     } 
    } 
    return $valid; 
} 

После того как я заметил, что условие ! empty($prefix) на самом деле само собой, я удалил его. Я ожидал минимального увеличения производительности или, по крайней мере, той же производительности, что и до изменения. Но вместо этого производительность ухудшилась.

Это может иметь смысл, если на самом деле имеются случаи с пустым $prefix или $partPrefix. Потому что проверка empty(...) будет очень дешевой.

Но нет таких случаев, я проверил это:

if(empty($prefix)) { 
    die(__FILE__); 
} 

перед: Webgrind (в процентах), с if(! empty(...))

before (with if(! empty(...)))

после: Webgrind (в процентах), без if(! empty(...))

after (without if(! empty(...)))

Так что может объяснить такое поведение? Почему ненужное предложение IF, которое всегда терпит неудачу, увеличивает производительность?


UPDATE

Просто посмотрел в Webgrind отчеты в миллисекундах:

перед: Webgrind (в миллисекундах), с if(! empty(...))

before (in milliseconds, with if(! empty(...)))

после: Webgrind (в миллисекундах), без if(! empty(...))

after (in milliseconds, without if(! empty(...)))

Так подсчитываются в миллисекундах удаление клаузулы Излишне IF увеличивает производительность ... Как результат в процентах отличаться от результата в миллисекундах ?

+9

Как вам удалось оценить производительность? –

+0

@ Zeratops верна. –

+0

Xdebug Profiler + Webgrind. Просто добавил скриншоты для обоих случаев. – automatix

ответ

-4

Оператор if будет оцениваться слева направо (& &), и, по моему мнению, в некоторых случаях функция empty() возвращает истинный результат, что означает, что функция strpos() не будет вызываться. В этом случае, если вы должны были удалить вызов функции empty(), всегда будет оцениваться strpos(), которая может объяснить потерю производительности (должна быть незначительной).

+3

Вы вводите в заблуждение '&&' и '||'. – Siguza

+1

Как я уже сказал, я проверил это: это предложение 'IF' никогда не преуспевает. – automatix

0

Как указано в комментариях, эти изменения и результаты настолько малы, что бессмысленны.

В вызовах 70 тыс. Вы получаете разницу в 171 мс, так что это, вероятно, результат изменения среды, а не вашего кода.Если вы запустите этот тест еще 100 раз, разница будет изменяться при каждом запуске (и может даже увеличиться), как ответ на изменения в среде.

В качестве «среды» я имею в виду компьютер, на котором запущен benckmark, сеть, где проходят данные, и т. Д. На компьютере работают другие процессы, кроме кода, поэтому, если другой процесс ест некоторый процессор, ваш код работает медленно чем раньше, и так далее.

Для правильной оценки кода вам нужно рассмотреть много вещей больше, чем просто код, и даже код может обмануть вас с помощью компилятора и т. Д. (Например, взгляните на this video. Это тест JS, но многое информация может быть применена и к другим языкам программирования).

2

Фактически существует вполне логичная причина, по которой одна функция оказывается незначительно быстрее другой, и она не имеет ничего общего с тем фактом, что вы удалили или добавили условие empty().

Эти различия крошечные, что их нужно измерять в микросекундах, но есть фактически разница. Разница заключается в том, какой порядок функций вызывается, а точнее, где они выделены в куче PHP VM.

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

Сначала посмотрим на this 3v4l, где мы определим вашу функцию с и без условия empty() как validateStringByPrefix1() и validateStringByPrefix2() соответственно. Здесь мы называем validateStringByPrefix2(), во-первых, результатом чего является время выполнения 40 microsecond. Обратите внимание, что в обоих случаях функция должна возвращать false, а empty($prefix) никогда не будет правдой (как и в вашем собственном тесте). Во втором тесте с использование empty($prefix) похоже, что функция фактически выполняется быстрее на 11 microseconds.

Во-вторых, посмотрите на this 3v4l где мы определяем те же функции в точности, но слово validateStringByPrefix1()первый и получить противоположные результаты. Теперь это выглядит как без использование empty($prefix) Функция работает немного быстрее на 12 microseconds, а другая работает немного медленнее на 88 microseconds.

Помните, что microtime() на самом деле не является точным часом. Он может слегка колебаться в течение нескольких микросекунд, но обычно этого недостаточно, чтобы быть на порядок медленнее или быстрее в среднем. Итак, да, есть разница, но нет это не из-за использования empty() или его отсутствия.

Вместо этого эта проблема имеет гораздо больше общего с тем, как работает типичная архитектура x86 и как ваш процессор и память имеют дело с кешем. Функция, определенная в вашем коде, обычно будет храниться в памяти с помощью PHP, в том порядке, в котором они выполняются в первый раз (здесь происходит этап компиляции). Первая функция, которую нужно выполнить, будет сначала кэширована. Существуют концепции кэша для записи, например, с write allocate и нет записи выделить, что может повлиять на это.Следующая выполняемая функция перезаписывает этот кеш, что приводит к очень незначительному замедлению в памяти, что может или не может быть последовательным в зависимости от факторов, которые я не буду здесь вводить.

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

Вот почему, когда вы действительно должны микро-оптимизации кода программы для максимально быстрого выполнения, вы, как правило, проходят через кропотливый процесс ведения Profile-guided Optimizationили PGO.

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

Смежные вопросы