Измерение длительности с помощью микротайма приводит к случайным результатам в ноль
У меня такая петля:
<?php
ini_set('memory_limit', '16024M');
ini_set('set_time_limit', 9999);
ini_set('max_execution_time', 9999);
ini_set('display_errors', TRUE);
ini_set('error_reporting', E_ALL);
for ($k = 1; $k <= 50; $k++) {
$haystack = array();
for ($i = 1; $i <= 100; $i++) {
$randomChar = substr(md5(microtime()),rand(0,26), 1);
$haystack[] = $randomChar;
}
$haystack[] = 'X';
$startTime = microtime(true);
// sleep(0);
$result = in_array('X', $haystack);
$endTime = microtime(true);
echo number_format(1000000 * ($endTime - $startTime), 20, ",", " ") . ' ';
}
И это первая пара строк из вывода:
1,90734863281250000000 0,95367431640625000000 1,19209289550781250000 1,90734863281250000000 1,19209289550781250000 0,95367431640625000000 0,95367431640625000000 1,90734863281250000000 0,95367431640625000000 20,02716064453125000000 0,95367431640625000000 1,19209289550781250000 0,95367431640625000000 0,95367431640625000000 0,00000000000000000000 0,95367431640625000000 0, 95367431640625000000 0,95367431640625000000 0,00000000000000000000 0,95367431640625000000 0,00000000000000000000
Как видите, есть пара строк, в которых указана длительность "0", что на самом деле невозможно. Если я раскомментирую строку, содержащую команду sleep(0), нулевой длительности не будет.
Настройка системы
- PHP 7.0 с FPM
- nginx 1.10.3
- Ubuntu 16.04
Я запускаю цикл на CLI и вызываю его через браузер.
2 ответа
101 элемент в массиве достаточно мал для умного php с его статическими приемами оптимизации и мощным процессором.
Если вы хотите увидеть, что 0 пропали, сгенерируйте 1000 элементов:
for ($i = 1; $i <= 1000; $i++) {
$haystack[] = substr(md5(microtime()),rand(0,26), 1);
}
PS Я проверил Ваш код, используя 7.1 и 5.6, поэтому есть большие различия:
Просто в дополнение к ответу @num8er, который, кажется, является ответом, я попытался узнать больше, потому что это действительно вызвало у меня бессонные ночи. Я немного улучшил приведенный выше скрипт и провел несколько дополнительных измерений:
ini_set('memory_limit', '16024M');
ini_set('set_time_limit', 9999);
ini_set('set_time_limit', -1);
ini_set('max_execution_time', 9999);
ini_set('max_execution_time', -1);
ini_set('display_errors', TRUE);
ini_set('error_reporting', E_ALL);
echo "<table>";
echo "<tr>";
echo "<th>duration</th>";
echo "<th>position</th>";
echo "<th>fake</th>";
echo "<th>found</th>";
echo "<th>optimized</th>";
echo "</tr>";
$endPosition = TRUE;
$fake = false;
for ($k = 1; $k <= 10000; $k++) {
$haystack = array();
for ($i = 1; $i <= 50000; $i++) {
$randomChar = substr(md5(microtime()),rand(0,26), 1);
$haystack[] = $randomChar;
}
if ($fake) {
$needle = NULL;
} else {
if ($endPosition) {
$needle = $haystack[sizeof($haystack) - 1];
} else {
$needle = $haystack[floor(sizeof($haystack)/ 2)];
}
}
$startTime = microtime(true);
//sleep(0);
$result = in_array($needle, $haystack);
$endTime = microtime(true);
$duration = ($endTime - $startTime);
echo "<tr>";
echo "<td>";
echo number_format($duration, 30, ",", " ");
echo "</td>";
echo "<td>";
echo ($endPosition) ? "end": "middle";
echo "</td>";
echo "<td>";
echo ($fake) ? "fake": "no fake";
echo "</td>";
echo "<td>";
echo ($result) ? "found": "not found";
echo "</td>";
echo "<td>";
echo ($duration == 0) ? "optimized": "---";
echo "</td>";
echo "</tr>";
$endPosition = (rand(0,100) < 50) ? TRUE : FALSE;
$fake = (rand(0,100) < 25) ? TRUE : FALSE;
}
echo "</table>";
Я добавил случайную "фальшивую функцию". Случайно 25% итераций не должны возвращать положительный результат поиска. И в случайных 50% итераций игла будет размещена в середине стога сена, а не в конце. Я запускал этот скрипт несколько раз для разных настроек (итерации, длина массива), и в конце у меня было около 225.000 строк результатов. Быстрое добавление небольшой сводной таблицы показывает, где PHP (7.0.32 fpm и CPU (Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz) достигают предела):
Числа равны миллисекундам / 1000, поэтому даже сложные (например, 500 000 ключей, 1000 итераций) заняли 0,000000953674 микросекунд - благодаря оптимизации. Это поразительно.
Что также интересно: минимальные длительности, если не "0", одинаковы (0,000953674) или удвоены (0,000001907349) даже для разных итераций! Итак, я предполагаю, но это довольно наивное мышление: если бы я запустил тест с большими массивами или большим количеством итераций, следующий предстоящий минимум составил бы 0,00000381469 микросекунд.
Как вы также можете видеть, и, как уже говорилось в num8er, потенциал для оптимизации возрастает по мере усложнения работы.
10-кратное сканирование массивов длиной 50 000 ключей даже медленнее, чем 100 или 1000 итераций. Из 1.000 итераций более 10% результатов были получены в "оптимизированное" время.
Наконец, я хочу отметить, что, похоже, нет никакой разницы, находится ли игла в середине стога сена или в конце. Следующая диаграмма показывает минимальные длительности для 10, 100 и 1.000 итераций при поиске в 500.000 массивов ключей. Как видите, minium всегда "магический" 0,000000953674:
Само собой разумеется, что каждая итерация возвращает правильный результат. Таким образом, in_array () никогда не возвращал положительный результат, когда он сканировал массив сена, не содержащий иглы.
Это, возможно, не добавляет более глубоких технических деталей к функции оптимизации PHP, но, тем не менее, я думаю, что интересно увидеть влияние этой функции.