Archiwum kategorii: Optymalizacja kodu

Rzecz o tworzeniu stringów w PHP.

PHP od początku oferuje wiele różnych sposobów na wygenerowanie takiego samego stringa. Ale który z nich jest najszybszy? W tym celu przeprowadziłem serię 9 testów – każdy test wykonywał się w pętli o 10 000 000 iteracji i budował string w inny sposób:

1. $a = sprintf("ala ma kota numer %d", $i);
2. $a = "ala ma kota numer " . $i;
3. $a = 'ala ma kota numer ' . $i;
4. $a = "ala ma kota numer $i";
5. $a = sprintf("ala ma kota numer %d%c%c%c%c%c%c%c%c%c%c", $i, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL);
6. $a = "ala ma kota numer " . $i . "\n\n\n\n\n\n\n\n\n\n";
7. $a = 'ala ma kota numer ' . $i . "\n\n\n\n\n\n\n\n\n\n";
8. $a = "ala ma kota numer $i\n\n\n\n\n\n\n\n\n\n";
9. $a = sprintf("ala ma kota numer %d%s%s%s%s%s%s%s%s%s%s", $i, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL, PHP_EOL);

Oczywiście nie wyczerpałem wszystkich możliwych kombinacji, ale przypuszczam że dość dobrze przedstawiłem istotę wszystkich możliwych do wykorzystania technik. Testy przeprowadziłem na wszystkich posiadanych wersjach PHP: 5.3.9; 5.4.45; 5.5.38; 5.6.31; 7.0.22; 7.1.8; 7.2.0beta2. Wszystkie wersje mają praktycznie takie same konfiguracje, zero dodatkowych modułów – okazało się np. że xdebug potrafi nieźle spowolnić interpreter.

Porównanie czasu wykonywania poszczególnych testów jest następujące:

1

Można by się spodziewać, że skoro fukncja sprintf() jest praktycznie taka sama jest w C (w którym został napisany PHP) to będzie proste mapowanie i będzie ona najszybsza, a tymczasem jest najwolniejsza; a im więcej argumentów (testy 5 i 9) tym czas wykonania znacznie większy… Masakra.
Między testami wykorzystującymi składnię typową dla PHP nie ma większych różnic, choć jestem mocno zaskoczony faktem, że składnia „asd $zmienna” jest najszybsza dla PHP7.x – jednocześnie jest to dla mnie konstrukcja najbardziej podatna na możliwość popełnienia błędu przez programistę.

Porównanie wydajności w zależności od wersji PHP nie było zaskoczeniem:

iqsi-screenshot (14)

PHP 5.3 najwolniej, PHP 7.2 najszybciej. Widać oczywiście poprawę z wersji na wersję i gwałtowne przyspieszenie dla wersji 7.x w związku z nowym silnikiem.

Jeśli ktoś jest zainteresowany dokładnymi danymi to mogę przesłać tabelkę w excelu.

Autorem tekstu jest Łukasz Bugaj.

Optymalizacja przy pomocy blackfire.io

Optymalizacja kodu to czasochłonny i trudny temat. Przede wszystkim musimy znaleźć miejsca w aplikacji, które „pochłaniają” większość czasu procesora lub zajmują dużo pamięci. Aby to zrobić potrzebujemy narzędzia, które przygotuje nam profil wywołań metod/funkcji naszej aplikacji. W tym celu przedstawione zostanie narzędzie blackfire.io

Blackfire.io dostępne jest do pobrania z oficjalnej strony: https://blackfire.io/ Jest to narzędzie płatne, jednak istnieje opcja darmowego użytkowania, mocno ograniczona ale pozwalająca na podstawowe optymalizacje naszego kodu.

Po zainstalowaniu i sprawdzeniu poprawnego działania (wg instrukcji na stronie producenta) możemy przystąpić do stworzenia pierwszego profilu naszej aplikacji. Załóżmy, że chcemy sprawdzić co obciąża naszą stronę główną. Wywołujemy polecenie:
blackfire curl –samples=10 „http://localhost/”
Gdzie parametr ‘samples’ mówi o tym ile razy chcemy wywołać dany adres.

Po zakończeniu profilowania w konsoli wyświetli nam się krótkie podsumowanie. Nie to jest jednak najważniejsze. Przechodzimy na dashboard znajdujący się na stronie blackfire.io i wybieramy profil który powinien tam się pojawić:
AT01

Po lewej widzimy listę wywołań metod wraz z ich czasami. Listę tą możemy posortować wg czasu wywołania wliczającego podwywołania (incl.) lub wg czasu własnego wywołania (excl.) co jest bardziej dla nas interesujące ponieważ pokazuje jak długo konkretna metoda działa. Po prawej widzimy drzewo wywołań, gdzie kolorem czerwonym podkreślone są wywołania/ścieżki zajmujące najwięcej czasu żądania.

AT02
W tym przypadku widzimy, że funkcja curl_exec zajmowała średnio 3s czasu z każdego z naszych 10 pomiarów. Widzimy również że znaczna większość tego czasu poświęcona została dla GeoLocalizationService. Nie wdając się w szczegóły i zasadność rozwiązania, spróbujmy „zoptymalizować” to miejsce (dla przykładu usuwając to wywołanie :D). Robimy to i profilujemy kod ponownie:
AT03

Widzimy znaczną poprawę, curl_exec zajmuje w tym momencie tylko 250 ms. Oczywiście sama optymalizacja jest nie do końca właściwa (nie powinniśmy optymalizować przez usunięcie funkcjonalności :D), natomiast pokazuje jako przykład poprawne wykorzystanie blackfire.
Dodatkowym atutem tego narzędzia jest możliwość czytelnego pokazania zmian pomiędzy dwoma profilami:
AT04

Osobiście zachęcam do spróbowania przynajmniej raz z blackfire w celu optymalizacji. Jeżeli nie chcemy bawić się w zakładanie kont/instalowanie zewnętrznych narzędzi, dobrą alternatywą może być wbudowany w xdebug profiler (xdebuga chyba każdy ma) + dodatkowo narzędzie qcachegrind do wyświetlania profili.
Dodam, że blackfire bardzo pomógł nam w optymalizacji sklepu w ramach projektu „Struś pędziwiatr”.

Autorem tekstu jest Adam Tront