Post

PHP Performance 1 - test IF

Wstęp

Środowisko testowe jest przygotowane - możemy przeprowadzać testy. Pierwsze test dotyczy jakże znanego "if-a". Jak wiecie jest to instrukcja warunkowa. Blok kodu zostaje wykonany tylko i wyłącznie jeśli warunek zostanie spełniony:

$a = 10;
if($a < 20) {
  // ten kod się wykona
}
?>

Razem ze stopniem skomplikowania aplikacji, warunki mogą być naprawdę rozbudowane. W tym miejscu chciałbym pokazać trywialny błąd, na który wiele osób nie zwraca uwagi jakim jest kolejność elementów w sprawdzanym warunku. Na początek bardziej rozbudowany warunek:

$a = 5;
$b = 8;
$c = 11;
if($a > 1 && $b > 5 && $c > 15) {
  // ten kod się nie wykona
}
?>

W powyższym przykładzie warunek składa się z trzech elementów - pomiędzy nimi jest operator AND (logiczne "i"), co oznacza, że wszystkie trzy elementy muszą zostać spełnione, aby cały warunek został spełniony (więcej na temat logiki boolowskiej). W tym przypadku blok kodu należący do "if-a" nie zostanie wykonany ponieważ trzeci element ($c > 15) nie zostaje spełniony, gdyż $c = 11. Najistotniejszą w tej chwili informacją dla nas jako programistów jest fakt, że warunki są sprawdzane od lewej do prawej. Na czym więc będzie polegał test? Chciałbym pokazać różnicę w czasie wykonywania na realnym przykładzie.

Jeśli jeszcze nie pobrałeś repozytorium z testami, to zapraszam pod ten link. W tym poście opisałem jak należy przygotować środowisko testowe.

Wspólny kod

Wspólną częścią testów są dołączane pliki z funkcjami, połączenie do bazy danych oraz odczytanie z linii poleceń ile razy ma zostać wykonany:

require_once 'inc/function.php';
require_once 'inc/db.php';
define('TEST_COUNT', isset($argv[1]) ? (int) $argv[1] : 1000);

Funkcja microtimeFloat() z pliku inc/function.php będzie nam służyła do odmierzania czasu pomiędzy rozpoczęciem testu a jego zakończeniem. Funkcja selectDb() będzie pomocna przy pobieraniu informacji z bazy danych. Jako parametr przyjmuje handler PDO, zapytanie SQL, parametry do zbindowania oraz zmienna do której zostanie zapisana ewentualna treść błędu (źródło funkcji tutaj).

Test

Poniższy test symuluje pseudo panel administracyjny. Ów panel pozwala wyłączyć tymczasowo logowanie dla użytkowników, np. na czas konserwacji systemu.

Zapytanie szukające użytkownika oraz istotna zmienna w naszym teście - $loginEnabled:

$userSelectQuery = 'SELECT id FROM user WHERE email = :email AND password = :password AND is_active = 1';
$loginEnabled = false;

Pierwszy przypadek testu:

$user = selectDb($pdo, $userSelectQuery, [
  ':email' => 'admin@example.com',
  ':password' => hash('sha256', 'admin'),
]);
if(sizeof($user) && $loginEnabled) {
} else {
}

Oraz drugi:

if($loginEnabled) {
  $user = selectDb($pdo, $userSelectQuery, [
    ':email' => 'admin@example.com',
    ':password' => hash('sha256', 'admin'),
  ]);
  if(sizeof($user)) {
  } else {
  }
} else {
}

Wyniki

Działanie tego kodu jest dokładnie takie samo. Jedyna różnica to kolejność. W pierwszym przypadku szukamy użytkownika w bazie danych i sprawdzamy czy można się zalogować do systemu. W drugim najpierw sprawdzamy czy można się logować i dopiero jeśli ten warunek jest spełniony, próbujemy znależć użytkownika. Teraz kwestia najistotniejsza - ile to zabiera czasu. Testy nie pozostawiają złudzeń:

$ php5 test-if.php 10000
[2017-09-11 12:38:43] Start
[2017-09-11 12:38:45] Condition 1 time: 1.9276778697968
[2017-09-11 12:38:45] Condition 2 time: 0.0022809505462646
[2017-09-11 12:38:45] Finish

$ php7 test-if.php 10000
[2017-09-11 12:38:50] Start
[2017-09-11 12:38:52] Condition 1 time: 1.7202689647675
[2017-09-11 12:38:52] Condition 2 time: 0.0012381076812744
[2017-09-11 12:38:52] Finish

Podsumowanie

Test został wykonany dziesięć tysięcy razy. PHP w wersji 5 potrzebował 8750% więcej czasu, żeby zupełnie niepotrzebnie pobrać informacje z bazy danych i dopiero później sprawdzić czy to pobranie danych miało jakikolwiek sens. W drugim przypadku test wykonał się błyskawicznie - wystarczyło minimalnie zmodyfikować logikę w kodzie. W praktyce można również zauważyć realny wzrost wydajność w PHP w wersji 7. Ten sam kod został wykonany prawie 2 razy szybciej (przypadek 2).

Na pewno wielu z czytających stwierdzi, że różnice w czasie są tak znikome przy takiej ilości testów i że to nie ma znaczenia. Przy jednym uruchomieniu takiego kodu, różnica faktycznie nie będzie widoczna - wynosi ona dziesięciotysięczne części sekundy. Przykład ten nie powinien być rozpatrywany w przypadku prywatnej strony czy bloga. Taka optymalizacja nabiera sensu przy dużych aplikacjach, gdzie jednocześnie pracuje wielu użytkowników i każda tego typu zmiana jest na wagę złota.

Jednak czy nie na tym polega rozwój, żeby pisać coraz lepszy i coraz wydajniejszy kod?


Przejdź do strony głównej


Komentarze

Brak komentarzy

Dodaj komentarz







© 2017 Łukasz Gogołkiewicz. Wszelkie prawa zastrzeżone.