Przypomnę argumentację twórców i wielu programistów, dlaczego stałe klasowe mają być szybsze. Jak wiadomo, stałych globalnych jest dość dużo i są one zgromadzone w pamięci interpretera w jednej ogromnej strukturze danych. Przy każdym wywołaniu PHP musi zajrzeć do tej tablicy, odnaleźć stałą i pobrać jej wartość. W stałych klasowych proces odszukiwania podzielony jest na dwie części. Na początku odnajdujemy odpowiednią klasę, a później patrzymy w jej zbiorze stałych. Zbiór ten jest znacznie mniejszy, niż globalna pamięć, dlatego jego przeszukiwanie ma trwać krócej. Przekonajmy się, jak jest w istocie.
Wykonałem łącznie cztery testy, używając różnych technik liczenia czasu. W trzech z nich pomiar robiony był zwykłą funkcją microtime(), jeden został sprawdzony Apache Benchem. Zarówno pełne wyniki, jak i kod testów zamieszczam w załączniku. W testach 1, 3 i 4 najgorszy zmierzony rezultat był odrzucany.
Test 1
Jest to prosty test znaleziony na jakimś forum dyskusyjnym, gdzie autor zachwalał wydajność stałych klasowych i załączył stosowny kod demonstracyjny. Pomiar czasu robiony jest tutaj zwykłym microtime(), po czym zapisywany jest do pliku tekstowego. Skrypt miał zadeklarować dziesięć stałych i każdą z nich wyświetlić jedna, po drugiej. Pomiar powtarzano 25 razy. Wyniki uśrednione:
- Stałe globalne: t = 0,02612 s; u(t) = 0,00045
- Stałe klasowe: t = 0,01183 s; u(t) = 0,00011
Tutaj stałe klasowe faktycznie wypadają znacznie lepiej.
Test 2
Test mojego autorstwa. Skrypt miał zadeklarować 30 stałych, po czym wyświetlić 5 z nich 1000 razy. Tak duża ilość iteracji została wprowadzona pod kątem Apache Bencha, który bez tego działał bardziej jak generator liczb pseudolosowych, a nie wyników :). Parametry wywołania: -n 700 -c 100. Pomiar powtórzono 10 razy. Mierzony był średni czas przetworzenia żądania (w milisekundach):
- Stałe globalne: t = 1,5773 ms; u(t) = 0,0093 ms
- Stałe klasowe: t = 1,7456 ms; u(t) = 0,0080 ms
Niespodzianka, stałe globalne okazały się szybsze.
Test 3
Zaintrygowany wynikami testu numer 2, postanowiłem zmierzyć kod z testu 2 funkcją microtime(). Jedyna modyfikacja to usunięcie 1000-iteracyjnej pętli. Pomiary identycznie, jak w teście 1.
- Stałe globalne: t = 0,0485 s; u(t) = 0,0027 s
- Stałe klasowe: t = 0,01419 s; u(t) = 0,00017 s
Stałe klasowe znowu lepsze. Zacząłem już podejrzewać, o co tu chodzi. Dla pewności więc zrobiłem jeszcze jeden test...
Test 4
Jest to test numer 3, w którym przywróciłem z powrotem pętlę z tysiącem iteracji tak, że te pięć stałych miało pokazać się 1000 razy. Pomiary identycznie, jak w testach 1 i 3.
- Stałe globalne: t = 2,6797 s; u(t) = 0,0080 s
- Stałe klasowe: t = 3,0501 s; u(t) = 0,0059 s
Stałe globalne znów wygrały.
Interpretacja
Prawda o zwiększonej wydajności stałych klasowych jest tylko połowiczna. Potwierdzam, że w części sytuacji ich użycie przyspiesza kod, ale paradoksalnie nie z powodów podanych przez twórców. Zauważmy, że wszędzie tam, gdzie wygrały stałe klasowe, występowała dość mała ilość odwołań do stałych: w teście 1 było ich 10, w teście 3 zaledwie 5, za to pojawiło się tam aż 30 deklaracji. Wychodzi na to, że najzwyczajniej w świecie stałe klasowe się znacznie szybciej inicjują (bardzo możliwe, że robione jest to częściowo już w momencie kompilacji, ale trzeba by przeanalizować źródła, by to potwierdzić).
Między bajki można natomiast włożyć opowieści, że stałe klasowe są szybsze, ponieważ wewnętrzna przestrzeń klasy jest znacznie mniejsza, a przez to łatwiejsza do przeszukania. Moja interpretacja jest następująca:
- Najpierw trzeba odnaleźć samą klasę, a tych też jest trochę.
- PHP do przechowywania różnych zbiorów wykorzystuje szeroko tablice z haszowaniem. Z tego, co pamiętam, w momencie inicjacji można określić ilość kubełków, wybierając od bardzo małej do ogromnej. Średni czas dostępu jest proporcjonalny do średniej ilości rekordów w jednym kubełku. Jeśli tablica na stałe globalne ma np. 3000 kubełków, a stałych jest wszystkich 2000, można się spodziewać, że dostawać się będziemy do nich w czasie jednostkowym. Tak więc albo twórcy źle dobrali rozmiary, albo wręcz zrezygnowali w tym miejscu z tablic z haszowaniem.
Zwróćmy jednak uwagę, że w większości skryptów zadeklarowane są setki stałych, a w praktyce podczas całego przetwarzania żądania korzysta się tylko z kilkudziesięciu z nich. Ba - często jest zbiór np. 40 stałych reprezentujących możliwe ustawienia, a programista ma z niego w jednym miejscu wybrać jedną i o reszcie może zapomnieć. Tak więc tutaj znacznie szybsza kompilacja będzie atutem, który nie tylko zniweluje narzut czasowy związany z odwołaniami, ale też w w ogólności przyspieszy całość.















Napisał KODON w wtorek, 17 czerwca 2008 o 16:53
Odczyt elementów znajdujących się wewnątrz klas jest wolniejszy, więc wynik do przewidzenia. :) Twórcy PHP powinni zoptymalizować OOP oraz czas wywołania funkcji i metod. Może w PHP 6 coś się zmieni.
Przydałby się pomiar szybkości wyświetlania stron zakodowanych w UTF-8 oraz ISO-8859-2 przez przeglądarki. Może ktoś trafił na taki w sieci Webb?