|
|
Webcity.pl | Artykuły
Optymalizacja skryptów w PHP
Autor: Zyx
|
Umiejętność programowania to nie tylko wiedza o tym, że w danym języku jest to a to, że zawiera takie, a takie funkcje. Umiejętność programowania to umiejętność wykorzystywania możliwości języka do tworzenia algorytmów oraz wiedza, jak wszystko zoptymalizować, by działało z przyzwoitą prędkością.
Optymalizacja narodziła się dawno temu, gdy komputery miały wielość domów, a szybkością dorównywały dzisiejszym kalkulatorom :). Wtedy też jak najmniejsze programy były na wagę złota - programiści wypracowywali więc techniki, a także odkrywali algorytmy, dzięki którym komputer wykonywał to samo zadanie w krótszym czasie. Wbrew pozorom, tamte doświadczenia oraz sama idea optymalizowania przetrwała do dziś, pomimo olbrzymiego wzrostu mocy obliczeniowej komputerów. Dlaczego? Ano gdyż wraz ze wzrostem szybkości procesorów, wzrastała też złożoność aplikacji.
Umiejętność przyspieszania swoich skryptów jest niezwykle ważna w dynamicznych serwisach WWW, szczególnie tych pracujących przy dużym obciążeniu serwera. Mniejszy czas wykonywania oznacza, że serwer może obsłużyć w tym samym czasie większą liczbę internautów, którzy dodatkowo otrzymują interesujące ich treści w krótszym czasie.
PHP jest obecnie bardzo popularnym językiem do tworzenia stron WWW, jednakże wielu jego programistów po prostu przyszło, że tak powiem, z ulicy, przeczytało jakiś kurs i już myślą, że są "profesjonalistami". A że kursy i większość materiałów w polskim Internecie nie porusza problemu optymalizacji, postanowiłem zapchać tę lukę i zebrać w tym oto tekście moje doświadczenia odnośnie problemu.
Co będzie potrzebne?
Przede wszystkim: zaopatrz się w jakiś skrypt, moduł, cokolwiek, co będzie w stanie policzyć czas, w jakim wykonuje się twój skrypt. Napisać coś takiego nie jest trudno - po szczegóły odsyłam do artykułu "Czas generowania strony w PHP" autorstwa Drakuli. Jednak ze skryptem PHP liczącym czas wykonania skryptu PHP jest taki problem, iż pomiar jest lekko niedokładny. Na to jest lekarstwo - z internetu można ściągnąć wiele modułów wyposażonych w zaawansowane narzędzia pomocne przy optymalizacji i debugowaniu aplikacji PHP. Jedną z nich jest [[XDebug]] ( http://xdebug.org). Został on stworzony dla PHP 4, aczkolwiek wersje dla PHP 5 są już w trakcie tworzenia. Instalacja jest dziecinnie prosta. Ściągamy DLL, kopiujemy go do katalogu z modułami, następnie otwieramy plik php.ini i dodajemy linijkę:
zend_extension_ts = php_xdebug.dll |
Jeśli nie korzystasz z PHP w trybie wielowątkowym, użyj dyrektywy zend_extension.
W systemach Unixowych procedura jest o wiele bardziej skomplikowana, gdyż nie jest to standardowy moduł PHP. Oto instrukcja krok po kroku (tłumaczenie dokumentacji XDebug):
Rozpakuj archiwum: tar -xzf xdebug-1.x.x.tgz, niekoniecznie do katalogu ze źródłami PHP.
cd xdebug-1.x.x
Uruchom phpize (lub /sciezka/do/phpize, gdy w aktualnej ścieżce nie ma tego programu). Sprawdź w tabelce, jakie wartości powinien zwrócić dla poszczególnych wersji PHP.
./configure --enable-xdebug (lub ./configure --enable-xdebug --with-php-config=/sciezka/do/php-config, jeśli program php-config nie znajduje się w aktualnej ścieżce).
make
cp modules/xdebug.so /gdzie/chcesz/to/miec
Dodaj do php.ini:
zend_extension_ts="/lokalizacja/xdebug.so" |
Jeśli nie korzystasz z PHP w trybie wielowątkowym, użyj dyrektywy zend_extension.
Uruchom ponownie serwer.
Wywołaj skrypt z funkcją phpinfo() - jeśli w wyniku znajdzie się informacja o XDebug, moduł został poprawnie zainstalowany.
Mierzenie czasu wykonywania skryptu jest niezwykle proste. Posłuży nam do tego funkcja xdebug_time_index(), zwracająca tenże czas:
<?php
$i = sqrt(8 + 5);
echo xdebug_time_index();
?> |
Biblioteka XDebug ma potężne możliwości. Możesz się z nimi zapoznać w przejrzyście napisanej i popartej mnóstwem przykładów dokumentacji dostępnej na stronie modułu.
Wydajność można mierzyć także dołączonym do każdego serwera Apache programem ab. Teoretycznie został on zaprojektowany do testowania wydajności serwera i łącza, ale znakomicie nadaje się także do robienia benchmarków skryptów. Znajduje się on w katalogu "bin", a jego użycie jest niezwykle proste. Otwieramy wiersz poleceń, wchodzimy do wspomnianego katalogu i wpisujemy:
ab.exe -n 500 http://localhost/nasz_skrypt.php |
Program działa na takiej zasadzie, że wysyła pod podany adres określoną w parametrze -n ilość żądań HTTP. Stara się to zrobić w jak najkrótszym czasie, symulując maksymalne przeciążenie serwera. Po zakończeniu prób otrzymujemy kilka ważnych informacji:
ab -n 500 http://localhost/skrypt.php
This is ApacheBench, Version 2.0.41-dev <$Revision: 1.121.2.12 $> apache-2.0
Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Finished 500 requests
Server Software: Apache/2.0.54
Server Hostname: localhost
Server Port: 80
Document Path: /skrypt.php
Document Length: 127 bytes
Concurrency Level: 1
Time taken for tests: 19.359375 seconds
Complete requests: 500
Failed requests: 0
Write errors: 0
Total transferred: 167000 bytes
HTML transferred: 63500 bytes
Requests per second: 25.83 [#/sec] (mean)
Time per request: 38.719 [ms] (mean)
Time per request: 38.719 [ms] (mean, across all concurrent requests)
Transfer rate: 8.42 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 3 12.0 0 140
Processing: 15 34 8.1 31 62
Waiting: 15 31 8.7 31 62
Total: 15 37 13.9 31 171
Percentage of the requests served within a certain time (ms)
50% 31
66% 46
75% 46
80% 46
90% 46
95% 46
98% 62
99% 125
100% 171 (longest request) |
Oto opis co ciekawszych danych:
Document length - średnia długość otrzymywanego dokumentu
Time taken for tests - łączny czas obsługi wszystkich wysłanych żądań
Requests per seconds - ile żądań na sekundę udało się przetworzyć
Time per request - czas obsługi pojedynczego żądania
Transfer rate - średni transfer
Optymalizacja
Wszystko, co powinieneś wiedzieć o przyspieszaniu skryptów PHP, zebrałem w formie punktów.
Wyrażenia regularne Perla są w 90% przypadków znacznie szybsze od tych określanych mianem "kompatybilnych z POSIX". Ponadto mają znacznie większe możliwości. Poznać je można po prefiksie funkcji preg_, np. preg_match(). Mnóstwo informacji o nich, a także o samej składni wyrażeń można znaleźć w dokumentacji PHP: http://www.php.net/manual/en/ref.pcre.php
Poznaj funkcje PHP - wiele rzeczy, które zwykle pracowicie kodujesz przy pomocy PHP, da się zastąpić jedną funkcją. Takie rozwiązanie nie dość, że jest przejrzystsze, to szybsze - funkcja wbudowana w parser, czyli funkcja skompilowana zawsze wykona się prędzej od kodu interpretowanego.
Dostosuj rozwiązanie do rangi problemu - jeśli potrzebujesz zwyczajnie zamienić jeden ciąg tekstu, np. "aaa" na inny (niech będzie, że "bbb"), użyj str_replace() i nie zaprzęgaj do tak prostej rzeczy wyrażeń regularnych. Poradę tę można zastosować nie tylko w sensie do pojedynczych, małych kawałków kodu, ale i do całych partii aplikacji (to jednak wymaga zdolności planowania oraz trochę wiedzy natury technicznej). Załóżmy, że piszesz aplikację WWW w PHP i pragniesz, aby mogła ona działać na różnych bazach danych. To wymaga stworzenia sterowników dla każdej z baz, dzięki którym ujednolicisz ich API (Interfejs programowania - do wykonania zapytania będzie służyć ta sama funkcja bez wzgędu na to, z jaką pracujesz bazą). I tu zaczynają się schody. Możesz bowiem wybrać bibliotekę ADODB dostarczającą rozbudowane API oraz aktualne sterowniki do obsługi mnóstwa systemów bazodanowych. Nie będziesz wtedy marnować czasu na pisanie wszystkiego samodzielnie, a zyskujesz narzędzie o nieprawdopodobnych możliwościach i funkcjonalności. Złożony kod sprawia jednak, iż PHP dostaje poważnej zadyszki przy najprostszym nawet zapytaniu! Musisz ustalić priorytet - czy większy nacisk położysz na funkcjonalność, wybierając ADODB, czy też zdecydujesz się na stworzenie czegoś w pełni dopasowanego do potrzeb, tracąc na możliwościach, zyskując jednak na szybkości (wiadomo, iż kod będzie uproszczony, zaś samo stworzenie sterownika nie jest aż tak trudne). Podobnie sprawa ma się z parserami szablonów. Do wyboru mamy Smarty, czyli potężna funkcjonalność, która jednak czasem niepotrzebnie obciąża system; nasz własny parser - wymaga dość sporego doświadczenia. Niekoniecznie uzyskamy żądaną funkcjonalność, ale całość w teorii będzie chodzić bardzo szybko i będzie dopasowana do potrzeb. Wszystko zależy od oczekiwań...
Nie przesadzaj z ilością parametrów w funkcjach, ponieważ więcej parametrów = więcej czasu potrzebnego parserowi na ich przetworzenie. Przeanalizujmy jeden przykład - chcemy dokładnie opisać stan jakiegoś elementu i napisaliśmy do jego ustawiania funkcję:
function ustaw($czy_bla, $czy_ble, $czy_bu, $czy_muuu, $czy_alala, $czy_uziebuzie){
...
}
ustaw(0,0,1,0,1,1); |
Czy warto wprowadzać parametry "0"? Nie... ale jak w takim razie to zrobić? Z pomocą przyjdą nam operatory do operowania na bitach. Cała sztuka polega na zdefiniowaniu sobie grupy stałych, których wartościami będą kolejne potęgi dwójki:
define('BLA', 1);
define('BLE', 2);
define('BU', 4);
define('MUUU', 8);
define('ALALA', 16);
define('UZIEBUZIE', 32); |
Teraz funkcja - jeden parametr oraz łatwość sprawdzenia, czy dana rzecz jest ustawiona:
function ustaw($co){
if($co & BLA){
//obsluga BLA
}
if($co & BLE){
//obsluga BLE
}
if($co & BU){
//obsluga BU
}
//itd.
}
ustaw(BLE | MUUU | UZIEBUZIE); |
No i proszę - jeden parametr, a w dodatku nie przesyłamy jakichś niepotrzebnych śmieci.
Stosuj referencje, szczególnie przy dużych tekstach i tablicach wprowadzanych do funkcji. Referencja powoduje, iż do ich wnętrza trafią tylko odnośniki do właściwego tekstu, a nie jego kopie. Pomijamy więc jedną, bardzo czasochłonną operację, tym samym przyspieszając skrypt:
function parsuj(&$text){
$text = preg_replace('wzor', 'wynik', $text);
} // end parsuj();
$tekst = 'tu jest bardzo dlugi tekst, np. ten artykul';
parsuj($tekst); // tekst nie jest kopiowany do funkcji, a wiec wszystko odbywa sie blyskawicznie
echo $tekst; |
Wadą referencji jest to, że nie można w ich miejsce podstawiać stałych wartości, aczkolwiek w takim przypadku to nie powinna być przeszkoda - tak duże ilości danych zwykle są wcześniej pobierane z jakiegoś miejsca, więc chcąc nie chcąc, w zmiennej znaleźć się muszą.
Nie używaj zmiennych tymczasowych bez powodu, gdy to nie jest konieczne. Jest to najgłupsza rzecz, jaką można sobie wyobrazić w programowaniu, a niestety wiele kursów uczy właśnie tak programować. Oto przykładowy kod:
// Pierwszy blad
$sql = 'SELECT * FROM news';
$r = mysql_query($sql);
while($row = mysql_fetch_row($r)){
// Drugi blad
$news_id = $row[0];
$news_title = $row[1];
$news_time = $row[2];
$news_body = $row[3];
// Trzeci blad
$czas = date('d.m.Y H:i',$news_time);
$tresc = nl2br($news_body);
$tresc_d = stripslashes($tresc);
echo 'tu wyswietlamy: '.$news_id.' ('.$news_title.', '.$czas.') '.$tresc_d.'<br/><br/>';
} |
Śmiejesz się, że to jest fikcja literacka? Niestety - to prawda. Takie, a czasem nawet znacznie bardziej "rozbudowane" przykłady bez trudu można znaleźć na forach dyskusyjnych. Przeanalizujmy go sobie - na początku tworzymy zmienną tymczasową dla zapytania SQL, pomimo iż to samo zapytanie możemy bez problemu wprowadzić od razu do funkcji mysql_query(). Niektórzy tu mogą się sprzeczać, że takie coś jest przydatne przy wyszukiwaniu błędów w zapytaniu, z czym się nie zgodzę. Jeżeli widzę, że zapytanie nie działa, tak jak powinno, wtedy kopiuję je raz jeszcze i wyświetlam, by widzieć, co ten PHP mi w ogóle wysyła. Ale błąd naprawię - kod usuwam, bo po co mi on dalej? Zapytanie jest przecież już w porządku. Idźmy dalej - "błąd drugi"... to kapituluje nawet logika, gdyż to, co tu jest robione przypomina pakowanie ubrań do jednej torby tylko po to, by zaraz je przepakować do drugiej. Gdyby ktoś tak właśnie pakował zawartość szafy, miałby pewnie darmowe wakacje w szpitalu psychiatrycznym :). Ludzie - przecież możemy operować na $row[0], $row[1] itd!
Błąd trzeci związany jest pewnie z przeświadczeniem, że gdyby napisać np.
$news_body = nl2br($news_body); |
Powstałyby problemy, gdyż zapisujemy do tej samej zmiennej, z której czytamy. Tak nie jest - kto nie wierzy, może sprawdzić doświadczalnie :).
Odnośnie błędu trzeciego dodam, iż przecież funkcje do przetwarzania danych możemy wrzucić bezpośrednio do generacji ciągu HTML i nie kopiować tego z powrotem do zmiennej. Po przeróbkach wszystko będzie wyglądać tak:
$r = mysql_query('SELECT * FROM news');
while($row = mysql_fetch_row($r)){
echo 'tu wyswietlamy: '.$row[0].' ('.$row[1].', '.date('d.m.Y H:i',$row[2]).') '.stripslashes(nl2br($row[3])).'<br/><br/>';
} |
Oczywiście nie neguję tu faktu, że zmienne tymczasowe mogą się przydać przy optymalizacji. Szczególnie widać to w przypadku "buforowania" wyników różnych czasochłonnych obliczeń, które muszą być użyte wiele razy. Przykład czegoś takiego wyjątkowo nie będzie w PHP, a w C++ (choć bez kodu). Otóż pisałem sobie swego czasu silnik do obsługi rzutu izometrycznego i w pewnym miejscu musiałem dość często korzystać wartości jakiegoś sinusa, cosinusa albo pierwiastka (już nie pamiętam). W każdym razie - funkcje liczące to są bardzo czasożerne, stąd też wywołałem je raz podczas inicjacji programu dla potrzebnych mi wartości, wynik zapisałem w zmiennych tymczasowych i z nich właśnie go brałem w pętli głównej.
Jaki z tego morał? Ze zmiennymi należy postępować ostrożnie i nauczyć się wybierać, gdzie się one przydadzą, a gdzie nie. Bowiem czasem są użyteczne, a innym razem niepotrzebnie spowalniają skrypt (dodam, iż tworzenie zmiennych jest wiele razy wolniejsze w dynamicznym PHP, niż w językach kompilowanych).
Nie twórz funkcji bez żadnego konkretnego powodu - odwołanie doń jest wolniejsze, niż wykonanie ich kodu wklejonego w dane miejsce. Oto dwa kody realizujące to samo zadanie. Czas ich wykonania jest mierzony przez bibliotekę XDebug na PHP 4.3.4, systemie Windows XP na procku AMD Athlon XP 1700+. Kod pierwszy - pętla bez funkcji:
<?php
for($i = 0; $i < 20000; $i++){
$value += (cos(time() % 360));
}
echo 'Czas generacji: '.xdebug_time_index().'<br />';
?> |
Kod drugi - z funkcją przetwarzającą:
<?php
function oblicz($okres){
return cos(time()%$okres);
}
for($i = 0; $i < 20000; $i++){
$value += oblicz(360);
}
echo 'Czas generacji: '.xdebug_time_index().'<br />';
?> |
Oto chwila prawdy, ogłaszamy wyniki. Kod pierwszy - 0.1977481842041 s, kod drugi - 0.97888588905334 s! Zobacz - jedna głupia funkcja, a szybkość spadła prawie pięciokrotnie. Kto jest trochę obyty z kodem źródłowym parsera PHP, może sprawdzić, czemu tak się dzieje - a jeśli już by sprawdził, proszę o pilny kontakt :).
No właśnie... funkcje. Funkcje spowalniają, natomiast ja w najlepsze w innych artykułach głoszę, jak bardzo ułatwia życie stworzenie API opartego na programowaniu obiektowym, funkcjach itd. Dlaczego? Ponieważ życie to sztuka kompromisów. Może i to nieco spowalnia kod (akurat jeśli zmierzymy czas dla tylko jednego wywołania powyższego obliczenia, różnice będą naprawdę minimalne) - ale jest po prostu niezbędne. Dobrze zaprojektowany i przyjazny programiście kod też jest ważny, by przyspieszyć proces tworzenia aplikacji. Ponadto poprawienie ewentualnych błędów w kodzie nie będzie aż takie trudne - zmieniamy kod w jednym miejscu, a zmiany widoczne są na wszystkich tworzonych podstronach. Znów oszczędzamy więc czas. Lecz jeszcze raz podkreślam: uważaj, aby nie przedobrzyć, gdyż możesz nie dość, że spowolnić, to nawet utrudnić prace nad serwisem przez nieodpowiednio zaprojektowane biblioteki!
Wykorzystuj możliwości baz danych. Bazy danych mają chyba największy wpływ na czas generowania kodu wynikowego HTML. Każde zapytanie oznacza olbrzymią ilość pracy dla mnóstwa rozmaitych komponentów, dlatego najważniejsze jest, aby ograniczyć ich ilość do absolutnego minimum. Pomaga tu wyższy stopień wtajemniczenia w język SQL służący do komunikowania się z większością baz :). Ma on tak potężne możliwości, że bez problemu możemy pobrać "w jednym rzucie" mnóstwo danych z wielu tabel naraz. Ponadto dzięki znajomości wbudowanych funkcji oraz operatorów, część operacji przetwarzania można przeprowadzić jeszcze na poziomie bazy. W dodatku takie obrobione dane da się wykorzystać w części warunkowej, w klauzulach sortowania. Oto przykład: chcemy pobrać teksty posortowane według ich długości. Wcale nie musimy się z tym męczyć przy pomocy PHP - MySQL dostarcza nam funkcję LENGTH():
SELECT tekst_id, tekst FROM teksty ORDER BY LENGTH(tekst) |
Osoby zainteresowane tego typu tajnikami języka SQL odsyłam do artykułu "MySQL w praktyce".
Jednak budowanie skomplikowanych zapytań to nie jedyna droga do zmniejszenia ich liczby. Mamy możliwość zrealizowania tego w inny sposób. Przyjrzyj się kodowi i zastanów się, które dane są warte tego, by je trzymać w bazie. Za przykład mogą posłużyć informacje o szablonach - w końcu podczas każdego uruchomienia strony potrzebny jest Ci tylko jeden, konkretny rekord. Dlaczego więc nie zapakować informacji do pliku jako zserializowanych tablic PHP, a sam szablon identyfikować po nazwie katalogu? Odpada jedno zapytanie, a dochodzi większa szybkość - odczyt ww. tablic z plików jest kilkanaście razy szybszy, niż odczyt rekordów z bazy. Tablice można także wykorzystać do tzw. cache'owania rzadko ulegających zmianom wyników zapytań. Polega to na tym, iż zapytanie wykonuje tylko pierwszy użytkownik wchodzący na stronę. Jego wyniki zapisywane są do pliku w postaci zserializowanej tablicy. U następnych internautów rekordy wczytywane są właśnie stamtąd, a samo zapytanie w ogóle nie jest wysyłane. Jednak taki sposób jest skuteczny tylko do danych nie zmieniających się zbyt często, np. listy artykułów. Dokładniejsze informacje o tym, jak zbudować własny mechanizm cache'owania, znajdują się w artykule "Cachowanie zapytań SQL w PHP".
Czasami liczbę zapytań można zredukować w jeszcze inny sposób. Nie kasujemy tu ani jednego z nich, nie przerzucamy jego obsługi na inny mechanizm (np. pliki tekstowe). Po prostu przenosimy zapytanie w inne miejsce. Można tak zrobić np. w mechaniźmie sesji - po co mamy kasować rekordy o tych przestarzałych za każdym razem? Dodajmy do zapytania pobierającego dane o sesji zabezpieczenie:
SELECT * FROM sesja WHERE id = 'jakiś_id' AND czas > (UNIX_TIMESTAMP() - 3600) |
Zaś tabelę czyśćmy wyłącznie podczas tworzenia sesji. Dzięki temu procesem tym internauci będą obciążeni podczas wejścia na stronę, natomiast podczas jej zwiedzania już nie. I znów liczba zapytań uległa zmniejszeniu.
Optymalizacja obsługi bazy nie ogranicza się jedynie do operacji na zapytaniach. Odpowiednio projektując strukturę bazy, da się również zwiększyć szybkość. Najpierw - sam typ tabeli. Taki MySQL dostarcza ich pięć rodzajów. Różnią się one między sobą czasem dostępu, liczbą obsługiwanych rzeczy, a także mechanizmem blokowania dostępu podczas aktualizacji danych. Warto przyjrzeć się również indeksom - kopiom kolumny w tabeli, tyle że z posortowanymi danymi. Nałożenie ich na odpowiednie kolumny również przyspieszy wyszukiwanie informacji. Temat ten jest bardzo rozległy, a jako, że dotyczy on już wyłącznie bazy danych (zaś artykuł jest o optymalizacji PHP :)), przytoczę adres artykułu, gdzie możesz dowiedzieć się o wiele więcej na ten temat: http://www.linux-mag.com/2001-06/mysql_01.html. Warunek to znajomość języka angielskiego.
Jak skuteczne są powyższe wskazówki odnośnie baz danych? Powiem tyle - mają niezwykłą moc. Dzięki nim mój silnik napisany w PHP, pomimo dużej liczby klas, obiektów, metod i funkcji (złożone API - patrz punkt poprzedni), wykonuje się bardzo szybko. Na własne potrzeby wysyła domyślnie tylko dwa zapytania: pobranie danych o sesji, aktualizacja danych sesji :), a przecież obsługuje on bany na hosty, adresy IP, moduł, szablony, języki, poddomeny serwisu i wiele innych rzeczy. Jak to zrobiłem? Po prostu - wiele różnych danych pobieram jednym zapytaniem. Część przerzuciłem do plików. Kilka z nich przetwarzanych jest tylko w przypadku tworzenia sesji. Niektóre z zapytań cache'uję. I tak to wszystko się kręci.
Stosuj szybkie algorytmy - a do tego potrzebna jest dobra znajomość, do czego dana konstrukcja języka PHP powinna być użyta. Wiele problemów można rozwiązać na kilka sposobów - gorszych lub lepszych. Wolniejszych lub szybszych. Teoria algorytmiki również jest zbyt obszerna, jak na ten artykuł, dlatego skupię się tu na bardziej przyziemnym problemie: "if vs. switch"...
Wędrując co jakiś czas po witrynach poświęconych PHP, bardzo często trafiam na artykuły opisujące obsługę menu polegającą na wpisaniu w URL numeru strony do wywołania, np. "index.php?co=5". Oto, jaki kod jest w tym miejscu tradycyjnie prezentowany przez ww. teksty:
if($_GET['id'] == 0){
require('teksty/0.txt');
}elseif($_GET['id'] == 1){
require('teksty/1.txt');
}elseif($_GET['id'] == 2){
require('teksty/2.txt');
}elseif($_GET['id'] == 3){
require('teksty/3.txt');
}elseif($_GET['id'] == 4){
require('teksty/4.txt');
}elseif($_GET['id'] == 5){
require('teksty/5.txt');
}else{
require('teksty/error.txt');
} |
Rozwiązanie głupie, jak nieszczęście - przecież mamy specjalnie zaprojektowaną do tego celu instrukcję wyboru:
switch($_GET['id']){
case 0:
require('./teksty/0.txt');
break;
case 1:
require('./teksty/1.txt');
break;
case 2:
require('./teksty/2.txt');
break;
case 3:
require('./teksty/3.txt');
break;
case 4:
require('./teksty/4.txt');
break;
case 5:
require('./teksty/5.txt');
break;
default:
require('./teksty/error.txt');
} |
Choć w tym akurat przypadku można to zrealizować w jeszcze lepszy sposób!
// upewnij sie, ze id jest cyfra. Inaczej moze byc niebezpiecznie
if(!ctype_digit($_GET['id'])){
require('./teksty/error.txt');
die();
}
if($_GET['id'] >= 0 && $_GET['id'] < 6){
require('./teksty/'.$_GET['id'].'.txt');
}else{
require('./teksty/error.txt');
} |
No i proszę - jak łatwo uprościć, a także przyspieszyć kod, gdy się dobrze wie, co do czego służy i jak może być użyte.
Przy tej okazji chciałbym dodać, iż kluczową sprawą jest różnica między apostrofem, a cudzysłowem, i to nie tylko pod względem wyglądu, a szybkości! Zainteresowanych odsyłam do tekstu "Ciągi tekstowe w PHP", gdyż nie chce mi się jeszcze raz powtarzać tego, co tam powypisywałem :).
Uprość obliczenia matematyczne. Tak, tak, nie przewidziałeś się. Proponuję Ci przypomnieć szkolne lekcje matematyki. Jeśli kiedykolwiek zastanawiałeś się, po jakie licho uczycie się skracać równania, upraszczać równania trygonometryczne i robić inne dziwne głupoty, oto masz odpowiedź. Programowanie jest połączone z matematyką od samego początku jego istnienia. Pamiętaj, że komputer wykona obliczenia tak, jak ty je mu podasz. I nie będzie go obchodzić, że dane obliczenie można zapisać w 10 razy prostszy sposób, sto razy zmniejszając ilość koniecznych obliczeń. Po prostu wykona je i poda wynik. Ale już ciebie, jako programistę, powinno obchodzić to, czy program wykonuje swą pracę w czasie 1 sekundy, czy też 10 sekund. I dlatego też odkurz podręczniki, przerób zadania i bierz się za programowanie obliczeń tak, by nie przeciążały one zbytnio maszyny. Niektóre systemy same potrafią dokonać optymalizacji obliczeń. Baza MySQL posiada zaawansowane algorytmy, które upraszczają wszystkie wyrażenia poprzez eliminację nieistotnych konstrukcji. Są to jednak wyjątki.
Przesiądź się na PHP 5, szczególnie jeśli korzystasz z XML'a lub z programowania obiektowego]]. W nowej edycji właśnie na te dwa elementy położono największy nacisk. Zyskasz naprawdę fantastyczne możliwości, przy większej stabilności kodu i szybkości działania! Kod odpowiedzialny za OOP został napisany od początku, tym razem ze szczególną uwagą, jako że to jest podstawa tej właśnie edycji. Obsługa XML'a również przeszła metamorfozę. Przede wszystkim stworzono zupełnie nowe API (SimpleXML + DOM), znacznie wygodniejsze, a co ważniejsze - wbudowane w PHP. Za cąłokształt odpowiada nowa biblioteka - libXML2, dająca przetwarzaniu tego metajęzyka olbrzymiego "kopa". To chyba wystarczająca rekomendacja. Twój admin nie wie o PHP 5? A więc poinformuj go!
Dostosuj rozwiązanie do potrzeb - cd. Tak, to również może dać porządnego kopa. Dla poparcia tezy przytoczę tu pewną opowieść znalezioną w jednym z PHP Weekly Summary na Zend.com. Pewien facet nadesłał twórcom PHP wiadomość, że moduł DomXML chodzi strasznie wolno - przetworzenie jednego pliku o wielkości 12 MB zajmuje 11 minut, podczas gdy w innym języku skryptowym czas trwania operacji mieścił się w granicach 6 sekund. Dla niego przyczyna leżała w samym rozszerzeniu, lecz twórcy PHP od razu wskazali mu sposób ładowania tego pliku:
$file = implode("n", file('file.xml')); |
Popatrzmy - funkcja file() wczytuje plik, po czym rozbija go na poszczególne linie. Ile to czasu musi zająć przy 12-MB pliku, nie muszę wspominać. Lecz popatrz dalej - ledwo PHP uporał się z operacją rozbicia tekstu, każemy mu go... z powrotem połączyć w całość :). Przypomina to kopanie dołu o głębokości 10 metrów tylko po to, by go zaraz zasypać. Nie ma to sensu, a czasu zajmuje, że hoho. Tak też było i w tym przypadku. Bowiem zamiana tej linijki na
$file = file_get_contents('file.xml'); |
sprawiła, że "nagle" czas trwania operacji skrócił się do 3 sekund! Po prostu wyrzuciliśmy to niepotrzebne rozbijanie i scalanie pliku, które spowalniało całą operację.
Zmniejsz ilość operacji dyskowych - odczyt, zapis oraz sprawdzanie jakichkolwiek danych na twardym dysku to dość duży narzut dla systemu, dlatego należy ilość tego typu operacji rozważnie dawkować. Rozważ, czy naprawdę potrzebne jest sprawdzanie istnienia pliku w tym miejscu, czy też można bez problemu przenieść go do jakiegoś warunku, gdzie będzie wykonywany rzadziej? Buforuj wyniki. Kiedy pisałem instrukcję Include dla systemu szablonów Open Power Template, stwierdziłem, że jeden i ten sam plik może być dołączony kilka razy pod rząd. To spowodowało, że dopisałem kawałek kodu, który za pierwszym razem buforuje informację, czy dołączany szablon istnieje, a przy kolejnych korzysta właśnie z niej. Tak oto znacząco zmniejszyłem ilość wywołań funkcji file_exists(), co dało wyraźnie widoczne przyspieszenie.
Zakończenie
Uff, porad jest dużo. Jak zastosować je w praktyce? Po prostu ćwicz i staraj się, by optymalizacja stała się częścią twego stylu pisania. Wtedy wychodzący spod twych palców kod będzie już na starcie bardzo szybki, co z pewnością wpłynie zarówno na zadowolenie użytkowników serwisu, jak i Ciebie. Spróbuj, gdyż to jest naprawdę warte swej ceny. |
Powrót |
|
|