Сегодня я наткнулся на забавный пост на r/ProgrammerHumor, который заставил меня задуматься об одной вещи, которая меня всегда интересовала. Действительно ли важно, какие утверждения мы используем? Или читабельность и связность важнее?
Для первого теста я решил сравнить между собой операторы if-else и switch, используя 100 000 итераций для каждого.
Что касается второго теста, я сравниваю циклы. Соответственно for, while и foreach при тех же условиях.
Для условных блоков я хочу сравнить их с числовыми значениями, строками и смесью между ними.
Сценарий 1: Числовые значения
function test_numeric_if_else($value) { if ($value === 1) { return "One"; } elseif ($value === 2) { return "Two"; } elseif ($value === 3) { return "Three"; } else { return "Other"; } } function test_numeric_switch($value) { switch ($value) { case 1: return "One"; case 2: return "Two"; case 3: return "Three"; default: return "Other"; } }
Сценарий 2: Строки
function test_string_if_else($value) { if ($value === "apple") { return "Fruit"; } elseif ($value === "carrot") { return "Vegetable"; } elseif ($value === "cookie") { return "Dessert"; } else { return "Unknown"; } } function test_string_switch($value) { switch ($value) { case "apple": return "Fruit"; case "carrot": return "Vegetable"; case "cookie": return "Dessert"; default: return "Unknown"; } }
Сценарий 3: смешанные значения
function test_mixed_if_else($value) { if ($value === 1) { return "One"; } elseif ($value === "two") { return "Two"; } elseif ($value === 3.0) { return "Three"; } else { return "Other"; } } function test_mixed_switch($value) { switch ($value) { case 1: return "One"; case "two": return "Two"; case 3.0: return "Three"; default: return "Other"; } }
Осталось сделать функцию, которая запускает каждую из этих функций 100 000 раз и возвращает среднее время.
function benchmark($testFunction, $iterations, $inputs) { $start = microtime(true); for ($i = 0; $i < $iterations; $i++) { foreach ($inputs as $input) { $testFunction($input); } } $end = microtime(true); return ($end - $start) * 1000; } $iterations = 100000; $inputs = [1, "two", 3.0, "unknown"]; echo "Scenario 1 - if-else time: " . benchmark("test_numeric_if_else", $iterations, $inputs) . "ms\n"; echo "Scenario 1 - switch time: " . benchmark("test_numeric_switch", $iterations, $inputs) . "ms\n"; echo "Scenario 2 - if-else time: " . benchmark("test_string_if_else", $iterations, $inputs) . "ms\n"; echo "Scenario 2 - switch time: " . benchmark("test_string_switch", $iterations, $inputs) . "ms\n"; echo "Scenario 3 - if-else time: " . benchmark("test_mixed_if_else", $iterations, $inputs) . "ms\n"; echo "Scenario 3 - switch time: " . benchmark("test_mixed_switch", $iterations, $inputs) . "ms\n";
В итоге мы получим следующее
+-----------------+-----------+-----------+ | Test Scenario | If-else | Switch | +-----------------+-----------+-----------+ | Numeric values | 25.487 ms | 38.195 ms | +-----------------+-----------+-----------+ | String values | 27.280 ms | 36.887 ms | +-----------------+-----------+-----------+ | Mixed values | 24.140 ms | 31.791 ms | +-----------------+-----------+-----------+
Мы видим, что в каждом сценарии оператор if-else быстрее, чем switch, но разница между ними настолько мала, что мы можем даже не учитывать ее в реальном сценарии.
Если функции требуется 24 или 32 мс для завершения, мы, как люди, не почувствуем разницы.
Что касается итеративных блоков, то мы собираемся повторить тот же процесс, имея 3 разных сценария:
Сценарий 1: Сумма массива целых чисел
function test_for_loop($arr) { $sum = 0; for ($i = 0, $count = count($arr); $i < $count; $i++) { $sum += $arr[$i]; } return $sum; } function test_while_loop($arr) { $sum = 0; $i = 0; $count = count($arr); while ($i < $count) { $sum += $arr[$i]; $i++; } return $sum; } function test_foreach_loop($arr) { $sum = 0; foreach ($arr as $value) { $sum += $value; } return $sum; }
Сценарий 2: Найти максимальное значение в массиве
function test_for_max($arr) { $max = $arr[0]; for ($i = 1, $count = count($arr); $i < $count; $i++) { if ($arr[$i] > $max) { $max = $arr[$i]; } } return $max; } function test_while_max($arr) { $max = $arr[0]; $i = 1; $count = count($arr); while ($i < $count) { if ($arr[$i] > $max) { $max = $arr[$i]; } $i++; } return $max; } function test_foreach_max($arr) { $max = $arr[0]; foreach ($arr as $value) { if ($value > $max) { $max = $value; } } return $max; }
Сценарий 3: Создать массив квадратов
function test_for_squares($count) { $squares = array(); for ($i = 0; $i < $count; $i++) { $squares[] = $i * $i; } return $squares; } function test_while_squares($count) { $squares = array(); $i = 0; while ($i < $count) { $squares[] = $i * $i; $i++; } return $squares; } function test_foreach_squares($count) { $squares = array(); $range = range(0, $count - 1); foreach ($range as $value) { $squares[] = $value * $value; } return $squares; }
Для повторных тестов мы будем использовать тот же подход, что и раньше:
function benchmark($testFunction, $iterations, $inputs) { $start = microtime(true); for ($i = 0; $i < $iterations; $i++) { foreach ($inputs as $input) { $testFunction($input); } } $end = microtime(true); return ($end - $start) * 1000; } $iterations = 100000; $inputs = [ range(1, 10), range(1, 20), range(1, 30) ]; echo "Scenario 1 - for loop time: " . benchmark("test_for_loop", $iterations, $inputs) . "ms\n"; echo "Scenario 1 - while loop time: " . benchmark("test_while_loop", $iterations, $inputs) . "ms\n"; echo "Scenario 1 - foreach loop time: " . benchmark("test_foreach_loop", $iterations, $inputs) . "ms\n"; echo "Scenario 2 - for loop time: " . benchmark("test_for_max", $iterations, $inputs) . "ms\n"; echo "Scenario 2 - while loop time: " . benchmark("test_while_max", $iterations, $inputs) . "ms\n"; echo "Scenario 2 - foreach loop time: " . benchmark("test_foreach_max", $iterations, $inputs) . "ms\n"; $iterations = 100000; $inputs = [ 10, 20, 30 ]; echo "Scenario 3 - for loop time: " . benchmark("test_for_squares", $iterations, $inputs) . "ms\n"; echo "Scenario 3 - while loop time: " . benchmark("test_while_squares", $iterations, $inputs) . "ms\n"; echo "Scenario 3 - foreach loop time: " . benchmark("test_foreach_squares", $iterations, $inputs) . "ms\n";
Результаты получились более интересными, чем в предыдущем бенчмарке:
+------------------------+-----------+-----------+--------------+ | Test Scenario | For loop | While | Foreach | +------------------------+-----------+-----------+--------------+ | Sum of array integers | 164.025 ms | 164.323 ms | 126.592 ms | +------------------------+-----------+-----------+--------------+ | Maximum value in array | 175.080 ms | 185.791 ms | 112.764 ms | +------------------------+-----------+-----------+--------------+ | Create array of squares| 260.133 ms | 259.297 ms | 240.117 ms | +------------------------+-----------+-----------+--------------+
Мы видим, что цикл for-each показал наилучшие результаты при любом сценарии, но опять же это всего лишь вопрос нескольких сотен мс.
Обе таблицы дают четкое сравнение между различными конструкциями и их производительностью в конкретных тестовых сценариях.
Следует иметь в виду, что эти результаты не всегда могут быть последовательными, в том числе в зависимости от аппаратного обеспечения. В любом случае, выбор между конструкциями должен быть основан, прежде всего, на читабельности и удобстве сопровождения кода.
20.08.2023 18:21
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в 2023-2024 годах? Или это полная лажа?".
20.08.2023 17:46
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
19.08.2023 18:39
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в частности, магию поплавков и гибкость flexbox.
19.08.2023 17:22
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для чтения благодаря своей простоте. Кроме того, мы всегда хотим проверить самые последние возможности в наших проектах!
18.08.2023 20:33
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий их языку и культуре.
14.08.2023 14:49
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип предназначен для представления неделимого значения.