0

Czy korzystanie ze wzorców projektowych spowalnia działanie programu?

PHP - test szybkości wybranych wzorców projektowych

Głównym zadaniem wzorców projektowych w programowaniu jest tworzenie przejrzystego kodu źródłowego, który będzie łatwy w utrzymaniu, rozwijaniu i debugowaniu. Czy jednak taki kod będzie się wykonywał równie szybko jak kod bez korzystania z takich zasad? Odpowiedź na pewno nie może być jednoznaczna, bo zależy od zbyt wielu czynników… ale mimo to postaram się ją znaleźć 😉

Dużo małych plików

Chyba najważniejszą zasadą stosowaną w programowaniu jest zasada pojedynczej odpowiedzialności (Single Responsibility Principle). Nie wdając się w szczegóły polega ona na założeniu, że łatwiej programiście jest się połapać w wielu małych plikach (dodatkowo pogrupowanych w foldery/paczki), niż w małej ilości plików z olbrzymią zawartością kodu. Dodatkowo sporo wzorców projektowych zmusza nas do wyodrębnienia kolejnych metod i wrzucenia ich do jakichś innych klas (często nowych), co znowu wiąże się ze wzrostem ilości plików. Jeśli korzystamy z języków programowania, które są kompilowane (np. C++) to praktycznie nie ma to wpływu na efekt końcowy, bo i tak kompilator zbierze za nas wszystko w jeden plik wykonywalny. W przypadku języków interpretowanych już tak niekoniecznie musi być. Postanowiłem więc przetestować w języku PHP (języku interpretowanym) jaki narzut powoduje przechodzenie interpretera przez wiele plików zamiast tylko jednego, w którym byłaby zapisana cała logika. W praktyce w każdym pliku mogą być wykonywane dodatkowe operacje. Eksperyment nie ma jednak na celu oceny wydajności „czystego” kodu, a kodu napisanego we frameworku, który korzysta z wielu etapów obsługi żądania, a jedynie porównanie czy wykorzystanie wielu pośrednich plików ma jakikolwiek wpływ na końcowy czas wykonania skryptu.

Jak to przetestować?

Zasada eksperymentu jest prosta. Przeprowadzamy trzy serie prób. Seria pierwsza polega na zbadaniu czasu wykonywania skryptu, który całą logikę wykonuje w jednej metodzie i nie wykorzystuje dodatkowych klas. Druga seria uruchamia natomiast łańcuch dziesięciu klas, które wywołują się po kolei, a dopiero ostatnia wykonuje logikę (identyczną jak w pierwszej serii prób). Seria trzecia jest analogiczna jak druga (łańcuch wywołań), jednak zastosujemy tu nie 10, a 100 plików. Żeby wyniki były możliwie rzetelne wszystkie serie muszą zostać wywołane na faktycznie istniejących i różnych plikach, gdyż operacja odczytu pliku może odgrywać istotny czynnik w ostatecznym czasie trwania serii, a w przypadku wywoływania np. naprzemiennie dwóch klas istnieje szansa, że PHP będzie cachować wyniki.  Test przeprowadzę na własnym komputerze korzystając z dysku talerzowego. Czytelnikowi pozostawiam przeprowadzenie prób w innych konfiguracjach, jeśli ta go nie zadowala. Kod testu jest publicznie dostępny.

Rezultaty

Poniżej zamieszczam otrzymane przeze mnie rezultaty eksperymentu. Wynika z nich, że narzut czasowy zdecydowanie występuje, chociaż nawet przy 100 plikach nie przekroczył on ok. 25 ms. W praktyce raczej nie stosuje się, aż tak wielu wzajemnych wywołań z różnych plików, więc testy z większą ilością plików nie mają zbytnio sensu. Nie zaobserwowałem jednak różnic narzutu pamięci operacyjnej (zawsze wynosił on 2 MB).

nr  |   short (1) [ms]  |   long (10) [ms]  |   extralong (100) [ms]
------------------------------------------------------------------------
1   |   6               |   8               |   23   
2   |   11              |   15              |   27   
3   |   6               |   7               |   22   
4   |   7               |   8               |   23   
5   |   6               |   8               |   29   
6   |   7               |   11              |   23   
7   |   7               |   9               |   31   
8   |   7               |   8               |   23   
9   |   7               |   8               |   23   
10  |   6               |   7               |   24   
11  |   7               |   14              |   24   
12  |   6               |   7               |   27   
13  |   6               |   22              |   23   
14  |   6               |   7               |   23   
15  |   6               |   7               |   22   
16  |   11              |   12              |   23   
------------------------------------------------------------------------
Średnia: 7              |   9.875           |   24.375
Mediana: 6.5            |   8               |   23

Jakie wnioski? Warto korzystać z Zasady Pojedynczej Odpowiedzialności?

Jak widać, mimo że istnieje pewien narzut czasowy to nie powoduje on zbytniego opóźnienia w przypadku języka PHP i jest to ok. 5-20 ms, co nie powinno być żadnym problemem w większości zastosowań tego języka (jeśli komuś bardzo zależy na czasie i wydajności to pewnie pisałby program w C/C++, a nie w PHPie, więc nie ma co nad tym dyskutować). Tak więc pod względem szybkości wykonywania skryptu zasada pojedynczej odpowiedzialności w niczym nam nie przeszkadza i warto ją stosować. Warto jednak podkreślić, że powyższy test nie uwzględnia wielowątkowości (sytuacji, gdy w jednej chwili n procesów chce odczytać te same pliki ze skryptami). Obecnie jednak dyski są na tyle szybkie, że taka sytuacja miałaby miejsce jedynie przy bardzo obciążonych serwisach, a i wtedy nie spodziewałbym się dużych opóźnień.  Wzorce projektowe, mimo że czasem dość skomplikowane, ułatwiają jednak życie i od czasu do czasu warto z nich korzystać.

 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *