Zacznę od YAML. Poprzednio testowaliśmy tu dwa przypadki. Pierwszy to napisana w PHP biblioteka Spyc - w czystej postaci nie wypadła ona rewelacyjnie, osiągając jedynie około 250 req/s, podczas gdy wszystkie inne formaty przetwarzane przez napisane w C parsery miały przynajmniej 2700. Dla zmniejszenia liczby operacji dyskowych usunąłem z kodu biblioteki wszystkie komentarze i wkleiłem jej kod bezpośrednio do skryptu testowego (przy korzystaniu z require wydajność spadała do ok. 140 req/s). Drugi przypadek to rozbudowa kodu o cache'owanie danych w postaci zserializowanej tablicy. Do testowania, czy istnieje cache, korzystałem z funkcji file_exists, lecz pozostawiłem w pliku kod Spyca. Rezultat to około 900 req/s.
Tu nastąpiło istotne przeoczenie - powinienem jeszcze uwzględnić przypadek, ładowania Spyca tylko wtedy, gdy jest on rzeczywiście potrzebny. Kod przykładu testowego prezentuje się następująco:
<?php if(@(filemtime('./file.ydt') > filemtime('./file.yaml'))) { $info = unserialize(file_get_contents('./file.ydt')); } else { require('./spyc.php'); $spyc = new spyc; $info = $spyc -> load('./file.yaml'); file_put_contents('./file.ydt', serialize($info)); } ?>
Tak napisany skrypt uzyskuje wyniki rzędu 2500-2600 req/s. Jeśli zastąpimy dwa wywołania filemtime() przez jedno file_exists(), wydajność wzrośnie do ok. 3200 req/s, lecz musimy wtedy wynaleźć inny sposób automatycznego sprawdzania, czy oryginał pliku nie został zmieniony.
Przetestowałem także rozszerzenie ściągnięte z repozytorium PECL. Są to w zasadzie dwie dodatkowe funkcje dla PHP: syck_load() oraz syck_dump(). Instalacja w Arch Linuksie była bardzo prosta:
# yaourt -Sy syck # pecl install syck-0.9.1 # echo "extension=syck.so" >> /etc/php.ini # /etc/rc.d/httpd restart
Moduł musiał wczytać ten sam plik konfiguracyjny, co inne. Udało mu się wykonać około 2700 req/s. Dodając cache'owanie można ten rezultat dalej poprawić.
Na prośbę Splatcha przyjrzałem się też dokładniej różnym sposobom przetwarzania XML-a w samym PHP. Przypomnę, że użyty wczoraj SimpleXML osiągnął wynik ok. 2800 req/s. Dziś sprawdziłem rozszerzenie DOM, które parsowało identyczny plik i jego rezultat to ok. 2700 req/s. Różnica nieduża, ale zauważalna podczas uruchamiania procedur testowych. Jednak to nie wszystko. PHP przechowuje dane wczytane z plików XML w formie drzewa obiektów, z którego trzeba je wyciągać na różne sposoby i nie zawsze jest to szczyt elegancji oraz wygody. Postanowiłem sprawdzić, jak wydajny jest ten proces. Stworzyłem dwa skrypty testowe, które załadowany plik testowy miały przekonwertować na dokładnie taką samą tablicę, jaką otrzymalibyśmy, ładując dane z YAML-a. Na początek konwerter SimpleXML:
<?php $xml = simplexml_load_file('./file.xml'); $info = array(); foreach($xml -> group as $grp) { $info[(string)$grp['name']] = array(); foreach($grp -> option as $opt) { $info[(string)$grp['name']][(string)$opt['name']] = (string)$opt; } } ?>
Potem DOM:
<?php $dom = new DOMDocument(); $dom -> load('file.xml'); $info = array(); $groups = $dom -> getElementsByTagName('group'); for($i = 0; $i < $groups -> length; $i++) { $item = $groups -> item($i); $info[$item -> getAttribute('name')] = array(); $options = $item -> getElementsByTagName('option'); for($j = 0; $j < $options -> length; $j++) { $subitem = $options -> item($j); $info[$item -> getAttribute('name')][$subitem -> getAttribute('name')] = (string)$subitem -> nodeValue; } } ?>
Wynik: SimpleXML - 2100 req/s. DOM - 1800 req/s. Nawet po uwzględnieniu samego czasu załadowania pliku do pamięci rezultaty działają na korzyść pierwszego z rozszerzeń. Można zatem pokusić się o wniosek, że rezygnując z części funkcjonalności i upraszczając API, zyskujemy w zamian większą wydajność.
Okazało się, że YAML-a skreśliłem nieco zbyt szybko. Dobry system cache potrafi bowiem zdziałać cuda. Z drugiej strony Syck mnie lekko zawiódł - sądziłem, że prostota tego języka umożliwi napisanie mniejszego i zarazem wydajniejszego parsera, ale jak widać, twórców projektu czeka jeszcze sporo optymalizacji, zanim uda im się tego dokonać. Różnice czasowe wprawdzie nie są wielkie, ale miałem nadzieję na przynajmniej 3000 req/s.















Napisał Ludvik w środę, 5 września 2007 o 11:27
Bardzo dobre porównanie, zdziwiłem się tylko słabym wynikiem yaml+cache w pierwszej części... Jak widzę, wszystko już sprostowane ;) Sam stawiałem właśnie na cache'owany yaml, który jest moim zdaniem w tym przypadku przyjemniejszy od xmla...
Miałem w przyszłości przemyśleć ten temat, więc mam jeden problem z głowy ;)