poniedziałek, 10 marca 2014

Pętla for

Dziś na warsztat trafiła pętla for. Istnieje kilka legend na jej temat. Standardowo implementuje się ją według sposobu pierwszego. Natomiast wiele źródeł wskazuje, by w warunku zakończenia pętli nie umieszczać wywołań jakiejś funkcji, ponieważ wywoływanie jej po każdym obiegu pętli może ją znacząco spowolnić. Zamiast tego powinniśmy obliczać warunek stopu na zewnątrz pętli jak pokazuje sposób drugi. Można również obliczać warunek stopu w instrukcji inicjalizującej i zamiast inkrementacji skorzystać z dekrementacji, czyli sposób trzeci. Jest to trochę mało intuicyjne i zawsze zastanawiam się czy pętla wykona się dokładnie tyle razy ile powinna. Ale dzięki temu nie tworzymy ani dodatkowej zmiennej, ani nie wywołujemy żadnej funkcji w warunku stopu.

Ale, ale, to nie wszystko. PHP może być zoptymalizowane pod kątem funkcji wbudowanych w PHP, a raczej funkcje wbudowane mogą być bardzo dobrze zoptymalizowane. Jak zmienią się wyniki testów, jeżeli funkcja w warunku stopu będzie faktycznie czasochłonna? Teoretycznie PHP mógłby wykazać się nieco inteligencją i zamieniać pętle ze sposobu pierwszego na sposób trzeci.

Żeby to sprawdzić, wykonujemy te same testy dla wszystkich powyższych sposobów z użyciem powolnej wersji funkcji count.

Kod

Funkcja testująca dostępna tutaj: benchmark.php

Wyniki

Okazuje się, że już dla wbudowanej funkcji count. różnice są zauważalne. Zwróćcie szczególną uwagę na to, że tablica ma tylko 1000 elementów. Zatem w tym przypadku warto wyrobić sobie nawyk tworzenia optymalnych pętli i tworzyć je najlepiej według sposobu trzeciego. Sposób pierwszy też nie jest zły w porównaniu do sposobu trzeciego, jednak dodatkowa zmienna nie jest zbyt elegancka, a w dodatku podnosi czas wykonywania pętli.

sobota, 8 marca 2014

Implementacje Singleton-u

Mimo, że jego stosowanie nie jest do końca poprawne, to korzysta z niego praktycznie każdy znany mi programista. Mowa o wzorcu projektowym Singleton. Możemy go zaimplementować na wiele sposobów, ale który jest najszybszy?

Klasa Singleton1 to najczęściej spotykana implementacja - jest przejrzysta i logiczna, natomiast klasa Singleton2 to jej jeszcze bardziej elegancka wersja (niestety rzadko spotykana). Często spotyka się implementację podobną do Singleton3. Na oko wygląda na szybszą z powodu użycia operatora ternarnego, który, w moim subiektywnym odczuciu, nieco zaburza czytelność kodu w tym wypadku. Ostatnia implementacja umożliwia dziedziczenie po klasie Singleton4 z zachowaniem jej funkcjonalności, niestety kosztem złożoności kodu.

Kod

Funkcja testująca dostępna tutaj: benchmark.php

Wyniki

Wyniki są zaskakujące, ponieważ najszybsza okazuje się implementacja z if-em! Ale koniecznie z lokalnie zdefiniowaną zmienną statyczną. Lecz jest ona szybsza od najszybszej wersji z operatorem ternarnym, która jest powszechnie uznawana za szybszą, tylko o niecały procent. Jej wolniejsza wersja również jest szybsza.

A zatem obaliliśmy mit o wyższości operatora ternarnego nad if-em w tym przypadku. Dodatkowo pokazaliśmy, że spadek wydajności nie tkwi wyłącznie w sposobie sprawdzania wartości zmiennej instance, ale również w miejscu deklaracji tej zmiennej.

PS. W deklaracjach klas świadomie pominąłem inne ważne elementy implementacji wzorca Singleton (jak chociażby prywatny konstruktor i metoda clone itp.).